-
-
Notifications
You must be signed in to change notification settings - Fork 32k
permission: add permission for udp #53398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -357,6 +357,58 @@ Error: Access to this API has been restricted | |
} | ||
``` | ||
|
||
### `--allow-net-udp` | ||
|
||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
> Stability: 1.1 - Active development | ||
|
||
When using the [Permission Model][], the process will not be able to create, | ||
receive, and send UDP packages by default. Attempts to do so will throw an | ||
`ERR_ACCESS_DENIED` unless the user explicitly passes the `--allow-net-udp` flag | ||
when starting Node.js. Node.js will check the permission when create a UDP socket | ||
from fd. | ||
|
||
The argument format is `--allow-net-udp=domain_or_ip[/netmask][:port]`. | ||
The valid arguments are: | ||
|
||
* `*` - To allow all operations. | ||
* `--allow-net-udp=nodejs.org` | ||
* `--allow-net-udp=127.0.0.1` | ||
* `--allow-net-udp=127.0.0.1:8888` | ||
* `--allow-net-udp=127.0.0.1:*` | ||
* `--allow-net-udp=*:9999` | ||
* `--allow-net-udp=127.0.0.1/24:*` | ||
* `--allow-net-udp=127.0.0.1/255.255.255.0:*` | ||
* `--allow-net-udp=127.0.0.1:8080 --allow-net-udp=127.0.0.1:9090` | ||
* `--allow-net-udp=127.0.0.1:8080,localhost:9090` | ||
|
||
Example: | ||
|
||
```js | ||
const dgram = require('node:dgram'); | ||
dgram.createSocket('udp4').bind(9297, '127.0.0.1') | ||
``` | ||
|
||
```console | ||
$ node --experimental-permission --allow-fs-read=./index.js index.js | ||
node:events:498 | ||
throw er; // Unhandled 'error' event | ||
^ | ||
|
||
Error [ERR_ACCESS_DENIED]: Access to this API has been restricted. Permission: bind to 127.0.0.1/9297 | ||
at node:dgram:379:18 | ||
at process.processTicksAndRejections (node:internal/process/task_queues:77:11) | ||
Emitted 'error' event on Socket instance at: | ||
at afterDns (node:dgram:337:12) | ||
at node:dgram:379:9 | ||
at process.processTicksAndRejections (node:internal/process/task_queues:77:11) { | ||
code: 'ERR_ACCESS_DENIED' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ping |
||
} | ||
``` | ||
|
||
### `--build-snapshot` | ||
|
||
<!-- YAML | ||
|
@@ -1014,6 +1066,7 @@ following permissions are restricted: | |
* Child Process - manageable through [`--allow-child-process`][] flag | ||
* Worker Threads - manageable through [`--allow-worker`][] flag | ||
* WASI - manageable through [`--allow-wasi`][] flag | ||
* UDP - manageable through [`--allow-net-udp`][] flag | ||
|
||
### `--experimental-require-module` | ||
|
||
|
@@ -2835,6 +2888,7 @@ one is included in the list below. | |
* `--allow-child-process` | ||
* `--allow-fs-read` | ||
* `--allow-fs-write` | ||
* `--allow-net-udp` | ||
* `--allow-wasi` | ||
* `--allow-worker` | ||
* `--conditions`, `-C` | ||
|
@@ -3390,6 +3444,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 | |
[`--allow-child-process`]: #--allow-child-process | ||
[`--allow-fs-read`]: #--allow-fs-read | ||
[`--allow-fs-write`]: #--allow-fs-write | ||
[`--allow-net-udp`]: #--allow-net-udp | ||
[`--allow-wasi`]: #--allow-wasi | ||
[`--allow-worker`]: #--allow-worker | ||
[`--build-snapshot`]: #--build-snapshot | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ const { | |
ErrnoException, | ||
ExceptionWithHostPort, | ||
codes: { | ||
ERR_ACCESS_DENIED, | ||
ERR_BUFFER_OUT_OF_BOUNDS, | ||
ERR_INVALID_ARG_TYPE, | ||
ERR_INVALID_FD_TYPE, | ||
|
@@ -81,6 +82,7 @@ const { | |
|
||
const dc = require('diagnostics_channel'); | ||
const udpSocketChannel = dc.channel('udp.socket'); | ||
const permission = require('internal/process/permission'); | ||
|
||
const BIND_STATE_UNBOUND = 0; | ||
const BIND_STATE_BINDING = 1; | ||
|
@@ -327,12 +329,9 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) { | |
else | ||
address = '::'; | ||
} | ||
|
||
// Resolve address first | ||
state.handle.lookup(address, (err, ip) => { | ||
const afterDns = (err, ip) => { | ||
if (!state.handle) | ||
return; // Handle has been closed in the mean time | ||
|
||
if (err) { | ||
state.bindState = BIND_STATE_UNBOUND; | ||
this.emit('error', err); | ||
|
@@ -361,7 +360,7 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) { | |
this.emit('error', ex); | ||
}); | ||
} else { | ||
const err = state.handle.bind(ip, port || 0, flags); | ||
const err = state.handle.bind(ip, port || 0, flags, false); | ||
if (err) { | ||
const ex = new ExceptionWithHostPort(err, 'bind', ip, port); | ||
state.bindState = BIND_STATE_UNBOUND; | ||
|
@@ -372,7 +371,22 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) { | |
|
||
startListening(this); | ||
} | ||
}); | ||
}; | ||
if (permission.isEnabled()) { | ||
const resource = `${address}/${port || '*'}`; | ||
ShogunPanda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!permission.has('net.udp', resource)) { | ||
process.nextTick(() => { | ||
afterDns(new ERR_ACCESS_DENIED( | ||
`bind to ${resource}`, | ||
'NetUDP', | ||
resource, | ||
)); | ||
}); | ||
return this; | ||
} | ||
} | ||
// Resolve address first | ||
state.handle.lookup(address, afterDns); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't we throw the error in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the address which is allowed is a domain, we should allow bind to all IPs this domain ponit to, so I think we need check before DNS lookup. |
||
|
||
return this; | ||
}; | ||
|
@@ -413,13 +427,38 @@ function _connect(port, address, callback) { | |
this.once('connect', callback); | ||
|
||
const afterDns = (ex, ip) => { | ||
if (!ex && !address && permission.isEnabled()) { | ||
const resource = `${ip}/${port}`; | ||
ShogunPanda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!permission.has('net.udp', resource)) { | ||
ex = new ERR_ACCESS_DENIED( | ||
`connect to ${resource}`, | ||
'NetUDP', | ||
resource, | ||
); | ||
} | ||
} | ||
defaultTriggerAsyncIdScope( | ||
this[async_id_symbol], | ||
doConnect, | ||
ex, this, ip, address, port, callback, | ||
); | ||
}; | ||
|
||
// If the address which is allowed is a domain, | ||
// we should allow bind to all IPs this domain ponit to, | ||
// so we need check here instead of in afterDns | ||
if (address && permission.isEnabled()) { | ||
const resource = `${address}/${port}`; | ||
ShogunPanda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!permission.has('net.udp', resource)) { | ||
process.nextTick(() => { | ||
afterDns(new ERR_ACCESS_DENIED( | ||
`connect to ${resource}`, | ||
'NetUDP', | ||
resource, | ||
)); | ||
}); | ||
return; | ||
} | ||
} | ||
state.handle.lookup(address, afterDns); | ||
} | ||
|
||
|
@@ -430,7 +469,7 @@ function doConnect(ex, self, ip, address, port, callback) { | |
return; | ||
|
||
if (!ex) { | ||
const err = state.handle.connect(ip, port); | ||
const err = state.handle.connect(ip, port, false); | ||
if (err) { | ||
ex = new ExceptionWithHostPort(err, 'connect', address, port); | ||
} | ||
|
@@ -663,6 +702,17 @@ Socket.prototype.send = function(buffer, | |
} | ||
|
||
const afterDns = (ex, ip) => { | ||
// If we have not checked before dns, check it now | ||
if (!ex && !connected && !address && permission.isEnabled()) { | ||
const resource = `${ip}/${port}`; | ||
ShogunPanda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!permission.has('net.udp', resource)) { | ||
ex = new ERR_ACCESS_DENIED( | ||
`send to ${resource}`, | ||
'NetUDP', | ||
resource, | ||
); | ||
} | ||
} | ||
defaultTriggerAsyncIdScope( | ||
this[async_id_symbol], | ||
doSend, | ||
|
@@ -671,6 +721,19 @@ Socket.prototype.send = function(buffer, | |
}; | ||
|
||
if (!connected) { | ||
if (address && permission.isEnabled()) { | ||
const resource = `${address}/${port}`; | ||
ShogunPanda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!permission.has('net.udp', resource)) { | ||
process.nextTick(() => { | ||
afterDns(new ERR_ACCESS_DENIED( | ||
`send to ${resource}`, | ||
'NetUDP', | ||
resource, | ||
)); | ||
}); | ||
return; | ||
} | ||
} | ||
state.handle.lookup(address, afterDns); | ||
} else { | ||
afterDns(null, null); | ||
|
@@ -703,7 +766,7 @@ function doSend(ex, self, ip, list, address, port, callback) { | |
|
||
let err; | ||
if (port) | ||
err = state.handle.send(req, list, list.length, port, ip, !!callback); | ||
err = state.handle.send(req, list, list.length, port, ip, !!callback, false); | ||
else | ||
err = state.handle.send(req, list, list.length, !!callback); | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.