Skip to content

Commit a51bf0c

Browse files
committed
Add SO_REUSEPORT + SO_BROADCAST support via socket stream context option
1 parent 29eb0ea commit a51bf0c

File tree

6 files changed

+76
-9
lines changed

6 files changed

+76
-9
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ PHP NEWS
2121
- SPL:
2222
. Revert fix for bug #67064 (BC issues). (Bob)
2323

24+
- Streams:
25+
. Expose so_reuseport and so_broadcast capability via socket context option
26+
assignment. (Daniel Lowrey)
27+
2428
- Zlib:
2529
. Fixed bug #67724 (chained zlib filters silently fail with large amounts of
2630
data). (Mike)

UPGRADING

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ PHP 5.6 UPGRADE NOTES
151151
- Non-blocking read/write query behavior now optionally available in database
152152
operations using the ext/pgsql extension.
153153

154+
- Stream socket servers may now use SO_REUSEPORT in systems that support the
155+
option to bind on the same address/port in separate processes via a new
156+
"so_reuseport" socket context option.
157+
158+
- UDP stream servers and clients may now enable SO_BROADCAST via a new
159+
"so_broadcast" socket context option.
160+
154161
========================================
155162
3. Changes in SAPI modules
156163
========================================

ext/ftp/ftp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
136136

137137
ftp->fd = php_network_connect_socket_to_host(host,
138138
(unsigned short) (port ? port : 21), SOCK_STREAM,
139-
0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC);
139+
0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE TSRMLS_CC);
140140
if (ftp->fd == -1) {
141141
goto bail;
142142
}

main/network.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,14 @@ static inline void sub_times(struct timeval a, struct timeval b, struct timeval
416416
* */
417417
/* {{{ php_network_bind_socket_to_local_addr */
418418
php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
419-
int socktype, char **error_string, int *error_code
419+
int socktype, long sockopts, char **error_string, int *error_code
420420
TSRMLS_DC)
421421
{
422422
int num_addrs, n, err = 0;
423423
php_socket_t sock;
424424
struct sockaddr **sal, **psal, *sa;
425425
socklen_t socklen;
426+
int sockoptval = 1;
426427

427428
num_addrs = php_network_getaddresses(host, socktype, &psal, error_string TSRMLS_CC);
428429

@@ -464,9 +465,16 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po
464465
/* attempt to bind */
465466

466467
#ifdef SO_REUSEADDR
467-
{
468-
int val = 1;
469-
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));
468+
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
469+
#endif
470+
#ifdef SO_REUSEPORT
471+
if (sockopts & STREAM_SOCKOP_SO_REUSEPORT) {
472+
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&sockoptval, sizeof(sockoptval));
473+
}
474+
#endif
475+
#ifdef SO_BROADCAST
476+
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
477+
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
470478
}
471479
#endif
472480

@@ -762,7 +770,7 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
762770
/* {{{ php_network_connect_socket_to_host */
763771
php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
764772
int socktype, int asynchronous, struct timeval *timeout, char **error_string,
765-
int *error_code, char *bindto, unsigned short bindport
773+
int *error_code, char *bindto, unsigned short bindport, long sockopts
766774
TSRMLS_DC)
767775
{
768776
int num_addrs, n, fatal = 0;
@@ -864,6 +872,7 @@ php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short
864872
}
865873
}
866874
#endif
875+
867876
if (!local_address || bind(sock, local_address, local_address_len)) {
868877
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
869878
}
@@ -878,6 +887,14 @@ php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short
878887
*error_string = NULL;
879888
}
880889

890+
#ifdef SO_BROADCAST
891+
{
892+
int val = 1;
893+
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
894+
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&val, sizeof(val));
895+
}
896+
}
897+
#endif
881898
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
882899
timeout ? &working_timeout : NULL,
883900
error_string, error_code);

main/php_network.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ typedef int php_socket_t;
105105
# define SOCK_RECV_ERR -1
106106
#endif
107107

108+
#define STREAM_SOCKOP_NONE 1 << 0
109+
#define STREAM_SOCKOP_SO_REUSEPORT 1 << 1
110+
#define STREAM_SOCKOP_SO_BROADCAST 1 << 2
111+
112+
108113
/* uncomment this to debug poll(2) emulation on systems that have poll(2) */
109114
/* #define PHP_USE_POLL_2_EMULATION 1 */
110115

@@ -229,7 +234,7 @@ PHPAPI void php_network_freeaddresses(struct sockaddr **sal);
229234

230235
PHPAPI php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
231236
int socktype, int asynchronous, struct timeval *timeout, char **error_string,
232-
int *error_code, char *bindto, unsigned short bindport
237+
int *error_code, char *bindto, unsigned short bindport, long sockopts
233238
TSRMLS_DC);
234239

235240
PHPAPI int php_network_connect_socket(php_socket_t sockfd,
@@ -244,7 +249,7 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd,
244249
php_network_connect_socket((sock), (addr), (addrlen), 0, (timeout), NULL, NULL)
245250

246251
PHPAPI php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
247-
int socktype, char **error_string, int *error_code
252+
int socktype, long sockopts, char **error_string, int *error_code
248253
TSRMLS_DC);
249254

250255
PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,

main/streams/xp_socket.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *
570570
{
571571
char *host = NULL;
572572
int portno, err;
573+
long sockopts = STREAM_SOCKOP_NONE;
574+
zval **tmpzval = NULL;
573575

574576
#ifdef AF_UNIX
575577
if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
@@ -599,8 +601,28 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *
599601
return -1;
600602
}
601603

604+
#ifdef SO_REUSEPORT
605+
if (stream->context
606+
&& php_stream_context_get_option(stream->context, "socket", "so_reuseport", &tmpzval) == SUCCESS
607+
&& zend_is_true(*tmpzval)
608+
) {
609+
sockopts |= STREAM_SOCKOP_SO_REUSEPORT;
610+
}
611+
#endif
612+
613+
#ifdef SO_BROADCAST
614+
if (&php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
615+
&& stream->context
616+
&& php_stream_context_get_option(stream->context, "socket", "so_broadcast", &tmpzval) == SUCCESS
617+
&& zend_is_true(*tmpzval)
618+
) {
619+
sockopts |= STREAM_SOCKOP_SO_BROADCAST;
620+
}
621+
#endif
622+
602623
sock->socket = php_network_bind_socket_to_local_addr(host, portno,
603624
stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
625+
sockopts,
604626
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
605627
&err
606628
TSRMLS_CC);
@@ -620,6 +642,7 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
620642
int err = 0;
621643
int ret;
622644
zval **tmpzval = NULL;
645+
long sockopts = STREAM_SOCKOP_NONE;
623646

624647
#ifdef AF_UNIX
625648
if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
@@ -665,6 +688,16 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
665688
bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
666689
}
667690

691+
#ifdef SO_BROADCAST
692+
if (&php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
693+
&& stream->context
694+
&& php_stream_context_get_option(stream->context, "socket", "so_broadcast", &tmpzval) == SUCCESS
695+
&& zend_is_true(*tmpzval)
696+
) {
697+
sockopts |= STREAM_SOCKOP_SO_BROADCAST;
698+
}
699+
#endif
700+
668701
/* Note: the test here for php_stream_udp_socket_ops is important, because we
669702
* want the default to be TCP sockets so that the openssl extension can
670703
* re-use this code. */
@@ -676,7 +709,8 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
676709
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
677710
&err,
678711
bindto,
679-
bindport
712+
bindport,
713+
sockopts
680714
TSRMLS_CC);
681715

682716
ret = sock->socket == -1 ? -1 : 0;

0 commit comments

Comments
 (0)