socket: support unix paths in the abstract namespace

Those starting with '\0'.
This commit is contained in:
Gustavo Lopes 2013-07-15 01:51:15 +02:00
parent e2744f1aa3
commit 710150ccb7
4 changed files with 119 additions and 11 deletions

View file

@ -98,8 +98,8 @@ typedef struct {
} field_descriptor; } field_descriptor;
#define KEY_FILL_SOCKADDR "fill_sockaddr" #define KEY_FILL_SOCKADDR "fill_sockaddr"
#define KEY_RECVMSG_RET "recvmsg_ret" #define KEY_RECVMSG_RET "recvmsg_ret"
#define KEY_CMSG_LEN "cmsg_len" #define KEY_CMSG_LEN "cmsg_len"
const struct key_value empty_key_value_list[] = {{0}}; const struct key_value empty_key_value_list[] = {{0}};
@ -667,6 +667,13 @@ static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_
path = &lzval; path = &lzval;
} }
/* code in this file relies on the path being nul terminated, even though
* this is not required, at least on linux for abstract paths. It also
* assumes that the path is not empty */
if (Z_STRLEN_P(path) == 0) {
do_from_zval_err(ctx, "%s", "the path is cannot be empty");
return;
}
if (Z_STRLEN_P(path) >= sizeof(saddr->sun_path)) { if (Z_STRLEN_P(path) >= sizeof(saddr->sun_path)) {
do_from_zval_err(ctx, "the path is too long, the maximum permitted " do_from_zval_err(ctx, "the path is too long, the maximum permitted "
"length is %ld", sizeof(saddr->sun_path) - 1); "length is %ld", sizeof(saddr->sun_path) - 1);
@ -768,10 +775,22 @@ static void from_zval_write_sockaddr_aux(const zval *container,
return; return;
} }
*sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_un), ctx); *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_un), ctx);
*sockaddr_len = sizeof(struct sockaddr_un);
if (fill_sockaddr) { if (fill_sockaddr) {
struct sockaddr_un *sock_un = (struct sockaddr_un*)*sockaddr_ptr;
from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx); from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx);
(*sockaddr_ptr)->sa_family = AF_UNIX; (*sockaddr_ptr)->sa_family = AF_UNIX;
/* calculating length is more complicated here. Giving the size of
* struct sockaddr_un here and relying on the nul termination of
* sun_path does not work for paths in the abstract namespace. Note
* that we always assume the path is not empty and nul terminated */
*sockaddr_len = offsetof(struct sockaddr_un, sun_path) +
(sock_un->sun_path[0] == '\0'
? (1 + strlen(&sock_un->sun_path[1]))
: strlen(sock_un->sun_path));
} else {
*sockaddr_len = sizeof(struct sockaddr_un);
} }
break; break;

View file

@ -1479,7 +1479,7 @@ PHP_FUNCTION(socket_strerror)
PHP_FUNCTION(socket_bind) PHP_FUNCTION(socket_bind)
{ {
zval *arg1; zval *arg1;
php_sockaddr_storage sa_storage; php_sockaddr_storage sa_storage = {0};
struct sockaddr *sock_type = (struct sockaddr*) &sa_storage; struct sockaddr *sock_type = (struct sockaddr*) &sa_storage;
php_socket *php_sock; php_socket *php_sock;
char *addr; char *addr;
@ -1497,10 +1497,19 @@ PHP_FUNCTION(socket_bind)
case AF_UNIX: case AF_UNIX:
{ {
struct sockaddr_un *sa = (struct sockaddr_un *) sock_type; struct sockaddr_un *sa = (struct sockaddr_un *) sock_type;
memset(sa, 0, sizeof(sa_storage));
sa->sun_family = AF_UNIX; sa->sun_family = AF_UNIX;
snprintf(sa->sun_path, 108, "%s", addr);
retval = bind(php_sock->bsd_socket, (struct sockaddr *) sa, SUN_LEN(sa)); if (addr_len >= sizeof(sa->sun_path)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Invalid path: too long (maximum size is %d)",
(int)sizeof(sa->sun_path) - 1);
RETURN_FALSE;
}
memcpy(&sa->sun_path, addr, addr_len);
retval = bind(php_sock->bsd_socket, (struct sockaddr *) sa,
offsetof(struct sockaddr_un, sun_path) + addr_len);
break; break;
} }
@ -1508,8 +1517,6 @@ PHP_FUNCTION(socket_bind)
{ {
struct sockaddr_in *sa = (struct sockaddr_in *) sock_type; struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
memset(sa, 0, sizeof(sa_storage)); /* Apparently, Mac OSX needs this */
sa->sin_family = AF_INET; sa->sin_family = AF_INET;
sa->sin_port = htons((unsigned short) port); sa->sin_port = htons((unsigned short) port);
@ -1525,8 +1532,6 @@ PHP_FUNCTION(socket_bind)
{ {
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type; struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
memset(sa, 0, sizeof(sa_storage)); /* Apparently, Mac OSX needs this */
sa->sin6_family = AF_INET6; sa->sin6_family = AF_INET6;
sa->sin6_port = htons((unsigned short) port); sa->sin6_port = htons((unsigned short) port);

View file

@ -0,0 +1,44 @@
--TEST--
Support for paths in the abstract namespace (bind, connect)
--SKIPIF--
<?php
if (!extension_loaded('sockets'))
die('skip sockets extension not available.');
if (PHP_OS != 'Linux') {
die('skip For Linux only');
}
?>
--FILE--
<?php
include __DIR__."/mcast_helpers.php.inc";
$path = "\x00/foo_bar";
echo "creating server socket\n";
$servers = socket_create(AF_UNIX, SOCK_STREAM, 0) or die("err");
socket_bind($servers, $path) or die("Could not bind");
socket_listen($servers) or die("Could not listen");
socket_set_nonblock($servers) or die("Could not put in non-blocking mode");
echo "creating client socket\n";
$clients = socket_create(AF_UNIX, SOCK_STREAM, 0) or die("err");
socket_connect($clients, $path) or die("Error connecting");
$conns = socket_accept($servers) or die("Could not accept connection");
$r = socket_sendmsg($clients, [
//"name" => [ "addr" => $path, ],
"iov" => ["test ", "thing", "\n"],
], 0);
var_dump($r);
checktimeout($conns, 500);
if (!socket_recv($conns, $buf, 20, 0)) die("recv");
print_r($buf);
?>
--EXPECTF--
creating server socket
creating client socket
int(11)
test thing

View file

@ -0,0 +1,40 @@
--TEST--
Support for paths in the abstract namespace (bind, sendmsg, recvmsg)
--SKIPIF--
<?php
if (!extension_loaded('sockets'))
die('skip sockets extension not available.');
if (PHP_OS != 'Linux') {
die('skip For Linux only');
}
?>
--FILE--
<?php
include __DIR__."/mcast_helpers.php.inc";
$path = "\x00/bar_foo";
echo "creating send socket\n";
$sends1 = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
echo "creating receive socket\n";
$s = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
socket_bind($s, $path) or die("err");
$r = socket_sendmsg($sends1, [
"name" => [ "path" => $path],
"iov" => ["test ", "thing", "\n"],
], 0);
var_dump($r);
checktimeout($s, 500);
if (!socket_recv($s, $buf, 20, 0)) die("recv");
print_r($buf);
?>
--EXPECTF--
creating send socket
creating receive socket
int(11)
test thing