-
Notifications
You must be signed in to change notification settings - Fork 653
linux abstract sockets support in connect / listen #1486
Comments
Hi! Yeah, both connect and bind (for pipes) would need to be adjusted. Now, given that abstract sockets are a linux-only thing, we'd be penalyzing the API for everyone else, I'm not 100% sure I'd want to do that. You can still create the socket yourself and pass it As for making this work with node, I created node-abstractsocket a while ago: https://github.com/saghul/node-abstractsocket, which exposes the same API as the net module, you might find it useful. |
great, I searched for existing module a while ago and couldn't find any basic support ( pipe_open only ) would be extra ~10 lines of code in terms of size and nearly zero penalty in execution speed ( extra check first byte of name ). API wise, that's extra paragraph in the doco "to connect to abstract socket you need to prefix name with '@'" |
I created it recently, to get my feet wet in addons development, but it should be solid, there is not much to it :-)
"@" is just what Linux uses to pretty print abstract socket names, using it to delimit actual abstract sockets is a workaround. I just did a quick test and I can bind to a Unix domain socket called "@lala" just fine. Whould would use that as a name I don't know, but I'd rather not guess :-) |
well, that would be handled in "unescape" part of non-abstract connect :) another reason to support - python and ruby have abstract sockets support, so might benefit pyuv |
I actually plan to add support for it in pyuv, but there it's easy because a Python string can have embedded nulls, so I can extract the C char pointer and real length and workaround this limitation. Given that this a Linux only thing, the cross-platform argument loses a bit of strength ;-) but that doesn't mean it wouldn't be nice to have it baked in. @indutny, @bnoordhuis, @piscisaureus: what do you guys think? Should we get the length when binding / connecting to a pipe? |
s/cross-platform/cross-architecture/ :) raspberry pi / android devices / routers / tizen |
So we only need to add a length parameter alongside the the string? Trivial solution and binding authors wouldn't be too mad about. Would add a bit more C code, but C is already tedious. |
Yes, that's all we need because |
Passing a \0 prefixed should already work for uv_pipe_connect. We are not using strlen anywhere. But for uv_pipe_bind we'd need a length parameter, since we are using strdup: Line 60 in 49cb40c
But we could just add a third parameter We also could just check in the string if the first char is == '\0'. I don't think any one would open a pipe with the name "". |
strncpy will stop when it finds the first '\0', it doesn't work. |
but it works for some reason even with that strncpy |
My proposed api breakes if you pass a memory chunk with the size 1 having only "\0". Then it will raise a memory exception. |
That approach won't work. Abstract sockets are allowed to have embedded nulls, which you can't accommodate unless you accept the length. Add a test with "\0uv-test\0ouch" and you'll see. |
another funny thing is that "\0\0" is a valid unix domain socket. |
@txdv yeah, while your solution is good enough for my use case unfortunately it's not just '\0' + normal c string |
what other usecases are there? |
@txdv what I said, embedded nulls. |
0 everywhere. Why would they add those embedded zeros? |
http://troydhanson.github.io/misc/Unix_domain_sockets.html it says nothing about embedded nulls apart from the first one |
man unix: abstract: an abstract socket address is distinguished by the fact that sun_path[0] is a null byte ('\0'). The socket's address in this namespace is |
@saghul is correct; if the path starts with a null byte, the kernel treats it as an opaque binary blob (up to 108 bytes, that is.) By the way, there's also an autobind feature for abstract sockets. When you call bind() with addrlen == sizeof(short), the kernel will assign the first free name from a 1M entries namespace. It can, in theory, fail with ENOSPC when all entries are in use or take a long time to complete because the scan is linear. |
@bnoordhuis interesting. Is this (autobind) documented somewhere? |
@sidorares It should be documented in |
|
As Android uses abstract sockets, it would be good if libuv could support it. Here are some alternatives to use a length parameter: 1 . Using the length encoded in the string, like a netstring format but without the final comma:
2 . If the first char is null, use the next one as que delimiter of the end:
In this method the user can choose a delimiter that is not contained in the socket name. Both methods support nulls in the socket name. |
Interestingly, with method 2 if we will use an abstract socket name without nulls inside (most cases) we can just do:
Because it will have the null character at the end. Creating the socket name for libuvWithout nulls: char name[128];
name[0] = 0;
name[1] = 0;
strncpy(&name[2], plain_name, 128-3); Containing null: char name[128], *ptr = &name[0];
*p++ = 0;
*p++ = '|'; /* or another char */
memcpy(p, sock_name, sock_length);
p += sock_length;
*p++ = '|'; /* or another char */ and then:
|
We could even have a function inside libuv for creating these socket names. It could detect the presence of chars to select the delimiter automatically: char * uv_build_abstract_socket_name(char *name, int length, char *buf) {
char used[256], delimiter, *dest = buf;
int i, found=0;
if (length > MAX_ABS_SOCK_LEN) return 0;
/* mark the used characters */
memset(used, 0, 256);
for (i=0; i<length; i++){
used[name[i]] = 1;
}
/* search for an unused character */
for (i=0; i<256; i++){
if (!used[i]) {
delimiter = i;
found = 1;
break;
}
}
if (!found) return 0;
/* build the abstract socket name */
*dest++ = 0;
*dest++ = delimiter;
memcpy(dest, name, length);
dest += length;
*dest++ = delimiter;
return buf;
} Usage: char buf[128], *sock_name;
sock_name = uv_build_abstract_socket_name(name, strlen(name), buf);
if (!sock_name) handle_error_here;
uv_pipe_bind(handle, sock_name); |
And then libuv could read these values, like this (not tested): int uv__get_pipe_name(char *name, char *pipe_name, int max_len) {
int len;
if (name[0]==0) {
char delimiter = name[1];
char *end = strchr(&name[2], delimiter);
len = end - name - 2;
if (len > max_len) return 0;
*pipe_name++ = 0; /* store the starting null char */
memcpy(pipe_name, &name[2], len);
len++; /* include the first null char */
} else {
len = strlen(name);
if (len > max_len) return 0;
strcpy(pipe_name, name);
}
return len;
} Usage: char pipe_name[128];
int len = uv__get_pipe_name(name, pipe_name, sizeof(saddr.sun_path) - 1);
if (len <= 0) return UV_EINVAL;
...
memcpy(saddr.sun_path, pipe_name, len); I know this is not the place to post code, but as I don't have enough time to make the full implementation I will let my code here as a contribution. |
I like the idea of using a function from libuv to build the abstract socket name. This allows us to change the internal (libuv) name representation without breaking backwards compatibility. Note that the user does not need to know how libuv encodes it. It just needs to call the We could even use another format: with the length encoded in a single byte.
|
libuv already supports abstract sockets in
uv_pipe_getsockname
Unfortunately, there is no way to connect to abstract socket with
uv_pipe_connect
.I'm currently wrapping socat binary to be able to connect to abstract socket address from node-dbus client but it would make life so much easier if abstract socket would just work from core node.
Some my notes here: sidorares/abstractsocket#3
Few problems here:
Afaik python and ruby connect to abstract sockets using "\0name". At uv level allowing zero in socket name would require changing
uv_pipe_connect
signature (or adding very similar one likeuv_pipe_connect2(req, handle, name, name_length, cb)
. Possible solution: encode leading zero somehow ( "@" prefix? do we want to allow non-abstract sockets to connect to"@name"
? If so, how to escape it -"@@name"
? ).I have node version which is able to connect to
socat abstract-listen:somename -
server by doingnet.connect('\0somename')
. Would you be interested in PR? Is yes, which is preferred way - addname_length
parameters where required or encode "\0" at js side and decode back at uv side ( by prefixing with @ for example )?The text was updated successfully, but these errors were encountered: