Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/api/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,10 @@ If the socket is `ref`ed calling `ref` again will have no effect.

<!-- YAML
added: v0.5.10
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/67402
description: The peer path is now reported for Unix domain sockets.
-->

* Type: {string}
Expand All @@ -1610,6 +1614,14 @@ The string representation of the remote IP address. For example,
`'74.125.127.100'` or `'2001:4860:a005::68'`. Value may be `undefined` if
the socket is destroyed (for example, if the client disconnected).

For a [Unix domain socket][IPC], this is the path the peer is bound to: the
listening path on the connecting side, and on the accepting side the path of a
client that was bound before connecting (see [`BoundSocket`][]). It is
`undefined` if the peer is not bound to a path, which is the case for most
clients. [`socket.remoteFamily`][] and [`socket.remotePort`][] are always
`undefined` for these connections, as is `socket.remoteAddress` for a Windows
named pipe.

### `socket.remoteFamily`

<!-- YAML
Expand Down Expand Up @@ -2680,6 +2692,8 @@ console.log('listening on', server.address().port);
[`socket.end()`]: #socketenddata-encoding-callback
[`socket.localAddress`]: #socketlocaladdress
[`socket.pause()`]: #socketpause
[`socket.remoteFamily`]: #socketremotefamily
[`socket.remotePort`]: #socketremoteport
[`socket.resume()`]: #socketresume
[`socket.setEncoding()`]: #socketsetencodingencoding
[`socket.setKeepAlive()`]: #socketsetkeepalive
Expand Down
6 changes: 4 additions & 2 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -2908,8 +2908,10 @@ function onconnection(err, clientHandle) {
return;
}

// The 'drop' data and the BlockList check describe IP endpoints only.
const isPipe = clientHandle instanceof Pipe;
if (self.maxConnections != null && self._connections >= self.maxConnections) {
if (clientHandle.getsockname || clientHandle.getpeername) {
if (!isPipe && (clientHandle.getsockname || clientHandle.getpeername)) {
const data = { __proto__: null };
if (clientHandle.getsockname) {
const localInfo = { __proto__: null };
Expand All @@ -2932,7 +2934,7 @@ function onconnection(err, clientHandle) {
clientHandle.close();
return;
}
if (self.blockList && typeof clientHandle.getpeername === 'function') {
if (self.blockList && !isPipe && typeof clientHandle.getpeername === 'function') {
const remoteInfo = { __proto__: null };
clientHandle.getpeername(remoteInfo);
const addressType = isIP(remoteInfo.address);
Expand Down
47 changes: 45 additions & 2 deletions src/pipe_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ using v8::Int32;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;

MaybeLocal<Object> PipeWrap::Instantiate(Environment* env,
Expand Down Expand Up @@ -83,7 +85,11 @@ void PipeWrap::Initialize(Local<Object> target,
SetProtoMethod(isolate, t, "connect", Connect);
SetProtoMethod(isolate, t, "open", Open);

#ifdef _WIN32
#ifndef _WIN32
// A named pipe has a single name rather than one per end, so no peer name
// is exposed on Windows.
SetProtoMethod(isolate, t, "getpeername", GetPeerName);
#else
SetProtoMethod(isolate, t, "setPendingInstances", SetPendingInstances);
#endif

Expand Down Expand Up @@ -114,7 +120,9 @@ void PipeWrap::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Listen);
registry->Register(Connect);
registry->Register(Open);
#ifdef _WIN32
#ifndef _WIN32
registry->Register(GetPeerName);
#else
registry->Register(SetPendingInstances);
#endif
registry->Register(Fchmod);
Expand Down Expand Up @@ -271,6 +279,41 @@ void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(err);
}

#ifndef _WIN32
void PipeWrap::GetPeerName(const FunctionCallbackInfo<Value>& args) {
PipeWrap* wrap;
ASSIGN_OR_RETURN_UNWRAP(
&wrap, args.This(), args.GetReturnValue().Set(UV_EBADF));
Environment* env = wrap->env();
CHECK(args[0]->IsObject());

MaybeStackBuffer<char, 256> buf;
size_t size = buf.capacity();
int err = uv_pipe_getpeername(&wrap->handle_, *buf, &size);
if (err == UV_ENOBUFS) {
buf.AllocateSufficientStorage(size);
size = buf.capacity();
err = uv_pipe_getpeername(&wrap->handle_, *buf, &size);
}

// An unbound peer has an empty name, which leaves the address unset.
if (err == 0 && size > 0) {
Local<String> name;
if (!String::NewFromUtf8(
env->isolate(), *buf, NewStringType::kNormal, size)
.ToLocal(&name) ||
args[0]
.As<Object>()
->Set(env->context(), env->address_string(), name)
.IsNothing()) {
return;
}
}

args.GetReturnValue().Set(err);
}
#endif

} // namespace node

NODE_BINDING_CONTEXT_AWARE_INTERNAL(pipe_wrap, node::PipeWrap::Initialize)
Expand Down
4 changes: 3 additions & 1 deletion src/pipe_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ class PipeWrap : public ConnectionWrap<PipeWrap, uv_pipe_t> {
static void Connect(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Open(const v8::FunctionCallbackInfo<v8::Value>& args);

#ifdef _WIN32
#ifndef _WIN32
static void GetPeerName(const v8::FunctionCallbackInfo<v8::Value>& args);
#else
static void SetPendingInstances(
const v8::FunctionCallbackInfo<v8::Value>& args);
#endif
Expand Down
99 changes: 99 additions & 0 deletions test/parallel/test-net-pipe-remote-address.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const net = require('net');
const tmpdir = require('../common/tmpdir');

tmpdir.refresh();

function checkPipePeer(socket, expected) {
assert.strictEqual(socket.remoteAddress, expected);
assert.strictEqual(socket.remoteFamily, undefined);
assert.strictEqual(socket.remotePort, undefined);
}

// An unbound client has no name: only the client side sees a peer name. A
// Windows named pipe has no peer name on either side.
{
const path = `${common.PIPE}-unbound`;
const listenerPath = common.isWindows ? undefined : path;
const server = net.createServer(common.mustCall((socket) => {
checkPipePeer(socket, undefined);
socket.end();
}));
server.listen(path, common.mustCall(() => {
const client = net.connect({ path });
checkPipePeer(client, undefined);
client.on('connect', common.mustCall(() => {
checkPipePeer(client, listenerPath);
client.resume();
}));
// The cached value survives the peer closing and the socket's destruction.
client.on('close', common.mustCall(() => {
assert.strictEqual(client.destroyed, true);
checkPipePeer(client, listenerPath);
server.close();
}));
}));
}

function testBound(serverPath, clientPath) {
const server = net.createServer(common.mustCall((socket) => {
checkPipePeer(socket, clientPath);
socket.resume();
socket.on('close', common.mustCall(() => {
checkPipePeer(socket, clientPath);
server.close();
}));
}));
server.listen(serverPath, common.mustCall(() => {
const client = new net.Socket({
handle: new net.BoundSocket({ path: clientPath }),
});
client.connect({ path: serverPath });
client.on('connect', common.mustCall(() => {
checkPipePeer(client, serverPath);
client.end();
}));
}));
}

// Binding a client to a path is specific to Unix domain sockets.
if (!common.isWindows) {
testBound(`${common.PIPE}-server`, `${common.PIPE}-client`);
}

if (common.isLinux) {
testBound(`\0node-test-peer-server-${process.pid}`,
`\0node-test-peer-client-${process.pid}`);
}

// A BlockList only applies to IP peers and must not reject pipe connections.
{
const path = `${common.PIPE}-blocklist`;
const blockList = new net.BlockList();
blockList.addCIDR('0.0.0.0/0');
blockList.addCIDR('::/0');
const server = net.createServer({ blockList }, common.mustCall((socket) => {
socket.end();
server.close();
}));
server.listen(path, common.mustCall(() => {
net.connect({ path }).resume();
}));
}

// Dropped pipe connections keep reporting no 'drop' data.
{
const path = `${common.PIPE}-drop`;
const server = net.createServer(common.mustNotCall());
server.maxConnections = 0;
server.on('drop', common.mustCall((data) => {
assert.strictEqual(data, undefined);
server.close();
}));
server.listen(path, common.mustCall(() => {
net.connect({ path }).on('error', () => {}).resume();
}));
}
Loading