Skip to content

Commit 37f135c

Browse files
committed
Add stream_socket_sendto and stream_socket_recvfrom which work very much
like sendto() and recvfrom() syscalls.
1 parent fe93c2a commit 37f135c

File tree

10 files changed

+389
-12
lines changed

10 files changed

+389
-12
lines changed

ext/standard/basic_functions.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -731,10 +731,12 @@ function_entry basic_functions[] = {
731731
PHP_FE(stream_socket_server, second_and_third_args_force_ref)
732732
PHP_FE(stream_socket_accept, third_arg_force_ref)
733733
PHP_FE(stream_socket_get_name, NULL)
734+
PHP_FE(stream_socket_recvfrom, fourth_arg_force_ref)
735+
PHP_FE(stream_socket_sendto, NULL)
734736
PHP_FE(stream_copy_to_stream, NULL)
735737
PHP_FE(stream_get_contents, NULL)
736738
PHP_FE(fgetcsv, NULL)
737-
PHP_FE(flock, NULL)
739+
PHP_FE(flock, third_arg_force_ref)
738740
PHP_FE(get_meta_tags, NULL)
739741
PHP_FE(stream_set_write_buffer, NULL)
740742
PHP_FALIAS(set_file_buffer, stream_set_write_buffer, NULL)
@@ -1098,7 +1100,7 @@ PHP_MINIT_FUNCTION(basic)
10981100
REGISTER_LONG_CONSTANT("INI_PERDIR", ZEND_INI_PERDIR, CONST_CS | CONST_PERSISTENT);
10991101
REGISTER_LONG_CONSTANT("INI_SYSTEM", ZEND_INI_SYSTEM, CONST_CS | CONST_PERSISTENT);
11001102
REGISTER_LONG_CONSTANT("INI_ALL", ZEND_INI_ALL, CONST_CS | CONST_PERSISTENT);
1101-
1103+
11021104
REGISTER_LONG_CONSTANT("SUNFUNCS_RET_TIMESTAMP", SUNFUNCS_RET_TIMESTAMP, CONST_CS | CONST_PERSISTENT);
11031105
REGISTER_LONG_CONSTANT("SUNFUNCS_RET_STRING", SUNFUNCS_RET_STRING, CONST_CS | CONST_PERSISTENT);
11041106
REGISTER_LONG_CONSTANT("SUNFUNCS_RET_DOUBLE", SUNFUNCS_RET_DOUBLE, CONST_CS | CONST_PERSISTENT);

ext/standard/file.c

+4
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ PHP_MINIT_FUNCTION(file)
201201

202202
REGISTER_LONG_CONSTANT("STREAM_CLIENT_PERSISTENT", PHP_STREAM_CLIENT_PERSISTENT, CONST_CS | CONST_PERSISTENT);
203203
REGISTER_LONG_CONSTANT("STREAM_CLIENT_ASYNC_CONNECT", PHP_STREAM_CLIENT_ASYNC_CONNECT, CONST_CS | CONST_PERSISTENT);
204+
REGISTER_LONG_CONSTANT("STREAM_CLIENT_CONNECT", PHP_STREAM_CLIENT_CONNECT, CONST_CS | CONST_PERSISTENT);
205+
206+
REGISTER_LONG_CONSTANT("STREAM_PEEK", STREAM_PEEK, CONST_CS | CONST_PERSISTENT);
207+
REGISTER_LONG_CONSTANT("STREAM_OOB", STREAM_OOB, CONST_CS | CONST_PERSISTENT);
204208

205209
REGISTER_LONG_CONSTANT("STREAM_SERVER_BIND", STREAM_XPORT_BIND, CONST_CS | CONST_PERSISTENT);
206210
REGISTER_LONG_CONSTANT("STREAM_SERVER_LISTEN", STREAM_XPORT_LISTEN, CONST_CS | CONST_PERSISTENT);

ext/standard/streamsfuncs.c

+82-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ PHP_FUNCTION(stream_socket_client)
5454
char *hashkey = NULL;
5555
php_stream *stream = NULL;
5656
int err;
57-
long flags = 0;
57+
long flags = PHP_STREAM_CLIENT_CONNECT;
5858
char *errstr = NULL;
5959
php_stream_context *context = NULL;
6060

@@ -85,7 +85,7 @@ PHP_FUNCTION(stream_socket_client)
8585
}
8686

8787
stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE | REPORT_ERRORS,
88-
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT |
88+
STREAM_XPORT_CLIENT | (flags & PHP_STREAM_CLIENT_CONNECT ? STREAM_XPORT_CONNECT : 0) |
8989
(flags & PHP_STREAM_CLIENT_ASYNC_CONNECT ? STREAM_XPORT_CONNECT_ASYNC : 0),
9090
hashkey, &tv, context, &errstr, &err);
9191

@@ -136,7 +136,7 @@ PHP_FUNCTION(stream_socket_server)
136136
long host_len;
137137
zval *zerrno = NULL, *zerrstr = NULL, *zcontext = NULL;
138138
php_stream *stream = NULL;
139-
int err;
139+
int err = 0;
140140
long flags = STREAM_XPORT_BIND | STREAM_XPORT_LISTEN;
141141
char *errstr = NULL;
142142
php_stream_context *context = NULL;
@@ -266,6 +266,85 @@ PHP_FUNCTION(stream_socket_get_name)
266266
}
267267
/* }}} */
268268

269+
/* {{{ proto long stream_socket_sendto(resouce stream, string data [, long flags [, string target_addr]])
270+
Send data to a socket stream. If target_addr is specified it must be in dotted quad (or [ipv6]) format */
271+
PHP_FUNCTION(stream_socket_sendto)
272+
{
273+
php_stream *stream;
274+
zval *zstream;
275+
long flags = 0;
276+
char *data, *target_addr = NULL;
277+
long datalen, target_addr_len = 0;
278+
php_sockaddr_storage sa;
279+
socklen_t sl = 0;
280+
281+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|ls", &zstream, &data, &datalen, &flags, &target_addr, &target_addr_len) == FAILURE) {
282+
RETURN_FALSE;
283+
}
284+
php_stream_from_zval(stream, &zstream);
285+
286+
if (target_addr) {
287+
/* parse the address */
288+
if (FAILURE == php_network_parse_network_address_with_port(target_addr, target_addr_len, (struct sockaddr*)&sa, &sl TSRMLS_CC)) {
289+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse `%s' into a valid network address", target_addr);
290+
RETURN_FALSE;
291+
}
292+
}
293+
294+
RETURN_LONG(php_stream_xport_sendto(stream, data, datalen, flags, target_addr ? &sa : NULL, sl TSRMLS_CC));
295+
}
296+
/* }}} */
297+
298+
/* {{{ proto string stream_socket_recvfrom(resource stream, long amount [, long flags [, string &remote_addr]])
299+
Receives data from a socket stream */
300+
PHP_FUNCTION(stream_socket_recvfrom)
301+
{
302+
php_stream *stream;
303+
zval *zstream, *zremote = NULL;
304+
long to_read = 0;
305+
char *read_buf;
306+
long flags = 0;
307+
int recvd;
308+
309+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|lz", &zstream, &to_read, &flags, &zremote) == FAILURE) {
310+
RETURN_FALSE;
311+
}
312+
313+
php_stream_from_zval(stream, &zstream);
314+
315+
if (zremote) {
316+
zval_dtor(zremote);
317+
ZVAL_NULL(zremote);
318+
Z_STRLEN_P(zremote) = 0;
319+
}
320+
321+
read_buf = emalloc(to_read + 1);
322+
323+
recvd = php_stream_xport_recvfrom(stream, read_buf, to_read, flags, NULL, NULL,
324+
zremote ? &Z_STRVAL_P(zremote) : NULL,
325+
zremote ? &Z_STRLEN_P(zremote) : NULL
326+
TSRMLS_CC);
327+
328+
if (recvd >= 0) {
329+
if (zremote && Z_STRLEN_P(zremote)) {
330+
Z_TYPE_P(zremote) = IS_STRING;
331+
}
332+
read_buf[recvd] = '\0';
333+
334+
if (PG(magic_quotes_runtime)) {
335+
Z_TYPE_P(return_value) = IS_STRING;
336+
Z_STRVAL_P(return_value) = php_addslashes(Z_STRVAL_P(return_value),
337+
Z_STRLEN_P(return_value), &Z_STRLEN_P(return_value), 1 TSRMLS_CC);
338+
return;
339+
} else {
340+
RETURN_STRINGL(read_buf, recvd, 0);
341+
}
342+
}
343+
344+
RETURN_FALSE;
345+
}
346+
/* }}} */
347+
269348
/* {{{ proto long stream_get_contents(resource source [, long maxlen ])
270349
Reads all remaining bytes (or up to maxlen bytes) from a stream and returns them as a string. */
271350
PHP_FUNCTION(stream_get_contents)

ext/standard/streamsfuncs.h

+3
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
/* Flags for stream_socket_client */
2222
#define PHP_STREAM_CLIENT_PERSISTENT 1
2323
#define PHP_STREAM_CLIENT_ASYNC_CONNECT 2
24+
#define PHP_STREAM_CLIENT_CONNECT 4
2425

2526
PHP_FUNCTION(stream_socket_client);
2627
PHP_FUNCTION(stream_socket_server);
2728
PHP_FUNCTION(stream_socket_accept);
2829
PHP_FUNCTION(stream_socket_get_name);
30+
PHP_FUNCTION(stream_socket_recvfrom);
31+
PHP_FUNCTION(stream_socket_sendto);
2932

3033
PHP_FUNCTION(stream_copy_to_stream);
3134
PHP_FUNCTION(stream_get_contents);

ext/standard/tests/network/tcp4loop.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
--TEST--
22
Streams Based IPv4 TCP Loopback test
33
--FILE--
4-
<?php
4+
<?php # vim:ft=php:
55
/* Setup socket server */
66
$server = stream_socket_server('tcp://127.0.0.1:31337');
77
if (!$server) {

main/network.c

+91-4
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,94 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po
478478
}
479479
/* }}} */
480480

481-
static void populate_name(
481+
PHPAPI int php_network_parse_network_address_with_port(const char *addr, long addrlen, struct sockaddr *sa, socklen_t *sl TSRMLS_DC)
482+
{
483+
char *colon;
484+
char *host = NULL;
485+
int is_v6;
486+
char *tmp;
487+
int ret = FAILURE;
488+
short port;
489+
struct sockaddr_in *in4 = (struct sockaddr_in*)sa;
490+
struct sockaddr **sal, **psal;
491+
int n;
492+
char *errstr = NULL;
493+
#ifdef HAVE_IPV6
494+
char *p;
495+
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
496+
#endif
497+
498+
if (*addr == '[') {
499+
colon = memchr(addr + 1, ']', addrlen-1);
500+
if (!colon || colon[1] != ':') {
501+
return 0;
502+
}
503+
port = atoi(colon + 2);
504+
addr++;
505+
} else {
506+
colon = memchr(addr, ':', addrlen);
507+
port = atoi(colon + 1);
508+
}
509+
510+
tmp = estrndup(addr, colon - addr);
511+
512+
/* first, try interpreting the address as a numeric address */
513+
514+
#if HAVE_IPV6 && HAVE_INET_PTON
515+
if (inet_pton(AF_INET6, tmp, &in6->sin6_addr) > 0) {
516+
in6->sin6_port = htons(port);
517+
in6->sin6_family = AF_INET6;
518+
*sl = sizeof(struct sockaddr_in6);
519+
ret = SUCCESS;
520+
goto out;
521+
}
522+
#endif
523+
if (inet_aton(tmp, &in4->sin_addr) > 0) {
524+
in4->sin_port = htons(port);
525+
in4->sin_family = AF_INET;
526+
*sl = sizeof(struct sockaddr_in);
527+
ret = SUCCESS;
528+
goto out;
529+
}
530+
531+
/* looks like we'll need to resolve it */
532+
n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr TSRMLS_CC);
533+
534+
if (n == 0) {
535+
if (errstr) {
536+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to resolve `%s': %s", tmp, errstr);
537+
STR_FREE(errstr);
538+
}
539+
goto out;
540+
}
541+
542+
/* copy the details from the first item */
543+
switch ((*psal)->sa_family) {
544+
#if HAVE_GETADDRINFO && HAVE_IPV6
545+
case AF_INET6:
546+
*in6 = **(struct sockaddr_in6**)psal;
547+
in6->sin6_port = htons(port);
548+
*sl = sizeof(struct sockaddr_in6);
549+
ret = SUCCESS;
550+
break;
551+
#endif
552+
case AF_INET:
553+
*in4 = **(struct sockaddr_in**)psal;
554+
in4->sin_port = htons(port);
555+
*sl = sizeof(struct sockaddr_in);
556+
ret = SUCCESS;
557+
break;
558+
}
559+
560+
php_network_freeaddresses(psal);
561+
562+
out:
563+
STR_FREE(tmp);
564+
return ret;
565+
}
566+
567+
568+
PHPAPI void php_network_populate_name_from_sockaddr(
482569
/* input address */
483570
struct sockaddr *sa, socklen_t sl,
484571
/* output readable address */
@@ -556,7 +643,7 @@ PHPAPI int php_network_get_peer_name(php_socket_t sock,
556643
socklen_t sl = sizeof(sa);
557644

558645
if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
559-
populate_name((struct sockaddr*)&sa, sl,
646+
php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
560647
textaddr, textaddrlen,
561648
addr, addrlen
562649
TSRMLS_CC);
@@ -575,7 +662,7 @@ PHPAPI int php_network_get_sock_name(php_socket_t sock,
575662
socklen_t sl = sizeof(sa);
576663

577664
if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
578-
populate_name((struct sockaddr*)&sa, sl,
665+
php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
579666
textaddr, textaddrlen,
580667
addr, addrlen
581668
TSRMLS_CC);
@@ -625,7 +712,7 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
625712
clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
626713

627714
if (clisock >= 0) {
628-
populate_name((struct sockaddr*)&sa, sl,
715+
php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
629716
textaddr, textaddrlen,
630717
addr, addrlen
631718
TSRMLS_CC);

main/php_network.h

+12
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const
173173
/* open a connection to a host using php_hostconnect and return a stream */
174174
PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
175175
int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC);
176+
PHPAPI void php_network_populate_name_from_sockaddr(
177+
/* input address */
178+
struct sockaddr *sa, socklen_t sl,
179+
/* output readable address */
180+
char **textaddr, long *textaddrlen,
181+
/* output address */
182+
struct sockaddr **addr,
183+
socklen_t *addrlen
184+
TSRMLS_DC);
185+
186+
PHPAPI int php_network_parse_network_address_with_port(const char *addr,
187+
long addrlen, struct sockaddr *sa, socklen_t *sl TSRMLS_DC);
176188

177189
#define php_stream_sock_open_from_socket(socket, persistent) _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_CC TSRMLS_CC)
178190
#define php_stream_sock_open_host(host, port, socktype, timeout, persistent) _php_stream_sock_open_host((host), (port), (socktype), (timeout), (persistent) STREAMS_CC TSRMLS_CC)

main/streams/php_stream_transport.h

+24-1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
8888
void **addr, socklen_t *addrlen
8989
TSRMLS_DC);
9090

91+
enum php_stream_xport_send_recv_flags {
92+
STREAM_OOB = 1,
93+
STREAM_PEEK = 2
94+
};
95+
96+
/* Similar to recv() system call; read data from the stream, optionally
97+
* peeking, optionally retrieving OOB data */
98+
PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
99+
long flags, void **addr, socklen_t *addrlen,
100+
char **textaddr, int *textaddrlen TSRMLS_DC);
101+
102+
/* Similar to send() system call; send data to the stream, optionally
103+
* sending it as OOB data */
104+
PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
105+
long flags, void *addr, socklen_t addrlen TSRMLS_DC);
106+
91107
/* Structure definition for the set_option interface that the above functions wrap */
92108

93109
typedef struct _php_stream_xport_param {
@@ -96,7 +112,9 @@ typedef struct _php_stream_xport_param {
96112
STREAM_XPORT_OP_LISTEN, STREAM_XPORT_OP_ACCEPT,
97113
STREAM_XPORT_OP_CONNECT_ASYNC,
98114
STREAM_XPORT_OP_GET_NAME,
99-
STREAM_XPORT_OP_GET_PEER_NAME
115+
STREAM_XPORT_OP_GET_PEER_NAME,
116+
STREAM_XPORT_OP_RECV,
117+
STREAM_XPORT_OP_SEND
100118
} op;
101119
unsigned int want_addr:1;
102120
unsigned int want_textaddr:1;
@@ -107,6 +125,11 @@ typedef struct _php_stream_xport_param {
107125
long namelen;
108126
int backlog;
109127
struct timeval *timeout;
128+
struct sockaddr *addr;
129+
socklen_t addrlen;
130+
char *buf;
131+
size_t buflen;
132+
long flags;
110133
} inputs;
111134
struct {
112135
php_stream *client;

0 commit comments

Comments
 (0)