Skip to content

Commit 710150c

Browse files
committed
socket: support unix paths in the abstract namespace
Those starting with '\0'.
1 parent e2744f1 commit 710150c

File tree

4 files changed

+119
-11
lines changed

4 files changed

+119
-11
lines changed

ext/sockets/conversions.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ typedef struct {
9898
} field_descriptor;
9999

100100
#define KEY_FILL_SOCKADDR "fill_sockaddr"
101-
#define KEY_RECVMSG_RET "recvmsg_ret"
102-
#define KEY_CMSG_LEN "cmsg_len"
101+
#define KEY_RECVMSG_RET "recvmsg_ret"
102+
#define KEY_CMSG_LEN "cmsg_len"
103103

104104
const struct key_value empty_key_value_list[] = {{0}};
105105

@@ -667,6 +667,13 @@ static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_
667667
path = &lzval;
668668
}
669669

670+
/* code in this file relies on the path being nul terminated, even though
671+
* this is not required, at least on linux for abstract paths. It also
672+
* assumes that the path is not empty */
673+
if (Z_STRLEN_P(path) == 0) {
674+
do_from_zval_err(ctx, "%s", "the path is cannot be empty");
675+
return;
676+
}
670677
if (Z_STRLEN_P(path) >= sizeof(saddr->sun_path)) {
671678
do_from_zval_err(ctx, "the path is too long, the maximum permitted "
672679
"length is %ld", sizeof(saddr->sun_path) - 1);
@@ -768,10 +775,22 @@ static void from_zval_write_sockaddr_aux(const zval *container,
768775
return;
769776
}
770777
*sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_un), ctx);
771-
*sockaddr_len = sizeof(struct sockaddr_un);
772778
if (fill_sockaddr) {
779+
struct sockaddr_un *sock_un = (struct sockaddr_un*)*sockaddr_ptr;
780+
773781
from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx);
774782
(*sockaddr_ptr)->sa_family = AF_UNIX;
783+
784+
/* calculating length is more complicated here. Giving the size of
785+
* struct sockaddr_un here and relying on the nul termination of
786+
* sun_path does not work for paths in the abstract namespace. Note
787+
* that we always assume the path is not empty and nul terminated */
788+
*sockaddr_len = offsetof(struct sockaddr_un, sun_path) +
789+
(sock_un->sun_path[0] == '\0'
790+
? (1 + strlen(&sock_un->sun_path[1]))
791+
: strlen(sock_un->sun_path));
792+
} else {
793+
*sockaddr_len = sizeof(struct sockaddr_un);
775794
}
776795
break;
777796

ext/sockets/sockets.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,7 +1479,7 @@ PHP_FUNCTION(socket_strerror)
14791479
PHP_FUNCTION(socket_bind)
14801480
{
14811481
zval *arg1;
1482-
php_sockaddr_storage sa_storage;
1482+
php_sockaddr_storage sa_storage = {0};
14831483
struct sockaddr *sock_type = (struct sockaddr*) &sa_storage;
14841484
php_socket *php_sock;
14851485
char *addr;
@@ -1497,19 +1497,26 @@ PHP_FUNCTION(socket_bind)
14971497
case AF_UNIX:
14981498
{
14991499
struct sockaddr_un *sa = (struct sockaddr_un *) sock_type;
1500-
memset(sa, 0, sizeof(sa_storage));
1500+
15011501
sa->sun_family = AF_UNIX;
1502-
snprintf(sa->sun_path, 108, "%s", addr);
1503-
retval = bind(php_sock->bsd_socket, (struct sockaddr *) sa, SUN_LEN(sa));
1502+
1503+
if (addr_len >= sizeof(sa->sun_path)) {
1504+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
1505+
"Invalid path: too long (maximum size is %d)",
1506+
(int)sizeof(sa->sun_path) - 1);
1507+
RETURN_FALSE;
1508+
}
1509+
memcpy(&sa->sun_path, addr, addr_len);
1510+
1511+
retval = bind(php_sock->bsd_socket, (struct sockaddr *) sa,
1512+
offsetof(struct sockaddr_un, sun_path) + addr_len);
15041513
break;
15051514
}
15061515

15071516
case AF_INET:
15081517
{
15091518
struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
15101519

1511-
memset(sa, 0, sizeof(sa_storage)); /* Apparently, Mac OSX needs this */
1512-
15131520
sa->sin_family = AF_INET;
15141521
sa->sin_port = htons((unsigned short) port);
15151522

@@ -1525,8 +1532,6 @@ PHP_FUNCTION(socket_bind)
15251532
{
15261533
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
15271534

1528-
memset(sa, 0, sizeof(sa_storage)); /* Apparently, Mac OSX needs this */
1529-
15301535
sa->sin6_family = AF_INET6;
15311536
sa->sin6_port = htons((unsigned short) port);
15321537

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
Support for paths in the abstract namespace (bind, connect)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('sockets'))
6+
die('skip sockets extension not available.');
7+
8+
if (PHP_OS != 'Linux') {
9+
die('skip For Linux only');
10+
}
11+
?>
12+
--FILE--
13+
<?php
14+
include __DIR__."/mcast_helpers.php.inc";
15+
16+
$path = "\x00/foo_bar";
17+
18+
echo "creating server socket\n";
19+
$servers = socket_create(AF_UNIX, SOCK_STREAM, 0) or die("err");
20+
socket_bind($servers, $path) or die("Could not bind");
21+
socket_listen($servers) or die("Could not listen");
22+
socket_set_nonblock($servers) or die("Could not put in non-blocking mode");
23+
24+
echo "creating client socket\n";
25+
$clients = socket_create(AF_UNIX, SOCK_STREAM, 0) or die("err");
26+
socket_connect($clients, $path) or die("Error connecting");
27+
28+
$conns = socket_accept($servers) or die("Could not accept connection");
29+
30+
$r = socket_sendmsg($clients, [
31+
//"name" => [ "addr" => $path, ],
32+
"iov" => ["test ", "thing", "\n"],
33+
], 0);
34+
var_dump($r);
35+
checktimeout($conns, 500);
36+
37+
if (!socket_recv($conns, $buf, 20, 0)) die("recv");
38+
print_r($buf);
39+
?>
40+
--EXPECTF--
41+
creating server socket
42+
creating client socket
43+
int(11)
44+
test thing
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Support for paths in the abstract namespace (bind, sendmsg, recvmsg)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('sockets'))
6+
die('skip sockets extension not available.');
7+
8+
if (PHP_OS != 'Linux') {
9+
die('skip For Linux only');
10+
}
11+
?>
12+
--FILE--
13+
<?php
14+
include __DIR__."/mcast_helpers.php.inc";
15+
16+
$path = "\x00/bar_foo";
17+
18+
echo "creating send socket\n";
19+
$sends1 = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
20+
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
21+
22+
echo "creating receive socket\n";
23+
$s = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
24+
socket_bind($s, $path) or die("err");
25+
26+
$r = socket_sendmsg($sends1, [
27+
"name" => [ "path" => $path],
28+
"iov" => ["test ", "thing", "\n"],
29+
], 0);
30+
var_dump($r);
31+
checktimeout($s, 500);
32+
33+
if (!socket_recv($s, $buf, 20, 0)) die("recv");
34+
print_r($buf);
35+
?>
36+
--EXPECTF--
37+
creating send socket
38+
creating receive socket
39+
int(11)
40+
test thing

0 commit comments

Comments
 (0)