Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/network.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Author: Stig Venaas <venaas@uninett.no>                              |
12
   | Streams work by Wez Furlong <wez@thebrainroom.com>                   |
13
   +----------------------------------------------------------------------+
14
 */
15
16
/*#define DEBUG_MAIN_NETWORK 1*/
17
18
#include "php.h"
19
20
#include <stddef.h>
21
#include <errno.h>
22
23
24
#ifdef PHP_WIN32
25
# include <Ws2tcpip.h>
26
# include "win32/winutil.h"
27
#endif
28
29
#include <sys/types.h>
30
#ifdef HAVE_SYS_SOCKET_H
31
#include <sys/socket.h>
32
#endif
33
34
#ifndef _FCNTL_H
35
#include <fcntl.h>
36
#endif
37
38
#ifdef HAVE_SYS_SELECT_H
39
#include <sys/select.h>
40
#endif
41
#ifdef HAVE_POLL_H
42
#include <poll.h>
43
#elif defined(HAVE_SYS_POLL_H)
44
#include <sys/poll.h>
45
#endif
46
47
48
#ifndef PHP_WIN32
49
#include <netinet/in.h>
50
#include <netdb.h>
51
#ifdef HAVE_ARPA_INET_H
52
#include <arpa/inet.h>
53
#endif
54
#endif
55
56
#include "php_network.h"
57
58
#if defined(PHP_WIN32) || defined(__riscos__)
59
#undef AF_UNIX
60
#endif
61
62
#if defined(AF_UNIX)
63
#include <sys/un.h>
64
#endif
65
66
#include "ext/standard/file.h"
67
68
#ifdef PHP_WIN32
69
# include "win32/time.h"
70
# define SOCK_ERR INVALID_SOCKET
71
# define SOCK_CONN_ERR SOCKET_ERROR
72
# define PHP_TIMEOUT_ERROR_VALUE    WSAETIMEDOUT
73
74
#ifdef HAVE_IPV6
75
const struct in6_addr in6addr_any = {0}; /* IN6ADDR_ANY_INIT; */
76
#endif
77
78
#else
79
0
# define SOCK_ERR -1
80
0
# define SOCK_CONN_ERR -1
81
0
# define PHP_TIMEOUT_ERROR_VALUE    ETIMEDOUT
82
#endif
83
84
#ifdef HAVE_GETADDRINFO
85
# if !defined(PHP_WIN32) && !defined(HAVE_GAI_STRERROR)
86
/* {{{ php_gai_strerror */
87
static const char *php_gai_strerror(int code)
88
{
89
  static struct {
90
    int code;
91
    const char *msg;
92
  } values[] = {
93
#  ifdef EAI_ADDRFAMILY
94
    {EAI_ADDRFAMILY, "Address family for hostname not supported"},
95
#  endif
96
    {EAI_AGAIN, "Temporary failure in name resolution"},
97
    {EAI_BADFLAGS, "Bad value for ai_flags"},
98
    {EAI_FAIL, "Non-recoverable failure in name resolution"},
99
    {EAI_FAMILY, "ai_family not supported"},
100
    {EAI_MEMORY, "Memory allocation failure"},
101
#  ifdef EAI_NODATA
102
    {EAI_NODATA, "No address associated with hostname"},
103
#  endif
104
    {EAI_NONAME, "Name or service not known"},
105
    {EAI_SERVICE, "Servname not supported for ai_socktype"},
106
    {EAI_SOCKTYPE, "ai_socktype not supported"},
107
#  ifdef EAI_SYSTEM
108
    {EAI_SYSTEM, "System error"},
109
#  endif
110
    {0, NULL}
111
  };
112
  int i;
113
114
  for (i = 0; values[i].msg != NULL; i++) {
115
    if (values[i].code == code) {
116
      return (char *)values[i].msg;
117
    }
118
  }
119
120
  return "Unknown error";
121
}
122
/* }}} */
123
# endif
124
#endif
125
126
/* {{{ php_network_freeaddresses */
127
PHPAPI void php_network_freeaddresses(struct sockaddr **sal)
128
0
{
129
0
  struct sockaddr **sap;
130
131
0
  if (sal == NULL)
132
0
    return;
133
0
  for (sap = sal; *sap != NULL; sap++)
134
0
    efree(*sap);
135
0
  efree(sal);
136
0
}
137
/* }}} */
138
139
/* {{{ php_network_getaddresses
140
 * Returns number of addresses, 0 for none/error
141
 */
142
PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string)
143
0
{
144
0
  struct sockaddr **sap;
145
0
  int n;
146
0
#ifdef HAVE_GETADDRINFO
147
0
# ifdef HAVE_IPV6
148
0
  static int ipv6_borked = -1; /* the way this is used *is* thread safe */
149
0
# endif
150
0
  struct addrinfo hints, *res, *sai;
151
#else
152
  struct hostent *host_info;
153
  struct in_addr in;
154
#endif
155
156
0
  if (host == NULL) {
157
0
    return 0;
158
0
  }
159
0
#ifdef HAVE_GETADDRINFO
160
0
  memset(&hints, '\0', sizeof(hints));
161
162
0
  hints.ai_family = AF_INET; /* default to regular inet (see below) */
163
0
  hints.ai_socktype = socktype;
164
165
0
# ifdef HAVE_IPV6
166
  /* probe for a working IPv6 stack; even if detected as having v6 at compile
167
   * time, at runtime some stacks are slow to resolve or have other issues
168
   * if they are not correctly configured.
169
   * static variable use is safe here since simple store or fetch operations
170
   * are atomic and because the actual probe process is not in danger of
171
   * collisions or race conditions. */
172
0
  if (ipv6_borked == -1) {
173
0
    int s;
174
175
0
    s = socket(PF_INET6, SOCK_DGRAM, 0);
176
0
    if (s == SOCK_ERR) {
177
0
      ipv6_borked = 1;
178
0
    } else {
179
0
      ipv6_borked = 0;
180
0
      closesocket(s);
181
0
    }
182
0
  }
183
0
  hints.ai_family = ipv6_borked ? AF_INET : AF_UNSPEC;
184
0
# endif
185
186
0
  if ((n = getaddrinfo(host, NULL, &hints, &res))) {
187
# if defined(PHP_WIN32)
188
    char *gai_error = php_win32_error_to_msg(n);
189
# elif defined(HAVE_GAI_STRERROR)
190
    const char *gai_error = gai_strerror(n);
191
# else
192
    const char *gai_error = php_gai_strerror(n)
193
# endif
194
0
    if (error_string) {
195
      /* free error string received during previous iteration (if any) */
196
0
      if (*error_string) {
197
0
        zend_string_release_ex(*error_string, 0);
198
0
      }
199
0
      *error_string = strpprintf(0, "php_network_getaddresses: getaddrinfo for %s failed: %s", host, gai_error);
200
0
      php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string));
201
0
    } else {
202
0
      php_error_docref(NULL, E_WARNING, "php_network_getaddresses: getaddrinfo for %s failed: %s", host, gai_error);
203
0
    }
204
# ifdef PHP_WIN32
205
    php_win32_error_msg_free(gai_error);
206
# endif
207
0
    return 0;
208
0
  } else if (res == NULL) {
209
0
    if (error_string) {
210
      /* free error string received during previous iteration (if any) */
211
0
      if (*error_string) {
212
0
        zend_string_release_ex(*error_string, 0);
213
0
      }
214
0
      *error_string = strpprintf(0, "php_network_getaddresses: getaddrinfo for %s failed (null result pointer) errno=%d", host, errno);
215
0
      php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string));
216
0
    } else {
217
0
      php_error_docref(NULL, E_WARNING, "php_network_getaddresses: getaddrinfo for %s failed (null result pointer)", host);
218
0
    }
219
0
    return 0;
220
0
  }
221
222
0
  sai = res;
223
0
  for (n = 1; (sai = sai->ai_next) != NULL; n++)
224
0
    ;
225
226
0
  *sal = safe_emalloc((n + 1), sizeof(**sal), 0);
227
0
  sai = res;
228
0
  sap = *sal;
229
230
0
  do {
231
0
    *sap = emalloc(sai->ai_addrlen);
232
0
    memcpy(*sap, sai->ai_addr, sai->ai_addrlen);
233
0
    sap++;
234
0
  } while ((sai = sai->ai_next) != NULL);
235
236
0
  freeaddrinfo(res);
237
#else
238
  if (!inet_pton(AF_INET, host, &in)) {
239
    if(strlen(host) > MAXFQDNLEN) {
240
      host_info = NULL;
241
      errno = E2BIG;
242
    } else {
243
      host_info = php_network_gethostbyname(host);
244
    }
245
    if (host_info == NULL) {
246
      if (error_string) {
247
        /* free error string received during previous iteration (if any) */
248
        if (*error_string) {
249
          zend_string_release_ex(*error_string, 0);
250
        }
251
        *error_string = strpprintf(0, "php_network_getaddresses: gethostbyname failed. errno=%d", errno);
252
        php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string));
253
      } else {
254
        php_error_docref(NULL, E_WARNING, "php_network_getaddresses: gethostbyname failed");
255
      }
256
      return 0;
257
    }
258
    in = *((struct in_addr *) host_info->h_addr);
259
  }
260
261
  *sal = safe_emalloc(2, sizeof(**sal), 0);
262
  sap = *sal;
263
  *sap = emalloc(sizeof(struct sockaddr_in));
264
  (*sap)->sa_family = AF_INET;
265
  ((struct sockaddr_in *)*sap)->sin_addr = in;
266
  sap++;
267
  n = 1;
268
#endif
269
270
0
  *sap = NULL;
271
0
  return n;
272
0
}
273
/* }}} */
274
275
#ifndef O_NONBLOCK
276
#define O_NONBLOCK O_NDELAY
277
#endif
278
279
#ifdef PHP_WIN32
280
typedef u_long php_non_blocking_flags_t;
281
#  define SET_SOCKET_BLOCKING_MODE(sock, save) \
282
  save = TRUE; ioctlsocket(sock, FIONBIO, &save)
283
#  define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
284
  ioctlsocket(sock, FIONBIO, &save)
285
#else
286
typedef int php_non_blocking_flags_t;
287
#  define SET_SOCKET_BLOCKING_MODE(sock, save) \
288
0
   save = fcntl(sock, F_GETFL, 0); \
289
0
   fcntl(sock, F_SETFL, save | O_NONBLOCK)
290
#  define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
291
0
   fcntl(sock, F_SETFL, save)
292
#endif
293
294
#ifdef HAVE_GETTIMEOFDAY
295
/* Subtract times */
296
static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
297
0
{
298
0
  result->tv_usec = a.tv_usec - b.tv_usec;
299
0
  if (result->tv_usec < 0L) {
300
0
    a.tv_sec--;
301
0
    result->tv_usec += 1000000L;
302
0
  }
303
0
  result->tv_sec = a.tv_sec - b.tv_sec;
304
0
  if (result->tv_sec < 0L) {
305
0
    result->tv_sec++;
306
0
    result->tv_usec -= 1000000L;
307
0
  }
308
0
}
309
310
static inline void php_network_set_limit_time(struct timeval *limit_time,
311
    struct timeval *timeout)
312
0
{
313
0
  gettimeofday(limit_time, NULL);
314
0
  const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0;
315
0
  ZEND_ASSERT(limit_time->tv_sec < (timeoutmax - timeout->tv_sec));
316
0
  limit_time->tv_sec += timeout->tv_sec;
317
0
  limit_time->tv_usec += timeout->tv_usec;
318
0
  if (limit_time->tv_usec >= 1000000) {
319
0
    limit_time->tv_usec -= 1000000;
320
0
    limit_time->tv_sec++;
321
0
  }
322
0
}
323
#endif
324
325
/* Connect to a socket using an interruptible connect with optional timeout.
326
 * Optionally, the connect can be made asynchronously, which will implicitly
327
 * enable non-blocking mode on the socket.
328
 * */
329
/* {{{ php_network_connect_socket */
330
PHPAPI int php_network_connect_socket(php_socket_t sockfd,
331
    const struct sockaddr *addr,
332
    socklen_t addrlen,
333
    int asynchronous,
334
    struct timeval *timeout,
335
    zend_string **error_string,
336
    int *error_code)
337
0
{
338
0
  php_non_blocking_flags_t orig_flags;
339
0
  int n;
340
0
  int error = 0;
341
0
  socklen_t len;
342
0
  int ret = 0;
343
344
0
  SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
345
346
0
  if ((n = connect(sockfd, addr, addrlen)) != 0) {
347
0
    error = php_socket_errno();
348
349
0
    if (error_code) {
350
0
      *error_code = error;
351
0
    }
352
353
0
    if (error != EINPROGRESS) {
354
0
      if (error_string) {
355
0
        *error_string = php_socket_error_str(error);
356
0
      }
357
358
0
      return -1;
359
0
    }
360
0
    if (asynchronous && error == EINPROGRESS) {
361
      /* this is fine by us */
362
0
      return 0;
363
0
    }
364
0
  }
365
366
0
  if (n == 0) {
367
0
    goto ok;
368
0
  }
369
# ifdef PHP_WIN32
370
  /* The documentation for connect() says in case of non-blocking connections
371
   * the select function reports success in the writefds set and failure in
372
   * the exceptfds set. Indeed, using PHP_POLLREADABLE results in select
373
   * failing only due to the timeout and not immediately as would be
374
   * expected when a connection is actively refused. This way,
375
   * php_pollfd_for will return a mask with POLLOUT if the connection
376
   * is successful and with POLLPRI otherwise. */
377
  int events = POLLOUT|POLLPRI;
378
#else
379
0
  int events = PHP_POLLREADABLE|POLLOUT;
380
0
#endif
381
0
  struct timeval working_timeout;
382
0
#ifdef HAVE_GETTIMEOFDAY
383
0
  struct timeval limit_time, time_now;
384
0
#endif
385
0
  if (timeout) {
386
0
    memcpy(&working_timeout, timeout, sizeof(working_timeout));
387
0
#ifdef HAVE_GETTIMEOFDAY
388
0
    php_network_set_limit_time(&limit_time, &working_timeout);
389
0
#endif
390
0
  }
391
392
0
  while (true) {
393
0
    n = php_pollfd_for(sockfd, events, timeout ? &working_timeout : NULL);
394
0
    if (n < 0) {
395
0
      if (errno == EINTR) {
396
0
#ifdef HAVE_GETTIMEOFDAY
397
0
        if (timeout) {
398
0
          gettimeofday(&time_now, NULL);
399
400
0
          if (!timercmp(&time_now, &limit_time, <)) {
401
            /* time limit expired; no need for another poll */
402
0
            error = PHP_TIMEOUT_ERROR_VALUE;
403
0
            break;
404
0
          } else {
405
            /* work out remaining time */
406
0
            sub_times(limit_time, time_now, &working_timeout);
407
0
          }
408
0
        }
409
0
#endif
410
0
        continue;
411
0
      }
412
0
      ret = -1;
413
0
    } else if (n == 0) {
414
0
      error = PHP_TIMEOUT_ERROR_VALUE;
415
0
    } else {
416
0
      len = sizeof(error);
417
      /* BSD-derived systems set errno correctly.
418
       * Solaris returns -1 from getsockopt in case of error. */
419
0
      if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) {
420
0
        ret = -1;
421
0
      }
422
0
    }
423
0
    break;
424
0
  }
425
426
0
ok:
427
0
  if (!asynchronous) {
428
    /* back to blocking mode */
429
0
    RESTORE_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
430
0
  }
431
432
0
  if (error_code) {
433
0
    *error_code = error;
434
0
  }
435
436
0
  if (error) {
437
0
    ret = -1;
438
0
    if (error_string) {
439
0
      *error_string = php_socket_error_str(error);
440
0
    }
441
0
  }
442
0
  return ret;
443
0
}
444
/* }}} */
445
446
static void php_network_set_socket_buffers(php_socket_t sock, const php_sockvals *sockvals)
447
0
{
448
0
#ifdef SO_RCVBUF
449
0
  if (sockvals->mask & PHP_SOCKVAL_SO_RCVBUF) {
450
0
    setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&sockvals->rcvbuf, sizeof(sockvals->rcvbuf));
451
0
  }
452
0
#endif
453
0
#ifdef SO_SNDBUF
454
0
  if (sockvals->mask & PHP_SOCKVAL_SO_SNDBUF) {
455
0
    setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&sockvals->sndbuf, sizeof(sockvals->sndbuf));
456
0
  }
457
0
#endif
458
0
}
459
460
/* Bind to a local IP address.
461
 * Returns the bound socket, or -1 on failure.
462
 * */
463
php_socket_t php_network_bind_socket_to_local_addr_ex(const char *host, unsigned port,
464
    int socktype, long sockopts, php_sockvals *sockvals, zend_string **error_string,
465
    int *error_code
466
    )
467
0
{
468
0
  int num_addrs, n, err = 0;
469
0
  php_socket_t sock;
470
0
  struct sockaddr **sal, **psal, *sa;
471
0
  socklen_t socklen;
472
0
  int sockoptval = 1;
473
474
0
  num_addrs = php_network_getaddresses(host, socktype, &psal, error_string);
475
476
0
  if (num_addrs == 0) {
477
    /* could not resolve address(es) */
478
0
    return -1;
479
0
  }
480
481
0
  for (sal = psal; *sal != NULL; sal++) {
482
0
    sa = *sal;
483
484
0
    switch (sa->sa_family) {
485
0
#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
486
0
      case AF_INET6:
487
0
        ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
488
0
        socklen = sizeof(struct sockaddr_in6);
489
0
        break;
490
0
#endif
491
0
      case AF_INET:
492
0
        ((struct sockaddr_in *)sa)->sin_port = htons(port);
493
0
        socklen = sizeof(struct sockaddr_in);
494
0
        break;
495
0
      default:
496
        /* Unsupported family, skip to the next */
497
0
        continue;
498
0
    }
499
500
    /* create a socket for this address */
501
0
    sock = socket(sa->sa_family, socktype, 0);
502
503
0
    if (sock == SOCK_ERR) {
504
0
      continue;
505
0
    }
506
507
    /* attempt to bind */
508
509
0
    if (sockopts & STREAM_SOCKOP_SO_REUSEADDR) {
510
0
      setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
511
0
    }
512
#ifdef PHP_WIN32
513
    else {
514
      setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&sockoptval, sizeof(sockoptval));
515
    }
516
#endif
517
0
#ifdef IPV6_V6ONLY
518
0
    if (sockopts & STREAM_SOCKOP_IPV6_V6ONLY) {
519
0
      int ipv6_val = !!(sockopts & STREAM_SOCKOP_IPV6_V6ONLY_ENABLED);
520
0
      setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6_val, sizeof(sockoptval));
521
0
    }
522
0
#endif
523
0
#ifdef SO_REUSEPORT
524
0
    if (sockopts & STREAM_SOCKOP_SO_REUSEPORT) {
525
# ifdef SO_REUSEPORT_LB
526
      /* Historically, SO_REUSEPORT on FreeBSD predates Linux version, however does not
527
       * involve load balancing grouping thus SO_REUSEPORT_LB is the genuine equivalent.*/
528
      setsockopt(sock, SOL_SOCKET, SO_REUSEPORT_LB, (char*)&sockoptval, sizeof(sockoptval));
529
# else
530
0
      setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&sockoptval, sizeof(sockoptval));
531
0
# endif
532
0
    }
533
0
#endif
534
0
#ifdef SO_BROADCAST
535
0
    if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
536
0
      setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
537
0
    }
538
0
#endif
539
0
#ifdef TCP_NODELAY
540
0
    if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
541
0
      setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
542
0
    }
543
0
#endif
544
0
#ifdef SO_KEEPALIVE
545
0
    if (sockopts & STREAM_SOCKOP_SO_KEEPALIVE) {
546
0
      setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&sockoptval, sizeof(sockoptval));
547
0
    }
548
0
#endif
549
550
    /* Set socket values if provided */
551
0
    if (sockvals != NULL) {
552
0
#ifdef SO_LINGER
553
0
      if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) {
554
        /* l_linger is an unsigned short on Windows, so clamp rather than
555
         * truncate: a truncated value may still be in range and would then
556
         * be applied silently (e.g. 65536 becoming 0, an abortive close). */
557
0
        unsigned short secs = sockvals->linger > USHRT_MAX
558
0
          ? USHRT_MAX : (unsigned short)sockvals->linger;
559
0
        struct linger linger_val = {
560
0
          .l_onoff = (sockvals->linger > 0),
561
0
          .l_linger = sockvals->linger > 0 ? secs : 0
562
0
        };
563
#ifdef SO_LINGER_SEC
564
        setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val));
565
#else
566
0
        setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val));
567
0
#endif
568
0
      }
569
0
#endif
570
0
#if defined(TCP_KEEPIDLE)
571
0
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
572
0
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
573
0
      }
574
#elif defined(TCP_KEEPALIVE)
575
      /* macOS uses TCP_KEEPALIVE instead of TCP_KEEPIDLE */
576
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
577
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
578
      }
579
#endif
580
0
#ifdef TCP_KEEPINTVL
581
0
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPINTVL) {
582
0
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&sockvals->keepalive.keepintvl, sizeof(sockvals->keepalive.keepintvl));
583
0
      }
584
0
#endif
585
0
#ifdef TCP_KEEPCNT
586
0
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPCNT) {
587
0
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&sockvals->keepalive.keepcnt, sizeof(sockvals->keepalive.keepcnt));
588
0
      }
589
0
#endif
590
0
      php_network_set_socket_buffers(sock, sockvals);
591
0
    }
592
593
0
    n = bind(sock, sa, socklen);
594
595
0
    if (n != SOCK_CONN_ERR) {
596
0
      goto bound;
597
0
    }
598
599
0
    err = php_socket_errno();
600
601
0
    closesocket(sock);
602
0
  }
603
0
  sock = -1;
604
605
0
  if (error_code) {
606
0
    *error_code = err;
607
0
  }
608
0
  if (error_string) {
609
0
    *error_string = php_socket_error_str(err);
610
0
  }
611
612
0
bound:
613
614
0
  php_network_freeaddresses(psal);
615
616
0
  return sock;
617
618
0
}
619
620
php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
621
    int socktype, long sockopts, zend_string **error_string, int *error_code
622
    )
623
0
{
624
0
  return php_network_bind_socket_to_local_addr_ex(host, port, socktype, sockopts, NULL, error_string, error_code);
625
0
}
626
627
PHPAPI zend_result php_network_parse_network_address_with_port(const char *addr, size_t addrlen, struct sockaddr *sa, socklen_t *sl)
628
0
{
629
0
  const char *colon;
630
0
  char *tmp;
631
0
  zend_result ret = FAILURE;
632
0
  short port;
633
0
  struct sockaddr_in *in4 = (struct sockaddr_in*)sa;
634
0
  struct sockaddr **psal;
635
0
  int n;
636
0
  zend_string *errstr = NULL;
637
0
#ifdef HAVE_IPV6
638
0
  struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
639
640
0
  memset(in6, 0, sizeof(struct sockaddr_in6));
641
#else
642
  memset(in4, 0, sizeof(struct sockaddr_in));
643
#endif
644
645
0
  if (*addr == '[') {
646
0
    colon = memchr(addr + 1, ']', addrlen-1);
647
0
    if (!colon || colon[1] != ':') {
648
0
      return FAILURE;
649
0
    }
650
0
    port = atoi(colon + 2);
651
0
    addr++;
652
0
  } else {
653
0
    colon = memchr(addr, ':', addrlen);
654
0
    if (!colon) {
655
0
      return FAILURE;
656
0
    }
657
0
    port = atoi(colon + 1);
658
0
  }
659
660
0
  tmp = estrndup(addr, colon - addr);
661
662
  /* first, try interpreting the address as a numeric address */
663
664
0
#ifdef HAVE_IPV6
665
0
  if (inet_pton(AF_INET6, tmp, &in6->sin6_addr) > 0) {
666
0
    in6->sin6_port = htons(port);
667
0
    in6->sin6_family = AF_INET6;
668
0
    *sl = sizeof(struct sockaddr_in6);
669
0
    ret = SUCCESS;
670
0
    goto out;
671
0
  }
672
0
#endif
673
0
  if (inet_pton(AF_INET, tmp, &in4->sin_addr) > 0) {
674
0
    in4->sin_port = htons(port);
675
0
    in4->sin_family = AF_INET;
676
0
    *sl = sizeof(struct sockaddr_in);
677
0
    ret = SUCCESS;
678
0
    goto out;
679
0
  }
680
681
  /* looks like we'll need to resolve it */
682
0
  n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr);
683
684
0
  if (n == 0) {
685
0
    if (errstr) {
686
0
      php_error_docref(NULL, E_WARNING, "Failed to resolve `%s': %s", tmp, ZSTR_VAL(errstr));
687
0
      zend_string_release_ex(errstr, 0);
688
0
    }
689
0
    goto out;
690
0
  }
691
692
  /* copy the details from the first item */
693
0
  switch ((*psal)->sa_family) {
694
0
#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
695
0
    case AF_INET6:
696
0
      *in6 = **(struct sockaddr_in6**)psal;
697
0
      in6->sin6_port = htons(port);
698
0
      *sl = sizeof(struct sockaddr_in6);
699
0
      ret = SUCCESS;
700
0
      break;
701
0
#endif
702
0
    case AF_INET:
703
0
      *in4 = **(struct sockaddr_in**)psal;
704
0
      in4->sin_port = htons(port);
705
0
      *sl = sizeof(struct sockaddr_in);
706
0
      ret = SUCCESS;
707
0
      break;
708
0
  }
709
710
0
  php_network_freeaddresses(psal);
711
712
0
out:
713
0
  efree(tmp);
714
0
  return ret;
715
0
}
716
717
718
PHPAPI void php_network_populate_name_from_sockaddr(
719
    /* input address */
720
    struct sockaddr *sa, socklen_t sl,
721
    /* output readable address */
722
    zend_string **textaddr,
723
    /* output address */
724
    struct sockaddr **addr,
725
    socklen_t *addrlen
726
    )
727
0
{
728
0
  if (addr) {
729
0
    *addr = emalloc(sl);
730
0
    memcpy(*addr, sa, sl);
731
0
    *addrlen = sl;
732
0
  }
733
734
0
  if (textaddr) {
735
0
    char abuf[256];
736
0
    const char *buf = NULL;
737
738
0
    switch (sa->sa_family) {
739
0
      case AF_INET:
740
        /* generally not thread safe, but it *is* thread safe under win32 */
741
0
        buf = inet_ntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr, (char *)&abuf, sizeof(abuf));
742
0
        if (buf) {
743
0
          *textaddr = strpprintf(0, "%s:%d",
744
0
            buf, ntohs(((struct sockaddr_in*)sa)->sin_port));
745
0
        }
746
747
0
        break;
748
749
0
#ifdef HAVE_IPV6
750
0
      case AF_INET6:
751
0
        buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
752
0
        if (buf) {
753
0
          *textaddr = strpprintf(0, "[%s]:%d",
754
0
            buf, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
755
0
        }
756
757
0
        break;
758
0
#endif
759
0
#ifdef AF_UNIX
760
0
      case AF_UNIX:
761
0
        {
762
0
          struct sockaddr_un *ua = (struct sockaddr_un*)sa;
763
764
0
          if (ua->sun_path[0] == '\0') {
765
            /* abstract name */
766
0
            int len = sl - sizeof(sa_family_t);
767
0
            *textaddr = zend_string_init((char*)ua->sun_path, len, 0);
768
0
          } else {
769
0
            int len = strlen(ua->sun_path);
770
0
            *textaddr = zend_string_init((char*)ua->sun_path, len, 0);
771
0
          }
772
0
        }
773
0
        break;
774
0
#endif
775
776
0
    }
777
778
0
  }
779
0
}
780
781
PHPAPI int php_network_get_peer_name(php_socket_t sock,
782
    zend_string **textaddr,
783
    struct sockaddr **addr,
784
    socklen_t *addrlen
785
    )
786
0
{
787
0
  php_sockaddr_storage sa;
788
0
  socklen_t sl = sizeof(sa);
789
0
  memset(&sa, 0, sizeof(sa));
790
791
0
  if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
792
0
    php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
793
0
        textaddr,
794
0
        addr, addrlen
795
0
        );
796
0
    return 0;
797
0
  }
798
0
  return -1;
799
0
}
800
801
PHPAPI int php_network_get_sock_name(php_socket_t sock,
802
    zend_string **textaddr,
803
    struct sockaddr **addr,
804
    socklen_t *addrlen
805
    )
806
0
{
807
0
  php_sockaddr_storage sa;
808
0
  socklen_t sl = sizeof(sa);
809
0
  memset(&sa, 0, sizeof(sa));
810
811
0
  if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
812
0
    php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
813
0
        textaddr,
814
0
        addr, addrlen
815
0
        );
816
0
    return 0;
817
0
  }
818
0
  return -1;
819
820
0
}
821
822
823
/* Accept a client connection from a server socket,
824
 * using an optional timeout.
825
 * Returns the peer address in addr/addrlen (it will emalloc
826
 * these, so be sure to efree the result).
827
 * If you specify textaddr, a text-printable
828
 * version of the address will be emalloc'd and returned.
829
 * */
830
831
 /* Accept a client connection from a server socket,
832
 * using an optional timeout.
833
 * Returns the peer address in addr/addrlen (it will emalloc
834
 * these, so be sure to efree the result).
835
 * If you specify textaddr, a text-printable
836
 * version of the address will be emalloc'd and returned.
837
 * */
838
839
PHPAPI php_socket_t php_network_accept_incoming_ex(php_socket_t srvsock,
840
    zend_string **textaddr,
841
    struct sockaddr **addr,
842
    socklen_t *addrlen,
843
    struct timeval *timeout,
844
    zend_string **error_string,
845
    int *error_code,
846
    php_sockvals *sockvals
847
    )
848
0
{
849
0
  php_socket_t clisock = -1;
850
0
  int error = 0, n;
851
0
  php_sockaddr_storage sa;
852
0
  socklen_t sl;
853
854
0
  n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout);
855
856
0
  if (n == 0) {
857
0
    error = PHP_TIMEOUT_ERROR_VALUE;
858
0
  } else if (n == -1) {
859
0
    error = php_socket_errno();
860
0
  } else {
861
0
    sl = sizeof(sa);
862
863
0
    clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
864
865
0
    if (clisock != SOCK_ERR) {
866
0
      php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
867
0
          textaddr,
868
0
          addr, addrlen
869
0
          );
870
0
#ifdef TCP_NODELAY
871
0
      if (PHP_SOCKVAL_IS_SET(sockvals, PHP_SOCKVAL_TCP_NODELAY)) {
872
0
        int tcp_nodelay = 1;
873
0
        setsockopt(clisock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcp_nodelay, sizeof(tcp_nodelay));
874
0
      }
875
0
#endif
876
#ifdef TCP_KEEPALIVE
877
      /* MacOS does not inherit TCP_KEEPALIVE so it needs to be set */
878
      if (PHP_SOCKVAL_IS_SET(sockvals, PHP_SOCKVAL_TCP_KEEPIDLE)) {
879
        setsockopt(clisock, IPPROTO_TCP, TCP_KEEPALIVE,
880
            (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
881
      }
882
#endif
883
0
    } else {
884
0
      error = php_socket_errno();
885
0
    }
886
0
  }
887
888
0
  if (error_code) {
889
0
    *error_code = error;
890
0
  }
891
0
  if (error_string) {
892
0
    *error_string = php_socket_error_str(error);
893
0
  }
894
895
0
  return clisock;
896
0
}
897
898
PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
899
    zend_string **textaddr,
900
    struct sockaddr **addr,
901
    socklen_t *addrlen,
902
    struct timeval *timeout,
903
    zend_string **error_string,
904
    int *error_code,
905
    int tcp_nodelay
906
    )
907
0
{
908
0
  php_sockvals sockvals = { .mask = tcp_nodelay ? PHP_SOCKVAL_TCP_NODELAY : 0 };
909
910
0
  return php_network_accept_incoming_ex(srvsock, textaddr, addr, addrlen, timeout, error_string,
911
0
      error_code, &sockvals);
912
0
}
913
914
/* Connect to a remote host using an interruptible connect with optional timeout.
915
 * Optionally, the connect can be made asynchronously, which will implicitly
916
 * enable non-blocking mode on the socket.
917
 * Returns the connected (or connecting) socket, or -1 on failure.
918
 * */
919
php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned short port,
920
    int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string,
921
    int *error_code, const char *bindto, unsigned short bindport, long sockopts, php_sockvals *sockvals
922
    )
923
0
{
924
0
  int num_addrs, n, fatal = 0;
925
0
  php_socket_t sock;
926
0
  struct sockaddr **sal, **psal, *sa;
927
0
  struct timeval working_timeout;
928
0
  socklen_t socklen;
929
0
#ifdef HAVE_GETTIMEOFDAY
930
0
  struct timeval limit_time, time_now;
931
0
#endif
932
933
0
  num_addrs = php_network_getaddresses(host, socktype, &psal, error_string);
934
935
0
  if (num_addrs == 0) {
936
    /* could not resolve address(es) */
937
0
    return -1;
938
0
  }
939
940
0
  if (timeout) {
941
0
    memcpy(&working_timeout, timeout, sizeof(working_timeout));
942
0
#ifdef HAVE_GETTIMEOFDAY
943
0
    php_network_set_limit_time(&limit_time, &working_timeout);
944
0
#endif
945
0
  }
946
947
0
  for (sal = psal; !fatal && *sal != NULL; sal++) {
948
0
    sa = *sal;
949
950
0
    switch (sa->sa_family) {
951
0
#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
952
0
      case AF_INET6:
953
0
        if (!bindto || strchr(bindto, ':')) {
954
0
          ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
955
0
          socklen = sizeof(struct sockaddr_in6);
956
0
        } else {
957
          /* Expect IPV4 address, skip to the next */
958
0
          continue;
959
0
        }
960
0
        break;
961
0
#endif
962
0
      case AF_INET:
963
0
        ((struct sockaddr_in *)sa)->sin_port = htons(port);
964
0
        socklen = sizeof(struct sockaddr_in);
965
0
        if (bindto && (strchr(bindto, ':') || !strcmp(bindto, "0"))) {
966
          /* IPV4 sock can not bind to IPV6 address */
967
0
          bindto = NULL;
968
0
        }
969
0
        break;
970
0
      default:
971
        /* Unsupported family, skip to the next */
972
0
        continue;
973
0
    }
974
975
    /* create a socket for this address */
976
0
    sock = socket(sa->sa_family, socktype, 0);
977
978
0
    if (sock == SOCK_ERR) {
979
0
      continue;
980
0
    }
981
982
      /* make a connection attempt */
983
984
0
    if (bindto) {
985
0
      union {
986
0
        struct sockaddr common;
987
0
        struct sockaddr_in in4;
988
0
#ifdef HAVE_IPV6
989
0
        struct sockaddr_in6 in6;
990
0
#endif
991
0
      } local_address = {0};
992
0
      size_t local_address_len = 0;
993
994
0
      if (sa->sa_family == AF_INET) {
995
0
        if (inet_pton(AF_INET, bindto, &local_address.in4.sin_addr) == 1) {
996
0
          local_address_len = sizeof(struct sockaddr_in);
997
0
          local_address.in4.sin_family = sa->sa_family;
998
0
          local_address.in4.sin_port = htons(bindport);
999
0
        }
1000
0
      }
1001
0
#ifdef HAVE_IPV6
1002
0
      else { /* IPV6 */
1003
0
        if (inet_pton(AF_INET6, bindto, &local_address.in6.sin6_addr) == 1) {
1004
0
          local_address_len = sizeof(struct sockaddr_in6);
1005
0
          local_address.in6.sin6_family = sa->sa_family;
1006
0
          local_address.in6.sin6_port = htons(bindport);
1007
0
        }
1008
0
      }
1009
0
#endif
1010
0
#ifdef IP_BIND_ADDRESS_NO_PORT
1011
0
      {
1012
0
        int val = 1;
1013
0
        (void) setsockopt(sock, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &val, sizeof(val));
1014
0
      }
1015
0
#endif
1016
0
      if (local_address_len == 0) {
1017
0
        php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
1018
0
      } else if (bind(sock, &local_address.common, local_address_len)) {
1019
0
        php_error_docref(NULL, E_WARNING, "Failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
1020
0
      }
1021
0
    }
1022
    /* free error string received during previous iteration (if any) */
1023
0
    if (error_string && *error_string) {
1024
0
      zend_string_release_ex(*error_string, 0);
1025
0
      *error_string = NULL;
1026
0
    }
1027
1028
0
#ifdef SO_BROADCAST
1029
0
    {
1030
0
      int val = 1;
1031
0
      if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
1032
0
        setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&val, sizeof(val));
1033
0
      }
1034
0
    }
1035
0
#endif
1036
1037
0
#ifdef TCP_NODELAY
1038
0
    {
1039
0
      int val = 1;
1040
0
      if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
1041
0
        setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
1042
0
      }
1043
0
    }
1044
0
#endif
1045
1046
0
#ifdef SO_KEEPALIVE
1047
0
    {
1048
0
      int val = 1;
1049
0
      if (sockopts & STREAM_SOCKOP_SO_KEEPALIVE) {
1050
0
        setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&val, sizeof(val));
1051
0
      }
1052
0
    }
1053
0
#endif
1054
1055
    /* Set socket values if provided */
1056
0
    if (sockvals != NULL) {
1057
0
#ifdef SO_LINGER
1058
0
      if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) {
1059
        /* l_linger is an unsigned short on Windows, so clamp rather than
1060
         * truncate: a truncated value may still be in range and would then
1061
         * be applied silently (e.g. 65536 becoming 0, an abortive close). */
1062
0
        unsigned short secs = sockvals->linger > USHRT_MAX
1063
0
          ? USHRT_MAX : (unsigned short)sockvals->linger;
1064
0
        struct linger linger_val = {
1065
0
          .l_onoff = (sockvals->linger > 0),
1066
0
          .l_linger = sockvals->linger > 0 ? secs : 0
1067
0
        };
1068
#ifdef SO_LINGER_SEC
1069
        setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val));
1070
#else
1071
0
        setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val));
1072
0
#endif
1073
0
      }
1074
0
#endif
1075
0
#if defined(TCP_KEEPIDLE)
1076
0
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
1077
0
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
1078
0
      }
1079
#elif defined(TCP_KEEPALIVE)
1080
      /* macOS uses TCP_KEEPALIVE instead of TCP_KEEPIDLE */
1081
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
1082
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
1083
      }
1084
#endif
1085
0
#ifdef TCP_KEEPINTVL
1086
0
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPINTVL) {
1087
0
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&sockvals->keepalive.keepintvl, sizeof(sockvals->keepalive.keepintvl));
1088
0
      }
1089
0
#endif
1090
0
#ifdef TCP_KEEPCNT
1091
0
      if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPCNT) {
1092
0
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&sockvals->keepalive.keepcnt, sizeof(sockvals->keepalive.keepcnt));
1093
0
      }
1094
0
#endif
1095
0
      php_network_set_socket_buffers(sock, sockvals);
1096
0
    }
1097
1098
0
    n = php_network_connect_socket(sock, sa, socklen, asynchronous,
1099
0
        timeout ? &working_timeout : NULL,
1100
0
        error_string, error_code);
1101
1102
0
    if (n != -1) {
1103
0
      goto connected;
1104
0
    }
1105
1106
    /* adjust timeout for next attempt */
1107
0
#ifdef HAVE_GETTIMEOFDAY
1108
0
    if (timeout) {
1109
0
      gettimeofday(&time_now, NULL);
1110
1111
0
      if (!timercmp(&time_now, &limit_time, <)) {
1112
        /* time limit expired; don't attempt any further connections */
1113
0
        fatal = 1;
1114
0
      } else {
1115
        /* work out remaining time */
1116
0
        sub_times(limit_time, time_now, &working_timeout);
1117
0
      }
1118
0
    }
1119
#else
1120
    if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
1121
      /* Don't even bother trying to connect to the next alternative;
1122
        * we have no way to determine how long we have already taken
1123
        * and it is quite likely that the next attempt will fail too. */
1124
      fatal = 1;
1125
    } else {
1126
      /* re-use the same initial timeout.
1127
        * Not the best thing, but in practice it should be good-enough */
1128
      if (timeout) {
1129
        memcpy(&working_timeout, timeout, sizeof(working_timeout));
1130
      }
1131
    }
1132
#endif
1133
1134
0
    closesocket(sock);
1135
0
  }
1136
0
  sock = -1;
1137
1138
0
connected:
1139
1140
0
  php_network_freeaddresses(psal);
1141
1142
0
  return sock;
1143
0
}
1144
1145
php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
1146
    int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string,
1147
    int *error_code, const char *bindto, unsigned short bindport, long sockopts
1148
    )
1149
0
{
1150
0
  return php_network_connect_socket_to_host_ex(host, port, socktype, asynchronous, timeout,
1151
0
      error_string, error_code, bindto, bindport, sockopts, NULL);
1152
0
}
1153
1154
/* {{{ php_any_addr
1155
 * Fills any (wildcard) address into php_sockaddr_storage
1156
 */
1157
PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port)
1158
0
{
1159
0
  memset(addr, 0, sizeof(php_sockaddr_storage));
1160
0
  switch (family) {
1161
0
#ifdef HAVE_IPV6
1162
0
  case AF_INET6: {
1163
0
    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr;
1164
0
    sin6->sin6_family = AF_INET6;
1165
0
    sin6->sin6_port = htons(port);
1166
0
    sin6->sin6_addr = in6addr_any;
1167
0
    break;
1168
0
  }
1169
0
#endif
1170
0
  case AF_INET: {
1171
0
    struct sockaddr_in *sin = (struct sockaddr_in *) addr;
1172
0
    sin->sin_family = AF_INET;
1173
0
    sin->sin_port = htons(port);
1174
0
    sin->sin_addr.s_addr = htonl(INADDR_ANY);
1175
0
    break;
1176
0
  }
1177
0
  }
1178
0
}
1179
/* }}} */
1180
1181
/* {{{ php_sockaddr_size
1182
 * Returns the size of struct sockaddr_xx for the family
1183
 */
1184
PHPAPI socklen_t php_sockaddr_size(php_sockaddr_storage *addr)
1185
0
{
1186
0
  switch (((struct sockaddr *)addr)->sa_family) {
1187
0
  case AF_INET:
1188
0
    return sizeof(struct sockaddr_in);
1189
0
#ifdef HAVE_IPV6
1190
0
  case AF_INET6:
1191
0
    return sizeof(struct sockaddr_in6);
1192
0
#endif
1193
0
#ifdef AF_UNIX
1194
0
  case AF_UNIX:
1195
0
    return sizeof(struct sockaddr_un);
1196
0
#endif
1197
0
  default:
1198
0
    return 0;
1199
0
  }
1200
0
}
1201
/* }}} */
1202
1203
#ifdef PHP_WIN32
1204
char *php_socket_strerror_s(long err, char *buf, size_t bufsize)
1205
{
1206
  if (buf == NULL) {
1207
    char ebuf[1024];
1208
    errno_t res = strerror_s(ebuf, sizeof(ebuf), err);
1209
    if (res == 0) {
1210
      buf = estrdup(ebuf);
1211
    } else {
1212
      buf = estrdup("Unknown error");
1213
    }
1214
  } else {
1215
    errno_t res = strerror_s(buf, bufsize, err);
1216
    if (res != 0) {
1217
      strncpy(buf, "Unknown error", bufsize);
1218
      buf[bufsize?(bufsize-1):0] = 0;
1219
    }
1220
  }
1221
  return buf;
1222
}
1223
#endif
1224
1225
/* Given a socket error code, if buf == NULL:
1226
 *   emallocs storage for the error message and returns
1227
 * else
1228
 *   sprintf message into provided buffer and returns buf
1229
 */
1230
/* {{{ php_socket_strerror */
1231
PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
1232
1.32k
{
1233
1.32k
#ifndef PHP_WIN32
1234
1.32k
# ifdef HAVE_STRERROR_R
1235
1.32k
  if (buf == NULL) {
1236
0
    char ebuf[1024];
1237
0
#  ifdef STRERROR_R_CHAR_P
1238
0
    char *errstr = strerror_r(err, ebuf, sizeof(ebuf));
1239
0
    buf = estrdup(errstr);
1240
#  else
1241
    int res = (int) strerror_r(err, ebuf, sizeof(ebuf));
1242
    if (res == 0) {
1243
      buf = estrdup(ebuf);
1244
    } else {
1245
      buf = estrdup("Unknown error");
1246
    }
1247
#  endif
1248
1.32k
  } else {
1249
1.32k
#  ifdef STRERROR_R_CHAR_P
1250
1.32k
    buf = strerror_r(err, buf, bufsize);
1251
#  else
1252
    int res = (int) strerror_r(err, buf, bufsize);
1253
    if (res != 0) {
1254
      strncpy(buf, "Unknown error", bufsize);
1255
      buf[bufsize?(bufsize-1):0] = 0;
1256
    }
1257
#  endif
1258
1.32k
  }
1259
# else
1260
  char *errstr = strerror(err);
1261
  if (buf == NULL) {
1262
    buf = estrdup(errstr);
1263
  } else {
1264
    strncpy(buf, errstr, bufsize);
1265
    buf[bufsize?(bufsize-1):0] = 0;
1266
  }
1267
# endif
1268
#else
1269
  char *sysbuf = php_win32_error_to_msg(err);
1270
  if (!sysbuf[0]) {
1271
    sysbuf = "Unknown Error";
1272
  }
1273
1274
  if (buf == NULL) {
1275
    buf = estrdup(sysbuf);
1276
  } else {
1277
    strncpy(buf, sysbuf, bufsize);
1278
    buf[bufsize?(bufsize-1):0] = 0;
1279
  }
1280
1281
  php_win32_error_msg_free(sysbuf);
1282
#endif
1283
1.32k
  return buf;
1284
1.32k
}
1285
/* }}} */
1286
1287
/* {{{ php_socket_error_str */
1288
PHPAPI zend_string *php_socket_error_str(long err)
1289
0
{
1290
0
#ifndef PHP_WIN32
1291
0
# ifdef HAVE_STRERROR_R
1292
0
  char ebuf[1024];
1293
0
#  ifdef STRERROR_R_CHAR_P
1294
0
  char *errstr = strerror_r(err, ebuf, sizeof(ebuf));
1295
#  else
1296
  const char *errstr;
1297
  int res = (int) strerror_r(err, ebuf, sizeof(ebuf));
1298
  if (res == 0) {
1299
    errstr = ebuf;
1300
  } else {
1301
    errstr = "Unknown error";
1302
  }
1303
#  endif
1304
# else
1305
  char *errstr = strerror(err);
1306
# endif
1307
0
  return zend_string_init(errstr, strlen(errstr), 0);
1308
#else
1309
  zend_string *ret;
1310
1311
  char *sysbuf = php_win32_error_to_msg(err);
1312
  if (!sysbuf[0]) {
1313
    sysbuf = "Unknown Error";
1314
  }
1315
1316
  ret = zend_string_init(sysbuf, strlen(sysbuf), 0);
1317
1318
  php_win32_error_msg_free(sysbuf);
1319
1320
  return ret;
1321
#endif
1322
0
}
1323
/* }}} */
1324
1325
/* deprecated */
1326
PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC)
1327
0
{
1328
0
  php_stream *stream;
1329
0
  php_netstream_data_t *sock;
1330
1331
0
  sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
1332
0
  memset(sock, 0, sizeof(php_netstream_data_t));
1333
1334
0
  sock->is_blocked = true;
1335
0
  sock->timeout.tv_sec = FG(default_socket_timeout);
1336
0
  sock->timeout.tv_usec = 0;
1337
0
  sock->socket = socket;
1338
1339
0
  stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
1340
1341
0
  if (stream == NULL) {
1342
0
    pefree(sock, persistent_id ? 1 : 0);
1343
0
  } else {
1344
0
    stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
1345
0
  }
1346
1347
0
  return stream;
1348
0
}
1349
1350
PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
1351
    int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC)
1352
0
{
1353
0
  char *res;
1354
0
  zend_long reslen;
1355
0
  php_stream *stream;
1356
1357
0
  reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
1358
1359
0
  stream = php_stream_xport_create(res, reslen, REPORT_ERRORS,
1360
0
      STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
1361
1362
0
  efree(res);
1363
1364
0
  return stream;
1365
0
}
1366
1367
PHPAPI zend_result php_set_sock_blocking(php_socket_t socketd, bool block)
1368
0
{
1369
0
  zend_result ret = SUCCESS;
1370
1371
#ifdef PHP_WIN32
1372
  u_long flags;
1373
1374
  /* with ioctlsocket, a non-zero sets nonblocking, a zero sets blocking */
1375
  flags = !block;
1376
  if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) {
1377
    ret = FAILURE;
1378
  }
1379
#else
1380
0
  int myflag = 0;
1381
0
  int flags = fcntl(socketd, F_GETFL);
1382
1383
0
#ifdef O_NONBLOCK
1384
0
  myflag = O_NONBLOCK; /* POSIX version */
1385
#elif defined(O_NDELAY)
1386
  myflag = O_NDELAY;   /* old non-POSIX version */
1387
#endif
1388
0
  if (!block) {
1389
0
    flags |= myflag;
1390
0
  } else {
1391
0
    flags &= ~myflag;
1392
0
  }
1393
0
  if (fcntl(socketd, F_SETFL, flags) == -1) {
1394
0
    ret = FAILURE;
1395
0
  }
1396
0
#endif
1397
0
  return ret;
1398
0
}
1399
1400
PHPAPI void _php_emit_fd_setsize_warning(int max_fd)
1401
0
{
1402
1403
#ifdef PHP_WIN32
1404
  php_error_docref(NULL, E_WARNING,
1405
    "PHP needs to be recompiled with a larger value of FD_SETSIZE.\n"
1406
    "If this binary is from an official www.php.net package, file a bug report\n"
1407
    "at https://github.com/php/php-src/issues, including the following information:\n"
1408
    "FD_SETSIZE=%d, but you are using %d.\n"
1409
    " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
1410
    "to match to maximum number of sockets each script will work with at\n"
1411
    "one time, in order to avoid seeing this error again at a later date.",
1412
    FD_SETSIZE, max_fd, (max_fd + 128) & ~127);
1413
#else
1414
0
  php_error_docref(NULL, E_WARNING,
1415
0
    "You MUST recompile PHP with a larger value of FD_SETSIZE.\n"
1416
0
    "It is set to %d, but you have descriptors numbered at least as high as %d.\n"
1417
0
    " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
1418
0
    "to equal the maximum number of open files supported by your system,\n"
1419
0
    "in order to avoid seeing this error again at a later date.",
1420
0
    FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023);
1421
0
#endif
1422
0
}
1423
1424
#if defined(PHP_USE_POLL_2_EMULATION)
1425
1426
/* emulate poll(2) using select(2), safely. */
1427
1428
PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout)
1429
{
1430
  fd_set rset, wset, eset;
1431
  php_socket_t max_fd = SOCK_ERR; /* effectively unused on Windows */
1432
  unsigned int i;
1433
  int n;
1434
  struct timeval tv;
1435
1436
#ifndef PHP_WIN32
1437
  /* check the highest numbered descriptor */
1438
  for (i = 0; i < nfds; i++) {
1439
    if (ufds[i].fd > max_fd)
1440
      max_fd = ufds[i].fd;
1441
  }
1442
#endif
1443
1444
  if (!PHP_SAFE_MAX_FD(max_fd, nfds + 1)) {
1445
#ifdef PHP_WIN32
1446
    WSASetLastError(WSAEINVAL);
1447
#else
1448
    errno = ERANGE;
1449
#endif
1450
    return -1;
1451
  }
1452
1453
  FD_ZERO(&rset);
1454
  FD_ZERO(&wset);
1455
  FD_ZERO(&eset);
1456
1457
  for (i = 0; i < nfds; i++) {
1458
    if (ufds[i].events & PHP_POLLREADABLE) {
1459
      PHP_SAFE_FD_SET(ufds[i].fd, &rset);
1460
    }
1461
    if (ufds[i].events & POLLOUT) {
1462
      PHP_SAFE_FD_SET(ufds[i].fd, &wset);
1463
    }
1464
    if (ufds[i].events & POLLPRI) {
1465
      PHP_SAFE_FD_SET(ufds[i].fd, &eset);
1466
    }
1467
  }
1468
1469
  if (timeout >= 0) {
1470
    tv.tv_sec = timeout / 1000;
1471
    tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
1472
  }
1473
/* Resetting/initializing */
1474
#ifdef PHP_WIN32
1475
  WSASetLastError(0);
1476
#else
1477
  errno = 0;
1478
#endif
1479
  n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL);
1480
1481
  if (n >= 0) {
1482
    for (i = 0; i < nfds; i++) {
1483
      ufds[i].revents = 0;
1484
1485
      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &rset)) {
1486
        /* could be POLLERR or POLLHUP but can't tell without probing */
1487
        ufds[i].revents |= POLLIN;
1488
      }
1489
      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &wset)) {
1490
        ufds[i].revents |= POLLOUT;
1491
      }
1492
      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &eset)) {
1493
        ufds[i].revents |= POLLPRI;
1494
      }
1495
    }
1496
  }
1497
  return n;
1498
}
1499
#endif
1500
1501
#if defined(HAVE_GETHOSTBYNAME_R)
1502
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_6
1503
static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1504
0
{
1505
0
  struct hostent *hp;
1506
0
  int herr,res;
1507
1508
0
  if (*hstbuflen == 0) {
1509
0
    *hstbuflen = 1024;
1510
0
    *tmphstbuf = (char *)pemalloc(*hstbuflen, true);
1511
0
  }
1512
1513
0
  while (( res =
1514
0
    gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr))
1515
0
    && (errno == ERANGE)) {
1516
    /* Enlarge the buffer. */
1517
0
    *hstbuflen *= 2;
1518
0
    *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true);
1519
0
  }
1520
1521
0
  if (res != 0) {
1522
0
    return NULL;
1523
0
  }
1524
1525
0
  return hp;
1526
0
}
1527
#endif
1528
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_5
1529
static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1530
{
1531
  struct hostent *hp;
1532
  int herr;
1533
1534
  if (*hstbuflen == 0) {
1535
    *hstbuflen = 1024;
1536
    *tmphstbuf = (char *)pemalloc(*hstbuflen, true);
1537
  }
1538
1539
  while ((NULL == ( hp =
1540
    gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&herr)))
1541
    && (errno == ERANGE)) {
1542
    /* Enlarge the buffer. */
1543
    *hstbuflen *= 2;
1544
    *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true);
1545
  }
1546
  return hp;
1547
}
1548
#endif
1549
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_3
1550
static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1551
{
1552
  if (*hstbuflen == 0) {
1553
    *hstbuflen = sizeof(struct hostent_data);
1554
    *tmphstbuf = (char *)pemalloc(*hstbuflen, true);
1555
  } else {
1556
    if (*hstbuflen < sizeof(struct hostent_data)) {
1557
      *hstbuflen = sizeof(struct hostent_data);
1558
      *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true);
1559
    }
1560
  }
1561
  memset((void *)(*tmphstbuf),0,*hstbuflen);
1562
1563
  if (0 != gethostbyname_r(host,hostbuf,(struct hostent_data *)*tmphstbuf)) {
1564
    return NULL;
1565
  }
1566
1567
  return hostbuf;
1568
}
1569
#endif
1570
#endif
1571
1572
0
PHPAPI struct hostent*  php_network_gethostbyname(const char *name) {
1573
#if !defined(HAVE_GETHOSTBYNAME_R)
1574
  return gethostbyname(name);
1575
#else
1576
0
  if (FG(tmp_host_buf)) {
1577
0
    free(FG(tmp_host_buf));
1578
0
  }
1579
1580
0
  FG(tmp_host_buf) = NULL;
1581
0
  FG(tmp_host_buf_len) = 0;
1582
1583
0
  memset(&FG(tmp_host_info), 0, sizeof(struct hostent));
1584
1585
0
  return gethostname_re(name, &FG(tmp_host_info), &FG(tmp_host_buf), &FG(tmp_host_buf_len));
1586
0
#endif
1587
0
}