diff --git a/go.work b/go.work new file mode 100644 index 0000000..c4eec06 --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.21.6 + +use . diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..8ca8349 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,6 @@ +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d77d85a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1350 @@ +{ + "name": "libtailscale", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "express": "^4.18.2", + "ffi-napi": "^4.0.3", + "ref-napi": "^3.0.3" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ffi-napi": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz", + "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", + "hasInstallScript": true, + "dependencies": { + "debug": "^4.1.1", + "get-uv-event-loop-napi-h": "^1.0.5", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1", + "ref-napi": "^2.0.1 || ^3.0.2", + "ref-struct-di": "^1.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ffi-napi/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ffi-napi/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-from-current-process-h": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz", + "integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==" + }, + "node_modules/get-uv-event-loop-napi-h": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz", + "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", + "dependencies": { + "get-symbol-from-current-process-h": "^1.0.1" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node_modules/node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ref-napi": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz", + "integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==", + "hasInstallScript": true, + "dependencies": { + "debug": "^4.1.1", + "get-symbol-from-current-process-h": "^1.0.2", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/ref-napi/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ref-napi/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ref-struct-di": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz", + "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", + "dependencies": { + "debug": "^3.1.0" + } + }, + "node_modules/ref-struct-di/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/ref-struct-di/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dependencies": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + } + }, + "dependencies": { + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "requires": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + } + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "ffi-napi": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz", + "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", + "requires": { + "debug": "^4.1.1", + "get-uv-event-loop-napi-h": "^1.0.5", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1", + "ref-napi": "^2.0.1 || ^3.0.2", + "ref-struct-di": "^1.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "requires": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "get-symbol-from-current-process-h": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz", + "integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==" + }, + "get-uv-event-loop-napi-h": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz", + "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", + "requires": { + "get-symbol-from-current-process-h": "^1.0.1" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "requires": { + "get-intrinsic": "^1.2.2" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==" + }, + "object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "ref-napi": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz", + "integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==", + "requires": { + "debug": "^4.1.1", + "get-symbol-from-current-process-h": "^1.0.2", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "ref-struct-di": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz", + "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "requires": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4a4c79c --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "express": "^4.18.2", + "ffi-napi": "^4.0.3", + "ref-napi": "^3.0.3" + } +} \ No newline at end of file diff --git a/platform/tailscale_unix.go b/platform/tailscale_unix.go new file mode 100644 index 0000000..e1448e6 --- /dev/null +++ b/platform/tailscale_unix.go @@ -0,0 +1,33 @@ +//go:build darwin || linux +// +build darwin linux + +package platform + +//#include "errno.h" +//#include "../socketpair_handler.h" +import "C" + +import ( + "syscall" +) + +func GetSocketPair() ([2]int, error) { + return syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) +} + +func CloseSocket(fd interface{}) (err error) { + return syscall.Close(fd.(int)) +} + +func ReadSocket(fd interface{}, buf *[256]byte) { + syscall.Read(fd.(int), (*buf)[:]) +} + +func SendMessage(fd interface{}, p []byte, connFd int, to syscall.Sockaddr, flags int) (err error) { + rights := syscall.UnixRights(int(connFd)) + return syscall.Sendmsg(fd.(int), p, rights, to, flags) +} + +func Shutdown(fd interface{}, how int) (err error) { + return syscall.Shutdown(fd.(int), how) +} diff --git a/platform/tailscale_windows.go b/platform/tailscale_windows.go new file mode 100644 index 0000000..4999052 --- /dev/null +++ b/platform/tailscale_windows.go @@ -0,0 +1,72 @@ +// If your file name follows the structure name_{GOOS}_{GOARCH}.go or +// simply name_{GOOS}.go, +// then it will be compiled only under the target OS+architecture +// or OS+any architecture respectively without needing a special comment +package platform + +//#cgo CFLAGS: -g -Wall +//#cgo LDFLAGS: -lws2_32 +//#include "errno.h" +//#include "../socketpair_handler.h" +import "C" + +import ( + "fmt" + "syscall" + "unsafe" +) + +func GetSocketPair() ([]syscall.Handle, *C.SOCKET, error) { + fds := make([]syscall.Handle, 2) + fds_pt := C.get_socket_pair() + fds_array := (*[2]C.SOCKET)(unsafe.Pointer(fds_pt))[:] + fds[0] = syscall.Handle(uintptr(fds_array[0])) + fds[1] = syscall.Handle(uintptr(fds_array[1])) + return fds, fds_pt, nil +} + +func CloseSocket(fd syscall.Handle) error { + fmt.Println("Closing socket", fd) + err := syscall.Close(fd) + if err != nil { + errCode := syscall.GetLastError() + // Handle the error or print it for debugging + fmt.Printf("Error closing handle: %v\n", err) + fmt.Printf("Errorcode: %v\n", errCode) + return err + } + fmt.Println("Closed socket", fd) + return nil +} + +func ReadSocket(fd syscall.Handle, buf *[256]byte) { + fmt.Println("Reading socket", fd) + syscall.Read(fd, (*buf)[:]) +} + +func SendMessage(fd syscall.Handle, p []byte, connFd int, to syscall.Sockaddr, flags int) error { + fmt.Println("Writing socket", fd) + var written uint32 + err := syscall.WSAStartup(uint32(0x202), &syscall.WSAData{}) + if err != nil { + return err + } + defer syscall.WSACleanup() + + iov := syscall.WSABuf{ + Len: uint32(len(p)), + Buf: &p[0], + } + var flagsUint32 uint32 = uint32(flags) + err = syscall.WSASend(fd, &iov, 1, &written, flagsUint32, nil, nil) + if err != nil { + return err + } + + return nil + +} + +func Shutdown(fd syscall.Handle, how int) error { + return syscall.Shutdown(fd, how) +} diff --git a/socketpair.c b/socketpair.c new file mode 100644 index 0000000..b8b89cc --- /dev/null +++ b/socketpair.c @@ -0,0 +1,162 @@ +/* socketpair.c +Copyright 2007, 2010 by Nathan C. Myers +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + The name of the author must not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Changes: + * 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements + * git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54 + * github.com/GerHobbelt/selectable-socketpair + * always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64 + * and UNIX/other platforms + * 2013-07-18: Change to BSD 3-clause license + * 2010-03-31: + * set addr to 127.0.0.1 because win32 getsockname does not always set it. + * 2010-02-25: + * set SO_REUSEADDR option to avoid leaking some windows resource. + * Windows System Error 10049, "Event ID 4226 TCP/IP has reached + * the security limit imposed on the number of concurrent TCP connect + * attempts." Bleah. + * 2007-04-25: + * preserve value of WSAGetLastError() on all error returns. + * 2007-04-22: (Thanks to Matthew Gregan ) + * s/EINVAL/WSAEINVAL/ fix trivial compile failure + * s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout + * of a child process. + * add argument make_overlapped + */ + +#include +#include + +#ifdef _WIN32 +# include /* socklen_t, et al (MSVC20xx) */ +# include +# include +#else +# include +# include +# include +#endif + +#ifdef _WIN32 + +/* dumb_socketpair: + * If make_overlapped is nonzero, both sockets created will be usable for + * "overlapped" operations via WSASend etc. If make_overlapped is zero, + * socks[0] (only) will be usable with regular ReadFile etc., and thus + * suitable for use as stdin or stdout of a child process. Note that the + * sockets must be closed with closesocket() regardless. + */ + +static int dumb_socketpair(SOCKET socks[2], int make_overlapped) +{ + union { + struct sockaddr_in inaddr; + struct sockaddr addr; + } a; + int iResult; + WSADATA wsaData; + iResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if (iResult != 0) { + printf("failed WSA startup"); + return SOCKET_ERROR; + } + SOCKET listener; + u_long mode = 1; // 1 to enable non-blocking socket + ioctlsocket(listener, FIONBIO, &mode); + int e; + socklen_t addrlen = sizeof(a.inaddr); + DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0); + int reuse = 1; + + if (socks == 0) { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + socks[0] = socks[1] = -1; + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == -1) { + printf("%d", WSAGetLastError()); + return SOCKET_ERROR; + } + memset(&a, 0, sizeof(a)); + a.inaddr.sin_family = AF_INET; + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_port = 0; + + for (;;) { + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, + (char*) &reuse, (socklen_t) sizeof(reuse)) == -1) + break; + if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + memset(&a, 0, sizeof(a)); + if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR) + break; + // win32 getsockname may only set the port number, p=0.0005. + // ( http://msdn.microsoft.com/library/ms738543.aspx ): + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_family = AF_INET; + + if (listen(listener, 1) == SOCKET_ERROR) + break; + + socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags); + if (socks[0] == -1) + break; + if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + socks[1] = accept(listener, NULL, NULL); + if (socks[1] == -1) + break; + WSACleanup(); + closesocket(listener); + return 0; + } + e = WSAGetLastError(); + closesocket(listener); + closesocket(socks[0]); + closesocket(socks[1]); + WSASetLastError(e); + socks[0] = socks[1] = -1; + WSACleanup(); + return SOCKET_ERROR; +} +#else +static int dumb_socketpair(int socks[2], int dummy) +{ + if (socks == 0) { + errno = EINVAL; + return -1; + } + dummy = socketpair(AF_LOCAL, SOCK_STREAM, 0, socks); + if (dummy) + socks[0] = socks[1] = -1; + return dummy; +} +#endif \ No newline at end of file diff --git a/socketpair_handler.c b/socketpair_handler.c new file mode 100644 index 0000000..f361f95 --- /dev/null +++ b/socketpair_handler.c @@ -0,0 +1,32 @@ +#ifdef _WIN32 +#include "socketpair_handler.h" +#include "socketpair.c" +#include +#include +#include +#include +#pragma comment(lib, "ws2_32.lib") +#endif + +SOCKET *get_socket_pair() { + #ifdef _WIN32 + SOCKET* spair = (SOCKET*)malloc(2 * sizeof(SOCKET)); + if (spair == NULL) { + fprintf(stderr, "Failed to allocate memory for socket pair\n"); + return NULL; + } + + spair[0] = 0; + spair[1] = 0; + if(dumb_socketpair(spair, 1) == SOCKET_ERROR) { + fprintf(stderr, "Init failed, creating socketpair: %s\n", strerror(errno)); + free(spair); + return NULL; + } + return spair; + #else + return NULL; + #endif + + +} \ No newline at end of file diff --git a/socketpair_handler.h b/socketpair_handler.h new file mode 100644 index 0000000..a36146e --- /dev/null +++ b/socketpair_handler.h @@ -0,0 +1,8 @@ +#ifndef SOCKET_H +#define SOCKET_H + +#include + +SOCKET *get_socket_pair(); + +#endif \ No newline at end of file diff --git a/tailscale.c b/tailscale.c index 7a39f50..1295a4c 100644 --- a/tailscale.c +++ b/tailscale.c @@ -2,55 +2,73 @@ // SPDX-License-Identifier: BSD-3-Clause #include "tailscale.h" +#if defined(__APPLE__) || defined(__linux__) #include -#include +#elif _WIN32 +#include +#include +#include +#else #include +#endif + +#include + // Functions exported by Go. extern int TsnetNewServer(); extern int TsnetStart(int sd); extern int TsnetUp(int sd); extern int TsnetClose(int sd); -extern int TsnetErrmsg(int sd, char* buf, size_t buflen); -extern int TsnetDial(int sd, char* net, char* addr, int* connOut); -extern int TsnetSetDir(int sd, char* str); -extern int TsnetSetHostname(int sd, char* str); -extern int TsnetSetAuthKey(int sd, char* str); -extern int TsnetSetControlURL(int sd, char* str); +extern int TsnetErrmsg(int sd, char *buf, size_t buflen); +extern int TsnetDial(int sd, char *net, char *addr, int *connOut); +extern int TsnetSetDir(int sd, char *str); +extern int TsnetSetHostname(int sd, char *str); +extern int TsnetSetAuthKey(int sd, char *str); +extern int TsnetSetControlURL(int sd, char *str); extern int TsnetSetEphemeral(int sd, int ephemeral); extern int TsnetSetLogFD(int sd, int fd); -extern int TsnetListen(int sd, char* net, char* addr, int* listenerOut); -extern int TsnetLoopback(int sd, char* addrOut, size_t addrLen, char* proxyOut, char* localOut); +extern int TsnetListen(int sd, char *net, char *addr, int *listenerOut); +extern int TsnetLoopback(int sd, char *addrOut, size_t addrLen, char *proxyOut, char *localOut); -tailscale tailscale_new() { +tailscale tailscale_new() +{ return TsnetNewServer(); } -int tailscale_start(tailscale sd) { +int tailscale_start(tailscale sd) +{ return TsnetStart(sd); } -int tailscale_up(tailscale sd) { +int tailscale_up(tailscale sd) +{ return TsnetUp(sd); } -int tailscale_close(tailscale sd) { +int tailscale_close(tailscale sd) +{ return TsnetClose(sd); } -int tailscale_dial(tailscale sd, const char* network, const char* addr, tailscale_conn* conn_out) { - return TsnetDial(sd, (char*)network, (char*)addr, (int*)conn_out); +int tailscale_dial(tailscale sd, const char *network, const char *addr, tailscale_conn *conn_out) +{ + return TsnetDial(sd, (char *)network, (char *)addr, (int *)conn_out); } -int tailscale_listen(tailscale sd, const char* network, const char* addr, tailscale_listener* listener_out) { - return TsnetListen(sd, (char*)network, (char*)addr, (int*)listener_out); +int tailscale_listen(tailscale sd, const char *network, const char *addr, tailscale_listener *listener_out) +{ + return TsnetListen(sd, (char *)network, (char *)addr, (int *)listener_out); } -int tailscale_accept(tailscale_listener ld, tailscale_conn* conn_out) { +int tailscale_accept(tailscale_listener ld, tailscale_conn *conn_out) +{ + +#if defined(__APPLE__) || defined(__linux__) struct msghdr msg = {0}; char mbuf[256]; - struct iovec io = { .iov_base = mbuf, .iov_len = sizeof(mbuf) }; + struct iovec io = {.iov_base = mbuf, .iov_len = sizeof(mbuf)}; msg.msg_iov = &io; msg.msg_iovlen = 1; @@ -58,41 +76,148 @@ int tailscale_accept(tailscale_listener ld, tailscale_conn* conn_out) { msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); - if (recvmsg(ld, &msg, 0) == -1) { + if (recvmsg(ld, &msg, 0) == -1) + { return -1; } - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - unsigned char* data = CMSG_DATA(cmsg); + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + unsigned char *data = CMSG_DATA(cmsg); - int fd = *(int*)data; + int fd = *(int *)data; *conn_out = fd; return 0; +#elif _WIN32 + + // SOCKET ListenSocket = ld; + // fd_set readfds; + // struct timeval tv; + // int result; + + // // Initialize the set + // FD_ZERO(&readfds); + // FD_SET(ListenSocket, &readfds); + + // // Set timeout to zero, for non-blocking operation + // tv.tv_sec = 1; + // tv.tv_usec = 0; + + // result = select(ListenSocket + 1, &readfds, NULL, NULL, &tv); + + // if (result == -1) { + // printf("select failed with error: %u\n", WSAGetLastError()); + // } else if (result == 0) { + // printf("No incoming connections\n"); + // } else { + // printf("Socket is ready to accept a connection\n"); + // } + // char mbuf[256]; + // WSABUF wsaBuf; + // DWORD bytesReceived; + // DWORD flags = 0; + // SOCKET fd; + + // wsaBuf.buf = mbuf; + // wsaBuf.len = sizeof(mbuf); + + // if (WSARecv(ld, &wsaBuf, 1, &bytesReceived, &flags, NULL, NULL) == SOCKET_ERROR) + // { + // // Print the error code + // int error = WSAGetLastError(); + // fprintf(stderr, "WSARecv failed with error: %d\n", error); + // return -1; + // } + + // // Extract the socket descriptor from the received control information + // if (WSAGetOverlappedResult(ld, NULL, &bytesReceived, FALSE, &flags) == SOCKET_ERROR) + // { + // int error = WSAGetLastError(); + // fprintf(stderr, "WSAGetOverlappedResult failed with error: %d\n", error); + // return -1; + // } + // second attemp + // WSADATA wsaData; + // int error = WSAStartup(MAKEWORD(2,2), &wsaData); + // if (error) { + // printf("WSAStartup() failed with error: %d\n", error); + // return 1; + // } + // fd = WSAAccept(ListenSocket + 1, NULL, NULL, NULL, 0); + // if (fd == INVALID_SOCKET) + // { + // int error = WSAGetLastError(); + // fprintf(stderr, "WSAAccept failed with error: %d\n", error); + // //return -1; + // } + + // *conn_out = fd; + // return 0; + // third attempt + // char mbuf[256]; + // WSABUF wsaBuf; + // DWORD bytesReceived; + // DWORD flags = 0; + + // wsaBuf.buf = mbuf; + // wsaBuf.len = sizeof(mbuf); + + // if (WSARecv(ld, &wsaBuf, 1, &bytesReceived, &flags, NULL, NULL) == SOCKET_ERROR) + // { + // // Print the error code + // int error = WSAGetLastError(); + // fprintf(stderr, "WSARecv failed with error: %d\n", error); + // return -1; + // } + + // // If WSARecv succeeded, return the socket + // *conn_out = ld; + // return 0; + struct sockaddr clientAddr; + int clientAddrSize = sizeof(clientAddr); + + // Accept incoming connection + *conn_out = accept(ld, &clientAddr, &clientAddrSize); + if (*conn_out == INVALID_SOCKET) { + printf("Accept failed with error code: %d\n", WSAGetLastError()); + return -1; + } + + return 0; + +#endif } -int tailscale_set_dir(tailscale sd, const char* dir) { - return TsnetSetDir(sd, (char*)dir); +int tailscale_set_dir(tailscale sd, const char *dir) +{ + return TsnetSetDir(sd, (char *)dir); } -int tailscale_set_hostname(tailscale sd, const char* hostname) { - return TsnetSetHostname(sd, (char*)hostname); +int tailscale_set_hostname(tailscale sd, const char *hostname) +{ + return TsnetSetHostname(sd, (char *)hostname); } -int tailscale_set_authkey(tailscale sd, const char* authkey) { - return TsnetSetAuthKey(sd, (char*)authkey); +int tailscale_set_authkey(tailscale sd, const char *authkey) +{ + return TsnetSetAuthKey(sd, (char *)authkey); } -int tailscale_set_control_url(tailscale sd, const char* control_url) { - return TsnetSetControlURL(sd, (char*)control_url); +int tailscale_set_control_url(tailscale sd, const char *control_url) +{ + return TsnetSetControlURL(sd, (char *)control_url); } -int tailscale_set_ephemeral(tailscale sd, int ephemeral) { +int tailscale_set_ephemeral(tailscale sd, int ephemeral) +{ return TsnetSetEphemeral(sd, ephemeral); } -int tailscale_set_logfd(tailscale sd, int fd) { +int tailscale_set_logfd(tailscale sd, int fd) +{ return TsnetSetLogFD(sd, fd); } -int tailscale_loopback(tailscale sd, char* addr_out, size_t addrlen, char* proxy_cred_out, char* local_api_cred_out) { +int tailscale_loopback(tailscale sd, char *addr_out, size_t addrlen, char *proxy_cred_out, char *local_api_cred_out) +{ return TsnetLoopback(sd, addr_out, addrlen, proxy_cred_out, local_api_cred_out); } -int tailscale_errmsg(tailscale sd, char* buf, size_t buflen) { +int tailscale_errmsg(tailscale sd, char *buf, size_t buflen) +{ return TsnetErrmsg(sd, buf, buflen); -} +} \ No newline at end of file diff --git a/tailscale.go b/tailscale.go index 7087ed0..62131c4 100644 --- a/tailscale.go +++ b/tailscale.go @@ -5,6 +5,8 @@ package main //#include "errno.h" +//#include "socketpair_handler.h" +//#include "tailscale.h" import "C" import ( @@ -17,6 +19,8 @@ import ( "syscall" "unsafe" + "github.com/tailscale/libtailscale/platform" + "tailscale.com/hostinfo" "tailscale.com/tsnet" "tailscale.com/types/logger" @@ -56,7 +60,7 @@ var listeners struct { type listener struct { s *server ln net.Listener - fd int // go side fd of socketpair sent to C + fd C.SOCKET // go side fd of socketpair sent to C } // conns tracks all the pipe(2)s allocated via tsnet_dial. @@ -80,6 +84,11 @@ func (s *server) recErr(err error) C.int { return -1 } +//export TsnetAccept +func TsnetAccept(sd C.int, connOut *C.int) C.int { + return C.tailscale_accept(sd, connOut) +} + //export TsnetNewServer func TsnetNewServer() C.int { servers.mu.Lock() @@ -120,6 +129,7 @@ func TsnetUp(sd C.int) C.int { //export TsnetClose func TsnetClose(sd C.int) C.int { + fmt.Println("CLOSING TSNET") servers.mu.Lock() s := servers.m[sd] if s != nil { @@ -169,25 +179,30 @@ func TsnetErrmsg(sd C.int, buf *C.char, buflen C.size_t) C.int { //export TsnetListen func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int { + fmt.Println("start listening, ", network, addr) s, err := getServer(sd) if err != nil { + fmt.Println("error No server") return s.recErr(err) } ln, err := s.s.Listen(C.GoString(network), C.GoString(addr)) if err != nil { + fmt.Println("error somewhere in the listening - ", err) return s.recErr(err) } - // The tailscale_listener we return to C is one side of a socketpair(2). // We do this so we can proactively call ln.Accept in a goroutine and // feed an fd for the connection through the listener. This lets C use // epoll on the tailscale_listener to know if it should call // tailscale_accept, which avoids a blocking call on the far side. - fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + fds, fdPt, err := platform.GetSocketPair() + if err != nil { + fmt.Println("error in getting socketpair") return s.recErr(err) } + sp := fds[1] fdC := C.int(fds[0]) @@ -195,9 +210,10 @@ func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int { if listeners.m == nil { listeners.m = map[C.int]*listener{} } - listeners.m[fdC] = &listener{s: s, ln: ln, fd: sp} + listeners.m[fdC] = &listener{s: s, ln: ln, fd: C.SOCKET(sp)} listeners.mu.Unlock() + fmt.Println("listener setup") cleanup := func() { // If fdC is closed on the C side, then we end up calling // into cleanup twice. Be careful to avoid syscall.Close @@ -205,11 +221,15 @@ func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int { listeners.mu.Lock() if tsLn, ok := listeners.m[fdC]; ok && tsLn.ln == ln { delete(listeners.m, fdC) - syscall.Close(sp) + platform.CloseSocket(sp) } listeners.mu.Unlock() + C.free(unsafe.Pointer(fdPt)) + fmt.Println("ln closing...") ln.Close() + fmt.Println("ln closed") + } go func() { // fdC is never written to, so trying to read from sp blocks @@ -218,14 +238,19 @@ func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int { // // TODO: would using os.NewFile avoid a locked up thread? var buf [256]byte - syscall.Read(sp, buf[:]) - cleanup() + platform.ReadSocket(sp, &buf) + //cleanup() }() go func() { defer cleanup() for { + fmt.Println("cleanup starting") + fmt.Println("listener: ", ln) netConn, err := ln.Accept() + fmt.Println("accept", netConn, err) if err != nil { + fmt.Println("error in accept") + s.s.Logf("libtailscale.accept: newConn: %v", err) return } var connFd C.int @@ -233,32 +258,41 @@ func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int { if s.s.Logf != nil { s.s.Logf("libtailscale.accept: newConn: %v", err) } + fmt.Println(("net closed 1")) netConn.Close() continue } - rights := syscall.UnixRights(int(connFd)) - err = syscall.Sendmsg(sp, nil, rights, nil, 0) + fmt.Println("send message") + err = platform.SendMessage(sp, []byte("hello"), int(connFd), nil, 0) + if err != nil { // We handle sp being closed in the read goroutine above. if s.s.Logf != nil { s.s.Logf("libtailscale.accept: sendmsg failed: %v", err) } + fmt.Println("net closed 12", err) netConn.Close() // fallthrough to close connFd, then continue Accept()ing } - syscall.Close(int(connFd)) // now owned by recvmsg + platform.CloseSocket(sp) // now owned by recvmsg + netConn.Close() + fmt.Println(("net closed 3")) } }() *listenerOut = fdC + fmt.Println("returning 0 ") return 0 } func newConn(s *server, netConn net.Conn, connOut *C.int) error { - fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + fmt.Println("newconn starting") + fds, fdPt, err := platform.GetSocketPair() if err != nil { + fmt.Println("Error getting new socketpair ") return err } + r := os.NewFile(uintptr(fds[1]), "socketpair-r") c := &conn{s: s.s, c: netConn, r: r} fdC := C.int(fds[0]) @@ -271,26 +305,33 @@ func newConn(s *server, netConn net.Conn, connOut *C.int) error { conns.mu.Unlock() connCleanup := func() { + fmt.Println("Connection cleanup") var inCleanup bool conns.mu.Lock() + fmt.Println("Locked") if tsConn, ok := conns.m[fdC]; ok && tsConn.c == netConn { + fmt.Println("Connection is alive") delete(conns.m, fdC) inCleanup = true } conns.mu.Unlock() - + fmt.Println("Unlocked + cleanup: ", inCleanup, conns) if !inCleanup { return } + C.free(unsafe.Pointer(fdPt)) r.Close() + fmt.Println("netconn closing ", conns) + fmt.Println("servers: ", servers) + fmt.Println("listeners: ", listeners) netConn.Close() } go func() { defer connCleanup() var b [1 << 16]byte io.CopyBuffer(r, netConn, b[:]) - syscall.Shutdown(int(r.Fd()), syscall.SHUT_WR) + platform.Shutdown(syscall.Handle(r.Fd()), syscall.SHUT_RD) if cr, ok := netConn.(interface{ CloseRead() error }); ok { cr.CloseRead() } @@ -299,12 +340,11 @@ func newConn(s *server, netConn net.Conn, connOut *C.int) error { defer connCleanup() var b [1 << 16]byte io.CopyBuffer(netConn, r, b[:]) - syscall.Shutdown(int(r.Fd()), syscall.SHUT_RD) + platform.Shutdown(syscall.Handle(r.Fd()), syscall.SHUT_WR) if cw, ok := netConn.(interface{ CloseWrite() error }); ok { cw.CloseWrite() } }() - *connOut = fdC return nil } diff --git a/tailscale_test.go b/tailscale_test.go index efa35d7..2cfcf72 100644 --- a/tailscale_test.go +++ b/tailscale_test.go @@ -26,6 +26,7 @@ func TestConn(t *testing.T) { for i := 0; i < 50; i++ { conns.mu.Lock() remConns = len(conns.m) + //fmt.Printf("New connection established: RemoteAddr=%s, LocalAddr=%s\n", newConn.RemoteAddr(), netConn.LocalAddr()) conns.mu.Unlock() listeners.mu.Lock() diff --git a/test_express.js b/test_express.js new file mode 100644 index 0000000..c26aaa2 --- /dev/null +++ b/test_express.js @@ -0,0 +1,88 @@ +var ref = require('ref-napi'); +var ffi = require("ffi-napi"); +const process = require('node:process'); +const net = require('node:net'); +const http = require('node:http'); + +var kernel32 = ffi.Library("kernel32", { + 'SetDllDirectoryA': ["bool", ["string"]] +}) +kernel32.SetDllDirectoryA("pathToAdd"); + +var tailscale_listener_ptr = ref.refType(ref.types.int); +var tailscale_conn_ptr = ref.refType(ref.types.int); +var char_ptr = ref.refType(ref.types.char) + +var path = require("path") + +var Tailscale = ffi.Library(path.join(__dirname, 'libtailscale.dll'), { + "TsnetNewServer": ['int', []], + "TsnetUp": ['int', ['int']], + "TsnetClose": ['int', ['int']], + "TsnetSetControlURL": ['int', ['int', 'string']], + "TsnetSetEphemeral": ['int', ['int', 'int']], + "TsnetSetAuthKey": ['int', ['int', 'string']], + "TsnetListen": ['int', ['int', 'string', 'string', tailscale_listener_ptr]], + "TsnetAccept": ['int', ['int', tailscale_conn_ptr]], + "TsnetErrmsg": ['int', ['int', char_ptr, 'int']], +}); + +const tailscale = Tailscale.TsnetNewServer(); +Tailscale.TsnetSetAuthKey(tailscale, "") +Tailscale.TsnetSetControlURL(tailscale, "https://headscale.dev.ffd.scapps.io"); +// Tailscale.TsnetSetEphemeral(tailscale, 1);​ +// TODO ezt meg kéne csinálni, hogy SIGTERMre gracefully leálljon +process.on('exit', (code) => { + console.log(`About to exit with code: ${code}`); + Tailscale.TsnetClose(tailscale) +}); + +Tailscale.TsnetUp(tailscale); + +let lnBuf = ref.alloc('int'); + +Tailscale.TsnetListen(tailscale, "tcp", ":1999", lnBuf); + +let ln = lnBuf.deref(); + +function err() { + var buffer = Buffer.alloc(2000); + Tailscale.TsnetErrmsg(tailscale, buffer, 2000); + var errorMessage = ref.readCString(buffer, 0); + console.log('====================='); + console.log(errorMessage); + console.log('====================='); +} +console.log("Listening on :1999"); + +const server = http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello World\n'); +}) + +function accept() { + let connRef = ref.alloc('int'); + Tailscale.TsnetAccept.async(ln, connRef, function (error) { + console.log("JS - accepting") + console.log("JS - ERROR: " + error) + let conn = ref.deref(connRef); + console.log("JS - conn: " + conn) + //let socket = net.Socket({ fd: conn, readable: true, writable: true, allowHalfOpen: false }); + console.log("JS - socket: " + socket) + server.emit('connection', socket) + console.log("JS - emitted") + if (error) { + err() + } else { + console.log('JS - Connection established'); + } + + accept(); + }); +} + +accept(); + +(function wait() { + setTimeout(wait, 1000); +})(); \ No newline at end of file diff --git a/tsnetctest/tsnetctest.c b/tsnetctest/tsnetctest.c new file mode 100644 index 0000000..1be045b --- /dev/null +++ b/tsnetctest/tsnetctest.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include "tsnetctest.h" + +// Define Windows equivalent types and functions +typedef SOCKET tailscale_conn_w; +typedef SOCKET tailscale_listener_w; + +// Define Windows equivalent error reporting +#define snprintf _snprintf + +char* tmps1; +char* tmps2; + +char* control_url = 0; + +int addrlen = 128; +char* addr; +char* proxy_cred; +char* local_api_cred ; + +int errlen = 512; +char* err ; + +tailscale s1, s2; + +int set_err(tailscale sd, char tag) { + // Implement your error reporting logic here + return 1; +} + +void print_error(const char* prefix) { + LPSTR messageBuffer; + DWORD errorCode = WSAGetLastError(); + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + + printf("%s: %s\n", prefix, messageBuffer); + + LocalFree(messageBuffer); +} + +int test_conn() { + err = calloc(errlen, 1); + addr = calloc(addrlen, 1); + proxy_cred = calloc(33, 1); + local_api_cred = calloc(33, 1); + int ret; + + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + printf("WSAStartup failed\n"); + return 1; + } + + s1 = tailscale_new(); + // Set control_url, tmps1, and other configurations for s1 + + s2 = tailscale_new(); + // Set control_url, tmps2, and other configurations for s2 + + int ln; + // Setup listener ln + + int w; + // Dial connection using s2 + + int r; + // Accept connection using ln + + if ((ret = tailscale_set_control_url(s1, control_url)) != 0) { + return set_err(s1, '0'); + } + if ((ret = tailscale_set_dir(s1, tmps1)) != 0) { + return set_err(s1, '1'); + } + if ((ret = tailscale_set_logfd(s1, -1)) != 0) { + return set_err(s1, '2'); + } + if ((ret = tailscale_up(s1)) != 0) { + return set_err(s1, '3'); + } + + if ((ret = tailscale_set_control_url(s2, control_url)) != 0) { + return set_err(s2, '4'); + } + if ((ret = tailscale_set_dir(s2, tmps2)) != 0) { + return set_err(s2, '5'); + } + if ((ret = tailscale_set_logfd(s2, -1)) != 0) { + return set_err(s1, '6'); + } + if ((ret = tailscale_up(s2)) != 0) { + return set_err(s2, '7'); + } + + if ((ret = tailscale_listen(s1, "tcp", ":8081", &ln)) != 0) { + return set_err(s1, '8'); + } + + if ((ret = tailscale_dial(s2, "tcp", "100.64.0.1:8081", &w)) != 0) { + return set_err(s2, '9'); + } + + if ((ret = tailscale_accept(ln, &r)) != 0) { + return set_err(s2, 'a'); + } + + const char want[] = "hello"; + SSIZE_T wret; + if ((wret = send(w, want, sizeof(want), 0)) != sizeof(want)) { + print_error("SEND error"); + } + char got[sizeof(want)]; + SSIZE_T rret; + int error = 0; + socklen_t len = sizeof (error); + int retval = getsockopt (r, SOL_SOCKET, SO_ERROR, &error, &len); + + if (retval != 0) { + // There was a problem getting the error code + fprintf(stderr, "error getting socket error code: %s\n", strerror(retval)); + } + + if (error != 0) { + // socket has a non zero error status + fprintf(stderr, "socket error: %s\n", strerror(error)); + } + if ((rret = recv(r, got, sizeof(got), 0)) != sizeof(want)) { + print_error("RECEIVE error"); + } + if ((ret = tailscale_loopback(s1, addr, addrlen, proxy_cred, local_api_cred)) != 0) { + // Handle error + } + // Compare got with want + + closesocket(w); + closesocket(r); + closesocket(ln); + + WSACleanup(); + + // Cleanup resources + + return 0; +} + +int close_conn() { + if (tailscale_close(s1) != 0) { + return set_err(s1, 'd'); + } + if (tailscale_close(s2) != 0) { + return set_err(s2, 'e'); + } + return 0; +} diff --git a/tsnetctest/tsnetctest.go b/tsnetctest/tsnetctest.go index a2d17df..8db0f9b 100644 --- a/tsnetctest/tsnetctest.go +++ b/tsnetctest/tsnetctest.go @@ -4,140 +4,12 @@ // use the 'import "C"' directive in tests. package tsnetctest -/* -#include -#include -#include -#include -#include -#include "../tailscale.h" - -char* tmps1; -char* tmps2; - -char* control_url = 0; - -int addrlen = 128; -char* addr = NULL; -char* proxy_cred = NULL; -char* local_api_cred = NULL; - -int errlen = 512; -char* err = NULL; - -tailscale s1, s2; - -int set_err(tailscale sd, char tag) { - err[0] = tag; - err[1] = ':'; - err[2] = ' '; - tailscale_errmsg(sd, &err[3], errlen-3); - return 1; -} - -int test_conn() { - err = calloc(errlen, 1); - addr = calloc(addrlen, 1); - proxy_cred = calloc(33, 1); - local_api_cred = calloc(33, 1); - int ret; - - s1 = tailscale_new(); - if ((ret = tailscale_set_control_url(s1, control_url)) != 0) { - return set_err(s1, '0'); - } - if ((ret = tailscale_set_dir(s1, tmps1)) != 0) { - return set_err(s1, '1'); - } - if ((ret = tailscale_set_logfd(s1, -1)) != 0) { - return set_err(s1, '2'); - } - if ((ret = tailscale_up(s1)) != 0) { - return set_err(s1, '3'); - } - - s2 = tailscale_new(); - if ((ret = tailscale_set_control_url(s2, control_url)) != 0) { - return set_err(s2, '4'); - } - if ((ret = tailscale_set_dir(s2, tmps2)) != 0) { - return set_err(s2, '5'); - } - if ((ret = tailscale_set_logfd(s2, -1)) != 0) { - return set_err(s1, '6'); - } - if ((ret = tailscale_up(s2)) != 0) { - return set_err(s2, '7'); - } - - tailscale_listener ln; - if ((ret = tailscale_listen(s1, "tcp", ":8081", &ln)) != 0) { - return set_err(s1, '8'); - } - - tailscale_conn w; - if ((ret = tailscale_dial(s2, "tcp", "100.64.0.1:8081", &w)) != 0) { - return set_err(s2, '9'); - } - - tailscale_conn r; - if ((ret = tailscale_accept(ln, &r)) != 0) { - return set_err(s2, 'a'); - } - - const char want[] = "hello"; - ssize_t wret; - if ((wret = write(w, want, sizeof(want))) != sizeof(want)) { - snprintf(err, errlen, "short write: %zd, errno: %d (%s)", wret, errno, strerror(errno)); - return 1; - } - char* got = malloc(sizeof(want)); - if ((wret = read(r, got, sizeof(want))) != sizeof("hello")) { - snprintf(err, errlen, "short read: %zd on fd %d, errno: %d (%s)", wret, r, errno, strerror(errno)); - return 1; - } - if (strncmp(got, want, sizeof(want)) != 0) { - snprintf(err, errlen, "got '%s' want '%s'", got, want); - return 1; - } - - if ((ret = close(w)) != 0) { - snprintf(err, errlen, "failed to close w: %d (%s)", errno, strerror(errno)); - return 1; - } - if ((ret = close(r)) != 0) { - snprintf(err, errlen, "failed to close r: %d (%s)", errno, strerror(errno)); - return 1; - } - if ((ret = close(ln)) != 0) { - return set_err(s1, 'a'); - } - if ((ret = close(ln)) == 0 || errno != EBADF) { - snprintf(err, errlen, "double tailscale_listener close = %d (errno %d: %s), want EBADF", ret, errno, strerror(errno)); - return 1; - } - - if ((ret = tailscale_loopback(s1, addr, addrlen, proxy_cred, local_api_cred)) != 0) { - return set_err(s1, 'b'); - } - - return 0; -} - -int close_conn() { - if (tailscale_close(s1) != 0) { - return set_err(s1, 'd'); - } - if (tailscale_close(s2) != 0) { - return set_err(s2, 'e'); - } - return 0; -} -*/ +//#include "tsnetctest.h" import "C" import ( "context" "flag" + "fmt" "io" "net/http" "net/http/httptest" @@ -155,7 +27,7 @@ import ( var verboseDERP = flag.Bool("verbose-derp", false, "if set, print DERP and STUN logs") func RunTestConn(t *testing.T) { - // Corp#4520: don't use netns for tests. + fmt.Println("Start Test connection") netns.SetEnabled(false) t.Cleanup(func() { netns.SetEnabled(true) @@ -173,6 +45,7 @@ func RunTestConn(t *testing.T) { control.HTTPTestServer.Start() t.Cleanup(control.HTTPTestServer.Close) controlURL := control.HTTPTestServer.URL + fmt.Println("testcontrol listening on", controlURL) t.Logf("testcontrol listening on %s", controlURL) C.control_url = C.CString(controlURL) @@ -184,8 +57,9 @@ func RunTestConn(t *testing.T) { tmps2 := filepath.Join(tmp, "s2") os.MkdirAll(tmps2, 0755) C.tmps2 = C.CString(tmps2) - + fmt.Println("") if C.test_conn() != 0 { + fmt.Println("Error", C.err) t.Fatal(C.GoString(C.err)) } @@ -196,23 +70,27 @@ func RunTestConn(t *testing.T) { t.Logf("fetching local API status from %q", localAPIStatus) req, err := http.NewRequestWithContext(ctx, "GET", localAPIStatus, nil) if err != nil { + fmt.Println("Error2", err) t.Fatal(err) } req.Header.Set("Sec-Tailscale", "localapi") req.SetBasicAuth("", C.GoString(C.local_api_cred)) res, err := http.DefaultClient.Do(req) if err != nil { + fmt.Println("Error3", err) t.Fatal(err) } b, err := io.ReadAll(res.Body) res.Body.Close() if err != nil { + fmt.Println("Error4", err) t.Fatal(err) } if res.StatusCode != 200 { + fmt.Println("Status code issue", res.StatusCode) t.Errorf("/status: %d: %s", res.StatusCode, b) } - + fmt.Println("Close connection") if C.close_conn() != 0 { t.Fatal(C.GoString(C.err)) } diff --git a/tsnetctest/tsnetctest.h b/tsnetctest/tsnetctest.h new file mode 100644 index 0000000..f341637 --- /dev/null +++ b/tsnetctest/tsnetctest.h @@ -0,0 +1,37 @@ +#ifndef TSNETTEST +#define TSNETTEST + +#include +#include +#include +#include +#include "../tailscale.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern char *tmps1; + extern char *tmps2; + + extern char *control_url; + + extern int addrlen; + extern char *addr; + extern char *proxy_cred; + extern char *local_api_cred; + + extern int errlen; + extern char *err; + + extern tailscale s1, s2; + + extern int set_err(tailscale sd, char tag); + extern int test_conn(); + extern int close_conn(); + +#endif +#ifdef __cplusplus +} +#endif \ No newline at end of file