Skip to content

Commit 6086e17

Browse files
authored
Remove unnecessary sockaddr memory allocation (#7219)
The size of sockaddr_in and sockaddr_in6 is known on compile-time and it is not that big for the stack. Also optimized the code.
1 parent 2bc23cc commit 6086e17

File tree

1 file changed

+112
-130
lines changed

1 file changed

+112
-130
lines changed

main/network.c

Lines changed: 112 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -430,69 +430,64 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po
430430
for (sal = psal; *sal != NULL; sal++) {
431431
sa = *sal;
432432

433-
/* create a socket for this address */
434-
sock = socket(sa->sa_family, socktype, 0);
435-
436-
if (sock == SOCK_ERR) {
437-
continue;
438-
}
439-
440433
switch (sa->sa_family) {
441434
#if HAVE_GETADDRINFO && HAVE_IPV6
442435
case AF_INET6:
443-
((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
444436
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
445437
socklen = sizeof(struct sockaddr_in6);
446438
break;
447439
#endif
448440
case AF_INET:
449-
((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
450441
((struct sockaddr_in *)sa)->sin_port = htons(port);
451442
socklen = sizeof(struct sockaddr_in);
452443
break;
453444
default:
454-
/* Unknown family */
455-
socklen = 0;
456-
sa = NULL;
445+
/* Unsupported family, skip to the next */
446+
continue;
457447
}
458448

459-
if (sa) {
460-
/* attempt to bind */
449+
/* create a socket for this address */
450+
sock = socket(sa->sa_family, socktype, 0);
451+
452+
if (sock == SOCK_ERR) {
453+
continue;
454+
}
455+
456+
/* attempt to bind */
461457

462458
#ifdef SO_REUSEADDR
463-
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
459+
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
464460
#endif
465461
#ifdef IPV6_V6ONLY
466-
if (sockopts & STREAM_SOCKOP_IPV6_V6ONLY) {
467-
int ipv6_val = !!(sockopts & STREAM_SOCKOP_IPV6_V6ONLY_ENABLED);
468-
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6_val, sizeof(sockoptval));
469-
}
462+
if (sockopts & STREAM_SOCKOP_IPV6_V6ONLY) {
463+
int ipv6_val = !!(sockopts & STREAM_SOCKOP_IPV6_V6ONLY_ENABLED);
464+
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6_val, sizeof(sockoptval));
465+
}
470466
#endif
471467
#ifdef SO_REUSEPORT
472-
if (sockopts & STREAM_SOCKOP_SO_REUSEPORT) {
473-
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&sockoptval, sizeof(sockoptval));
474-
}
468+
if (sockopts & STREAM_SOCKOP_SO_REUSEPORT) {
469+
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&sockoptval, sizeof(sockoptval));
470+
}
475471
#endif
476472
#ifdef SO_BROADCAST
477-
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
478-
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
479-
}
473+
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
474+
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
475+
}
480476
#endif
481477
#ifdef TCP_NODELAY
482-
if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
483-
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
484-
}
478+
if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
479+
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
480+
}
485481
#endif
486482

487-
n = bind(sock, sa, socklen);
488-
489-
if (n != SOCK_CONN_ERR) {
490-
goto bound;
491-
}
483+
n = bind(sock, sa, socklen);
492484

493-
err = php_socket_errno();
485+
if (n != SOCK_CONN_ERR) {
486+
goto bound;
494487
}
495488

489+
err = php_socket_errno();
490+
496491
closesocket(sock);
497492
}
498493
sock = -1;
@@ -825,148 +820,135 @@ php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short
825820
for (sal = psal; !fatal && *sal != NULL; sal++) {
826821
sa = *sal;
827822

828-
/* create a socket for this address */
829-
sock = socket(sa->sa_family, socktype, 0);
830-
831-
if (sock == SOCK_ERR) {
832-
continue;
833-
}
834-
835823
switch (sa->sa_family) {
836824
#if HAVE_GETADDRINFO && HAVE_IPV6
837825
case AF_INET6:
838826
if (!bindto || strchr(bindto, ':')) {
839-
((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
840827
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
841828
socklen = sizeof(struct sockaddr_in6);
842829
} else {
843-
socklen = 0;
844-
sa = NULL;
830+
/* Expect IPV4 address, skip to the next */
831+
continue;
845832
}
846833
break;
847834
#endif
848835
case AF_INET:
849-
((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
850836
((struct sockaddr_in *)sa)->sin_port = htons(port);
851837
socklen = sizeof(struct sockaddr_in);
838+
if (bindto && strchr(bindto, ':')) {
839+
/* IPV4 sock can not bind to IPV6 address */
840+
bindto = NULL;
841+
}
852842
break;
853843
default:
854-
/* Unknown family */
855-
socklen = 0;
856-
sa = NULL;
844+
/* Unsupported family, skip to the next */
845+
continue;
857846
}
858847

859-
if (sa) {
860-
/* make a connection attempt */
848+
/* create a socket for this address */
849+
sock = socket(sa->sa_family, socktype, 0);
861850

862-
if (bindto) {
863-
struct sockaddr *local_address = NULL;
864-
int local_address_len = 0;
851+
if (sock == SOCK_ERR) {
852+
continue;
853+
}
865854

866-
if (sa->sa_family == AF_INET) {
867-
if (strchr(bindto,':')) {
868-
goto skip_bind;
869-
}
870-
struct sockaddr_in *in4 = emalloc(sizeof(struct sockaddr_in));
855+
/* make a connection attempt */
871856

872-
local_address = (struct sockaddr*)in4;
873-
local_address_len = sizeof(struct sockaddr_in);
857+
if (bindto) {
858+
union {
859+
struct sockaddr common;
860+
struct sockaddr_in in4;
861+
#if HAVE_IPV6 && HAVE_INET_PTON
862+
struct sockaddr_in6 in6;
863+
#endif
864+
} local_address;
865+
int local_address_len = 0;
874866

875-
in4->sin_family = sa->sa_family;
876-
in4->sin_port = htons(bindport);
867+
if (sa->sa_family == AF_INET) {
877868
#ifdef HAVE_INET_PTON
878-
if (!inet_pton(AF_INET, bindto, &in4->sin_addr)) {
869+
if (inet_pton(AF_INET, bindto, &local_address.in4.sin_addr) == 1) {
879870
#else
880-
if (!inet_aton(bindto, &in4->sin_addr)) {
871+
if (inet_aton(bindto, &local_address.in4.sin_addr)) {
881872
#endif
882-
php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
883-
goto skip_bind;
884-
}
885-
memset(&(in4->sin_zero), 0, sizeof(in4->sin_zero));
873+
local_address_len = sizeof(struct sockaddr_in);
874+
local_address.in4.sin_family = sa->sa_family;
875+
local_address.in4.sin_port = htons(bindport);
876+
memset(&(local_address.in4.sin_zero), 0, sizeof(local_address.in4.sin_zero));
886877
}
878+
}
887879
#if HAVE_IPV6 && HAVE_INET_PTON
888-
else { /* IPV6 */
889-
struct sockaddr_in6 *in6 = emalloc(sizeof(struct sockaddr_in6));
890-
891-
local_address = (struct sockaddr*)in6;
880+
else { /* IPV6 */
881+
if (inet_pton(AF_INET6, bindto, &local_address.in6.sin6_addr) == 1) {
892882
local_address_len = sizeof(struct sockaddr_in6);
893-
894-
in6->sin6_family = sa->sa_family;
895-
in6->sin6_port = htons(bindport);
896-
if (inet_pton(AF_INET6, bindto, &in6->sin6_addr) < 1) {
897-
php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
898-
goto skip_bind;
899-
}
900-
}
901-
#endif
902-
903-
if (!local_address || bind(sock, local_address, local_address_len)) {
904-
php_error_docref(NULL, E_WARNING, "Failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
905-
}
906-
skip_bind:
907-
if (local_address) {
908-
efree(local_address);
883+
local_address.in6.sin6_family = sa->sa_family;
884+
local_address.in6.sin6_port = htons(bindport);
909885
}
910886
}
911-
/* free error string received during previous iteration (if any) */
912-
if (error_string && *error_string) {
913-
zend_string_release_ex(*error_string, 0);
914-
*error_string = NULL;
887+
#endif
888+
if (local_address_len == 0) {
889+
php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
890+
} else if (bind(sock, &local_address.common, local_address_len)) {
891+
php_error_docref(NULL, E_WARNING, "Failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
915892
}
893+
}
894+
/* free error string received during previous iteration (if any) */
895+
if (error_string && *error_string) {
896+
zend_string_release_ex(*error_string, 0);
897+
*error_string = NULL;
898+
}
916899

917900
#ifdef SO_BROADCAST
918-
{
919-
int val = 1;
920-
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
921-
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&val, sizeof(val));
922-
}
901+
{
902+
int val = 1;
903+
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
904+
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&val, sizeof(val));
923905
}
906+
}
924907
#endif
925908

926909
#ifdef TCP_NODELAY
927-
{
928-
int val = 1;
929-
if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
930-
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
931-
}
910+
{
911+
int val = 1;
912+
if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
913+
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
932914
}
915+
}
933916
#endif
934-
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
935-
timeout ? &working_timeout : NULL,
936-
error_string, error_code);
917+
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
918+
timeout ? &working_timeout : NULL,
919+
error_string, error_code);
937920

938-
if (n != -1) {
939-
goto connected;
940-
}
921+
if (n != -1) {
922+
goto connected;
923+
}
941924

942-
/* adjust timeout for next attempt */
925+
/* adjust timeout for next attempt */
943926
#if HAVE_GETTIMEOFDAY
944-
if (timeout) {
945-
gettimeofday(&time_now, NULL);
927+
if (timeout) {
928+
gettimeofday(&time_now, NULL);
946929

947-
if (!timercmp(&time_now, &limit_time, <)) {
948-
/* time limit expired; don't attempt any further connections */
949-
fatal = 1;
950-
} else {
951-
/* work out remaining time */
952-
sub_times(limit_time, time_now, &working_timeout);
953-
}
954-
}
955-
#else
956-
if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
957-
/* Don't even bother trying to connect to the next alternative;
958-
* we have no way to determine how long we have already taken
959-
* and it is quite likely that the next attempt will fail too. */
930+
if (!timercmp(&time_now, &limit_time, <)) {
931+
/* time limit expired; don't attempt any further connections */
960932
fatal = 1;
961933
} else {
962-
/* re-use the same initial timeout.
963-
* Not the best thing, but in practice it should be good-enough */
964-
if (timeout) {
965-
memcpy(&working_timeout, timeout, sizeof(working_timeout));
966-
}
934+
/* work out remaining time */
935+
sub_times(limit_time, time_now, &working_timeout);
936+
}
937+
}
938+
#else
939+
if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
940+
/* Don't even bother trying to connect to the next alternative;
941+
* we have no way to determine how long we have already taken
942+
* and it is quite likely that the next attempt will fail too. */
943+
fatal = 1;
944+
} else {
945+
/* re-use the same initial timeout.
946+
* Not the best thing, but in practice it should be good-enough */
947+
if (timeout) {
948+
memcpy(&working_timeout, timeout, sizeof(working_timeout));
967949
}
968-
#endif
969950
}
951+
#endif
970952

971953
closesocket(sock);
972954
}

0 commit comments

Comments
 (0)