Coverage Report

Created: 2026-10-28 06:20

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
PHPAPI void php_network_apply_sockvals(php_socket_t sock, const php_sockvals *sockvals)
447
0
{
448
0
#ifdef SO_LINGER
449
0
  if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) {
450
0
    unsigned short secs = sockvals->linger > USHRT_MAX
451
0
      ? USHRT_MAX : (unsigned short)sockvals->linger;
452
0
    struct linger linger_val = {
453
0
      .l_onoff = (sockvals->linger > 0),
454
0
      .l_linger = sockvals->linger > 0 ? secs : 0
455
0
    };
456
#ifdef SO_LINGER_SEC
457
    setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val));
458
#else
459
0
    setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val));
460
0
#endif
461
0
  }
462
0
#endif
463
0
#if defined(TCP_KEEPIDLE)
464
0
  if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
465
0
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
466
0
  }
467
#elif defined(TCP_KEEPALIVE)
468
  if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) {
469
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
470
  }
471
#endif
472
0
#ifdef TCP_KEEPINTVL
473
0
  if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPINTVL) {
474
0
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&sockvals->keepalive.keepintvl, sizeof(sockvals->keepalive.keepintvl));
475
0
  }
476
0
#endif
477
0
#ifdef TCP_KEEPCNT
478
0
  if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPCNT) {
479
0
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&sockvals->keepalive.keepcnt, sizeof(sockvals->keepalive.keepcnt));
480
0
  }
481
0
#endif
482
0
#ifdef SO_RCVBUF
483
0
  if (sockvals->mask & PHP_SOCKVAL_SO_RCVBUF) {
484
0
    setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&sockvals->rcvbuf, sizeof(sockvals->rcvbuf));
485
0
  }
486
0
#endif
487
0
#ifdef SO_SNDBUF
488
0
  if (sockvals->mask & PHP_SOCKVAL_SO_SNDBUF) {
489
0
    setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&sockvals->sndbuf, sizeof(sockvals->sndbuf));
490
0
  }
491
0
#endif
492
0
}
493
494
/* Bind to a local IP address.
495
 * Returns the bound socket, or -1 on failure.
496
 * */
497
php_socket_t php_network_bind_socket_to_local_addr_ex(const char *host, unsigned port,
498
    int socktype, long sockopts, php_sockvals *sockvals, zend_string **error_string,
499
    int *error_code
500
    )
501
0
{
502
0
  int num_addrs, n, err = 0;
503
0
  php_socket_t sock;
504
0
  struct sockaddr **sal, **psal, *sa;
505
0
  socklen_t socklen;
506
0
  int sockoptval = 1;
507
508
0
  num_addrs = php_network_getaddresses(host, socktype, &psal, error_string);
509
510
0
  if (num_addrs == 0) {
511
    /* could not resolve address(es) */
512
0
    return -1;
513
0
  }
514
515
0
  for (sal = psal; *sal != NULL; sal++) {
516
0
    sa = *sal;
517
518
0
    switch (sa->sa_family) {
519
0
#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
520
0
      case AF_INET6:
521
0
        ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
522
0
        socklen = sizeof(struct sockaddr_in6);
523
0
        break;
524
0
#endif
525
0
      case AF_INET:
526
0
        ((struct sockaddr_in *)sa)->sin_port = htons(port);
527
0
        socklen = sizeof(struct sockaddr_in);
528
0
        break;
529
0
      default:
530
        /* Unsupported family, skip to the next */
531
0
        continue;
532
0
    }
533
534
    /* create a socket for this address */
535
0
    sock = socket(sa->sa_family, socktype, 0);
536
537
0
    if (sock == SOCK_ERR) {
538
0
      continue;
539
0
    }
540
541
    /* attempt to bind */
542
543
0
    if (sockopts & STREAM_SOCKOP_SO_REUSEADDR) {
544
0
      setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
545
0
    }
546
#ifdef PHP_WIN32
547
    else {
548
      setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&sockoptval, sizeof(sockoptval));
549
    }
550
#endif
551
0
#ifdef IPV6_V6ONLY
552
0
    if (sockopts & STREAM_SOCKOP_IPV6_V6ONLY) {
553
0
      int ipv6_val = !!(sockopts & STREAM_SOCKOP_IPV6_V6ONLY_ENABLED);
554
0
      setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6_val, sizeof(sockoptval));
555
0
    }
556
0
#endif
557
0
#ifdef SO_REUSEPORT
558
0
    if (sockopts & STREAM_SOCKOP_SO_REUSEPORT) {
559
# ifdef SO_REUSEPORT_LB
560
      /* Historically, SO_REUSEPORT on FreeBSD predates Linux version, however does not
561
       * involve load balancing grouping thus SO_REUSEPORT_LB is the genuine equivalent.*/
562
      setsockopt(sock, SOL_SOCKET, SO_REUSEPORT_LB, (char*)&sockoptval, sizeof(sockoptval));
563
# else
564
0
      setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&sockoptval, sizeof(sockoptval));
565
0
# endif
566
0
    }
567
0
#endif
568
0
#ifdef SO_BROADCAST
569
0
    if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
570
0
      setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
571
0
    }
572
0
#endif
573
0
#ifdef TCP_NODELAY
574
0
    if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
575
0
      setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
576
0
    }
577
0
#endif
578
0
#ifdef SO_KEEPALIVE
579
0
    if (sockopts & STREAM_SOCKOP_SO_KEEPALIVE) {
580
0
      setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&sockoptval, sizeof(sockoptval));
581
0
    }
582
0
#endif
583
584
0
    if (sockvals != NULL) {
585
0
      php_network_apply_sockvals(sock, sockvals);
586
0
    }
587
588
0
    n = bind(sock, sa, socklen);
589
590
0
    if (n != SOCK_CONN_ERR) {
591
0
      goto bound;
592
0
    }
593
594
0
    err = php_socket_errno();
595
596
0
    closesocket(sock);
597
0
  }
598
0
  sock = -1;
599
600
0
  if (error_code) {
601
0
    *error_code = err;
602
0
  }
603
0
  if (error_string) {
604
0
    *error_string = php_socket_error_str(err);
605
0
  }
606
607
0
bound:
608
609
0
  php_network_freeaddresses(psal);
610
611
0
  return sock;
612
613
0
}
614
615
php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
616
    int socktype, long sockopts, zend_string **error_string, int *error_code
617
    )
618
0
{
619
0
  return php_network_bind_socket_to_local_addr_ex(host, port, socktype, sockopts, NULL, error_string, error_code);
620
0
}
621
622
PHPAPI zend_result php_network_parse_network_address_with_port(const char *addr, size_t addrlen, struct sockaddr *sa, socklen_t *sl)
623
0
{
624
0
  const char *colon;
625
0
  char *tmp;
626
0
  zend_result ret = FAILURE;
627
0
  zend_long port;
628
0
  struct sockaddr_in *in4 = (struct sockaddr_in*)sa;
629
0
  struct sockaddr **psal;
630
0
  int n;
631
0
  zend_string *errstr = NULL;
632
0
#ifdef HAVE_IPV6
633
0
  struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
634
635
0
  memset(in6, 0, sizeof(struct sockaddr_in6));
636
#else
637
  memset(in4, 0, sizeof(struct sockaddr_in));
638
#endif
639
640
0
  if (*addr == '[') {
641
0
    colon = memchr(addr + 1, ']', addrlen-1);
642
0
    if (!colon || colon[1] != ':') {
643
0
      return FAILURE;
644
0
    }
645
0
    port = ZEND_STRTOL(colon + 2, NULL, 10);
646
0
    addr++;
647
0
  } else {
648
0
    colon = memchr(addr, ':', addrlen);
649
0
    if (!colon) {
650
0
      return FAILURE;
651
0
    }
652
0
    port = ZEND_STRTOL(colon + 1, NULL, 10);
653
0
  }
654
655
0
  if (port < 0 || port > 65535) {
656
0
    return FAILURE;
657
0
  }
658
659
0
  tmp = estrndup(addr, colon - addr);
660
661
  /* first, try interpreting the address as a numeric address */
662
663
0
#ifdef HAVE_IPV6
664
0
  if (inet_pton(AF_INET6, tmp, &in6->sin6_addr) > 0) {
665
0
    in6->sin6_port = htons(port);
666
0
    in6->sin6_family = AF_INET6;
667
0
    *sl = sizeof(struct sockaddr_in6);
668
0
    ret = SUCCESS;
669
0
    goto out;
670
0
  }
671
0
#endif
672
0
  if (inet_pton(AF_INET, tmp, &in4->sin_addr) > 0) {
673
0
    in4->sin_port = htons(port);
674
0
    in4->sin_family = AF_INET;
675
0
    *sl = sizeof(struct sockaddr_in);
676
0
    ret = SUCCESS;
677
0
    goto out;
678
0
  }
679
680
  /* looks like we'll need to resolve it */
681
0
  n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr);
682
683
0
  if (n == 0) {
684
0
    if (errstr) {
685
0
      php_error_docref(NULL, E_WARNING, "Failed to resolve `%s': %s", tmp, ZSTR_VAL(errstr));
686
0
      zend_string_release_ex(errstr, 0);
687
0
    }
688
0
    goto out;
689
0
  }
690
691
  /* copy the details from the first item */
692
0
  switch ((*psal)->sa_family) {
693
0
#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
694
0
    case AF_INET6:
695
0
      *in6 = **(struct sockaddr_in6**)psal;
696
0
      in6->sin6_port = htons(port);
697
0
      *sl = sizeof(struct sockaddr_in6);
698
0
      ret = SUCCESS;
699
0
      break;
700
0
#endif
701
0
    case AF_INET:
702
0
      *in4 = **(struct sockaddr_in**)psal;
703
0
      in4->sin_port = htons(port);
704
0
      *sl = sizeof(struct sockaddr_in);
705
0
      ret = SUCCESS;
706
0
      break;
707
0
  }
708
709
0
  php_network_freeaddresses(psal);
710
711
0
out:
712
0
  efree(tmp);
713
0
  return ret;
714
0
}
715
716
717
PHPAPI void php_network_populate_name_from_sockaddr(
718
    /* input address */
719
    struct sockaddr *sa, socklen_t sl,
720
    /* output readable address */
721
    zend_string **textaddr,
722
    /* output address */
723
    struct sockaddr **addr,
724
    socklen_t *addrlen
725
    )
726
0
{
727
0
  if (addr) {
728
0
    *addr = emalloc(sl);
729
0
    memcpy(*addr, sa, sl);
730
0
    *addrlen = sl;
731
0
  }
732
733
0
  if (textaddr) {
734
0
    char abuf[256];
735
0
    const char *buf = NULL;
736
737
0
    switch (sa->sa_family) {
738
0
      case AF_INET:
739
        /* generally not thread safe, but it *is* thread safe under win32 */
740
0
        buf = inet_ntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr, (char *)&abuf, sizeof(abuf));
741
0
        if (buf) {
742
0
          *textaddr = strpprintf(0, "%s:%d",
743
0
            buf, ntohs(((struct sockaddr_in*)sa)->sin_port));
744
0
        }
745
746
0
        break;
747
748
0
#ifdef HAVE_IPV6
749
0
      case AF_INET6:
750
0
        buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
751
0
        if (buf) {
752
0
          *textaddr = strpprintf(0, "[%s]:%d",
753
0
            buf, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
754
0
        }
755
756
0
        break;
757
0
#endif
758
0
#ifdef AF_UNIX
759
0
      case AF_UNIX:
760
0
        {
761
0
          struct sockaddr_un *ua = (struct sockaddr_un*)sa;
762
763
0
          if (ua->sun_path[0] == '\0') {
764
            /* abstract name */
765
0
            int len = sl - sizeof(sa_family_t);
766
0
            *textaddr = zend_string_init((char*)ua->sun_path, len, 0);
767
0
          } else {
768
0
            int len = strlen(ua->sun_path);
769
0
            *textaddr = zend_string_init((char*)ua->sun_path, len, 0);
770
0
          }
771
0
        }
772
0
        break;
773
0
#endif
774
775
0
    }
776
777
0
  }
778
0
}
779
780
PHPAPI int php_network_get_peer_name(php_socket_t sock,
781
    zend_string **textaddr,
782
    struct sockaddr **addr,
783
    socklen_t *addrlen
784
    )
785
0
{
786
0
  php_sockaddr_storage sa;
787
0
  socklen_t sl = sizeof(sa);
788
0
  memset(&sa, 0, sizeof(sa));
789
790
0
  if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
791
0
    php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
792
0
        textaddr,
793
0
        addr, addrlen
794
0
        );
795
0
    return 0;
796
0
  }
797
0
  return -1;
798
0
}
799
800
PHPAPI int php_network_get_sock_name(php_socket_t sock,
801
    zend_string **textaddr,
802
    struct sockaddr **addr,
803
    socklen_t *addrlen
804
    )
805
0
{
806
0
  php_sockaddr_storage sa;
807
0
  socklen_t sl = sizeof(sa);
808
0
  memset(&sa, 0, sizeof(sa));
809
810
0
  if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
811
0
    php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
812
0
        textaddr,
813
0
        addr, addrlen
814
0
        );
815
0
    return 0;
816
0
  }
817
0
  return -1;
818
819
0
}
820
821
822
/* Accept a client connection from a server socket,
823
 * using an optional timeout.
824
 * Returns the peer address in addr/addrlen (it will emalloc
825
 * these, so be sure to efree the result).
826
 * If you specify textaddr, a text-printable
827
 * version of the address will be emalloc'd and returned.
828
 * */
829
830
 /* Accept a client connection from a server socket,
831
 * using an optional timeout.
832
 * Returns the peer address in addr/addrlen (it will emalloc
833
 * these, so be sure to efree the result).
834
 * If you specify textaddr, a text-printable
835
 * version of the address will be emalloc'd and returned.
836
 * */
837
838
PHPAPI php_socket_t php_network_accept_incoming_ex(php_socket_t srvsock,
839
    zend_string **textaddr,
840
    struct sockaddr **addr,
841
    socklen_t *addrlen,
842
    struct timeval *timeout,
843
    zend_string **error_string,
844
    int *error_code,
845
    php_sockvals *sockvals
846
    )
847
0
{
848
0
  php_socket_t clisock = -1;
849
0
  int error = 0, n;
850
0
  php_sockaddr_storage sa;
851
0
  socklen_t sl;
852
853
0
  n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout);
854
855
0
  if (n == 0) {
856
0
    error = PHP_TIMEOUT_ERROR_VALUE;
857
0
  } else if (n == -1) {
858
0
    error = php_socket_errno();
859
0
  } else {
860
0
    sl = sizeof(sa);
861
862
0
    clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
863
864
0
    if (clisock != SOCK_ERR) {
865
0
      php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
866
0
          textaddr,
867
0
          addr, addrlen
868
0
          );
869
0
#ifdef TCP_NODELAY
870
0
      if (PHP_SOCKVAL_IS_SET(sockvals, PHP_SOCKVAL_TCP_NODELAY)) {
871
0
        int tcp_nodelay = 1;
872
0
        setsockopt(clisock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcp_nodelay, sizeof(tcp_nodelay));
873
0
      }
874
0
#endif
875
#ifdef TCP_KEEPALIVE
876
      /* MacOS does not inherit TCP_KEEPALIVE so it needs to be set */
877
      if (PHP_SOCKVAL_IS_SET(sockvals, PHP_SOCKVAL_TCP_KEEPIDLE)) {
878
        setsockopt(clisock, IPPROTO_TCP, TCP_KEEPALIVE,
879
            (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle));
880
      }
881
#endif
882
0
    } else {
883
0
      error = php_socket_errno();
884
0
    }
885
0
  }
886
887
0
  if (error_code) {
888
0
    *error_code = error;
889
0
  }
890
0
  if (error_string) {
891
0
    *error_string = php_socket_error_str(error);
892
0
  }
893
894
0
  return clisock;
895
0
}
896
897
PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
898
    zend_string **textaddr,
899
    struct sockaddr **addr,
900
    socklen_t *addrlen,
901
    struct timeval *timeout,
902
    zend_string **error_string,
903
    int *error_code,
904
    int tcp_nodelay
905
    )
906
0
{
907
0
  php_sockvals sockvals = { .mask = tcp_nodelay ? PHP_SOCKVAL_TCP_NODELAY : 0 };
908
909
0
  return php_network_accept_incoming_ex(srvsock, textaddr, addr, addrlen, timeout, error_string,
910
0
      error_code, &sockvals);
911
0
}
912
913
/* Connect to a remote host using an interruptible connect with optional timeout.
914
 * Optionally, the connect can be made asynchronously, which will implicitly
915
 * enable non-blocking mode on the socket.
916
 * Returns the connected (or connecting) socket, or -1 on failure.
917
 * */
918
php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned short port,
919
    int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string,
920
    int *error_code, const char *bindto, unsigned short bindport, long sockopts, php_sockvals *sockvals
921
    )
922
0
{
923
0
  int num_addrs, n, fatal = 0;
924
0
  php_socket_t sock;
925
0
  struct sockaddr **sal, **psal, *sa;
926
0
  struct timeval working_timeout;
927
0
  socklen_t socklen;
928
0
#ifdef HAVE_GETTIMEOFDAY
929
0
  struct timeval limit_time, time_now;
930
0
#endif
931
932
0
  num_addrs = php_network_getaddresses(host, socktype, &psal, error_string);
933
934
0
  if (num_addrs == 0) {
935
    /* could not resolve address(es) */
936
0
    return -1;
937
0
  }
938
939
0
  if (timeout) {
940
0
    memcpy(&working_timeout, timeout, sizeof(working_timeout));
941
0
#ifdef HAVE_GETTIMEOFDAY
942
0
    php_network_set_limit_time(&limit_time, &working_timeout);
943
0
#endif
944
0
  }
945
946
0
  for (sal = psal; !fatal && *sal != NULL; sal++) {
947
0
    sa = *sal;
948
949
0
    switch (sa->sa_family) {
950
0
#if defined(HAVE_GETADDRINFO) && defined(HAVE_IPV6)
951
0
      case AF_INET6:
952
0
        if (!bindto || strchr(bindto, ':')) {
953
0
          ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
954
0
          socklen = sizeof(struct sockaddr_in6);
955
0
        } else {
956
          /* Expect IPV4 address, skip to the next */
957
0
          continue;
958
0
        }
959
0
        break;
960
0
#endif
961
0
      case AF_INET:
962
0
        ((struct sockaddr_in *)sa)->sin_port = htons(port);
963
0
        socklen = sizeof(struct sockaddr_in);
964
0
        if (bindto && (strchr(bindto, ':') || !strcmp(bindto, "0"))) {
965
          /* IPV4 sock can not bind to IPV6 address */
966
0
          bindto = NULL;
967
0
        }
968
0
        break;
969
0
      default:
970
        /* Unsupported family, skip to the next */
971
0
        continue;
972
0
    }
973
974
    /* create a socket for this address */
975
0
    sock = socket(sa->sa_family, socktype, 0);
976
977
0
    if (sock == SOCK_ERR) {
978
0
      continue;
979
0
    }
980
981
      /* make a connection attempt */
982
983
0
    if (bindto) {
984
0
      union {
985
0
        struct sockaddr common;
986
0
        struct sockaddr_in in4;
987
0
#ifdef HAVE_IPV6
988
0
        struct sockaddr_in6 in6;
989
0
#endif
990
0
      } local_address = {0};
991
0
      size_t local_address_len = 0;
992
993
0
      if (sa->sa_family == AF_INET) {
994
0
        if (inet_pton(AF_INET, bindto, &local_address.in4.sin_addr) == 1) {
995
0
          local_address_len = sizeof(struct sockaddr_in);
996
0
          local_address.in4.sin_family = sa->sa_family;
997
0
          local_address.in4.sin_port = htons(bindport);
998
0
        }
999
0
      }
1000
0
#ifdef HAVE_IPV6
1001
0
      else { /* IPV6 */
1002
0
        if (inet_pton(AF_INET6, bindto, &local_address.in6.sin6_addr) == 1) {
1003
0
          local_address_len = sizeof(struct sockaddr_in6);
1004
0
          local_address.in6.sin6_family = sa->sa_family;
1005
0
          local_address.in6.sin6_port = htons(bindport);
1006
0
        }
1007
0
      }
1008
0
#endif
1009
0
#ifdef IP_BIND_ADDRESS_NO_PORT
1010
0
      {
1011
0
        int val = 1;
1012
0
        (void) setsockopt(sock, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &val, sizeof(val));
1013
0
      }
1014
0
#endif
1015
0
      if (local_address_len == 0) {
1016
0
        php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
1017
0
      } else if (bind(sock, &local_address.common, local_address_len)) {
1018
0
        php_error_docref(NULL, E_WARNING, "Failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
1019
0
      }
1020
0
    }
1021
    /* free error string received during previous iteration (if any) */
1022
0
    if (error_string && *error_string) {
1023
0
      zend_string_release_ex(*error_string, 0);
1024
0
      *error_string = NULL;
1025
0
    }
1026
1027
0
#ifdef SO_BROADCAST
1028
0
    {
1029
0
      int val = 1;
1030
0
      if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
1031
0
        setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&val, sizeof(val));
1032
0
      }
1033
0
    }
1034
0
#endif
1035
1036
0
#ifdef TCP_NODELAY
1037
0
    {
1038
0
      int val = 1;
1039
0
      if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
1040
0
        setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
1041
0
      }
1042
0
    }
1043
0
#endif
1044
1045
0
#ifdef SO_KEEPALIVE
1046
0
    {
1047
0
      int val = 1;
1048
0
      if (sockopts & STREAM_SOCKOP_SO_KEEPALIVE) {
1049
0
        setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&val, sizeof(val));
1050
0
      }
1051
0
    }
1052
0
#endif
1053
1054
0
    if (sockvals != NULL) {
1055
0
      php_network_apply_sockvals(sock, sockvals);
1056
0
    }
1057
1058
0
    n = php_network_connect_socket(sock, sa, socklen, asynchronous,
1059
0
        timeout ? &working_timeout : NULL,
1060
0
        error_string, error_code);
1061
1062
0
    if (n != -1) {
1063
0
      goto connected;
1064
0
    }
1065
1066
    /* adjust timeout for next attempt */
1067
0
#ifdef HAVE_GETTIMEOFDAY
1068
0
    if (timeout) {
1069
0
      gettimeofday(&time_now, NULL);
1070
1071
0
      if (!timercmp(&time_now, &limit_time, <)) {
1072
        /* time limit expired; don't attempt any further connections */
1073
0
        fatal = 1;
1074
0
      } else {
1075
        /* work out remaining time */
1076
0
        sub_times(limit_time, time_now, &working_timeout);
1077
0
      }
1078
0
    }
1079
#else
1080
    if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
1081
      /* Don't even bother trying to connect to the next alternative;
1082
        * we have no way to determine how long we have already taken
1083
        * and it is quite likely that the next attempt will fail too. */
1084
      fatal = 1;
1085
    } else {
1086
      /* re-use the same initial timeout.
1087
        * Not the best thing, but in practice it should be good-enough */
1088
      if (timeout) {
1089
        memcpy(&working_timeout, timeout, sizeof(working_timeout));
1090
      }
1091
    }
1092
#endif
1093
1094
0
    closesocket(sock);
1095
0
  }
1096
0
  sock = -1;
1097
1098
0
connected:
1099
1100
0
  php_network_freeaddresses(psal);
1101
1102
0
  return sock;
1103
0
}
1104
1105
php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
1106
    int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string,
1107
    int *error_code, const char *bindto, unsigned short bindport, long sockopts
1108
    )
1109
0
{
1110
0
  return php_network_connect_socket_to_host_ex(host, port, socktype, asynchronous, timeout,
1111
0
      error_string, error_code, bindto, bindport, sockopts, NULL);
1112
0
}
1113
1114
/* {{{ php_any_addr
1115
 * Fills any (wildcard) address into php_sockaddr_storage
1116
 */
1117
PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port)
1118
0
{
1119
0
  memset(addr, 0, sizeof(php_sockaddr_storage));
1120
0
  switch (family) {
1121
0
#ifdef HAVE_IPV6
1122
0
  case AF_INET6: {
1123
0
    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr;
1124
0
    sin6->sin6_family = AF_INET6;
1125
0
    sin6->sin6_port = htons(port);
1126
0
    sin6->sin6_addr = in6addr_any;
1127
0
    break;
1128
0
  }
1129
0
#endif
1130
0
  case AF_INET: {
1131
0
    struct sockaddr_in *sin = (struct sockaddr_in *) addr;
1132
0
    sin->sin_family = AF_INET;
1133
0
    sin->sin_port = htons(port);
1134
0
    sin->sin_addr.s_addr = htonl(INADDR_ANY);
1135
0
    break;
1136
0
  }
1137
0
  }
1138
0
}
1139
/* }}} */
1140
1141
/* {{{ php_sockaddr_size
1142
 * Returns the size of struct sockaddr_xx for the family
1143
 */
1144
PHPAPI socklen_t php_sockaddr_size(php_sockaddr_storage *addr)
1145
0
{
1146
0
  switch (((struct sockaddr *)addr)->sa_family) {
1147
0
  case AF_INET:
1148
0
    return sizeof(struct sockaddr_in);
1149
0
#ifdef HAVE_IPV6
1150
0
  case AF_INET6:
1151
0
    return sizeof(struct sockaddr_in6);
1152
0
#endif
1153
0
#ifdef AF_UNIX
1154
0
  case AF_UNIX:
1155
0
    return sizeof(struct sockaddr_un);
1156
0
#endif
1157
0
  default:
1158
0
    return 0;
1159
0
  }
1160
0
}
1161
/* }}} */
1162
1163
#ifdef PHP_WIN32
1164
char *php_socket_strerror_s(long err, char *buf, size_t bufsize)
1165
{
1166
  if (buf == NULL) {
1167
    char ebuf[1024];
1168
    errno_t res = strerror_s(ebuf, sizeof(ebuf), err);
1169
    if (res == 0) {
1170
      buf = estrdup(ebuf);
1171
    } else {
1172
      buf = estrdup("Unknown error");
1173
    }
1174
  } else {
1175
    errno_t res = strerror_s(buf, bufsize, err);
1176
    if (res != 0) {
1177
      strncpy(buf, "Unknown error", bufsize);
1178
      buf[bufsize?(bufsize-1):0] = 0;
1179
    }
1180
  }
1181
  return buf;
1182
}
1183
#endif
1184
1185
/* Given a socket error code, if buf == NULL:
1186
 *   emallocs storage for the error message and returns
1187
 * else
1188
 *   sprintf message into provided buffer and returns buf
1189
 */
1190
/* {{{ php_socket_strerror */
1191
PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
1192
289
{
1193
289
#ifndef PHP_WIN32
1194
289
# ifdef HAVE_STRERROR_R
1195
289
  if (buf == NULL) {
1196
0
    char ebuf[1024];
1197
0
#  ifdef STRERROR_R_CHAR_P
1198
0
    char *errstr = strerror_r(err, ebuf, sizeof(ebuf));
1199
0
    buf = estrdup(errstr);
1200
#  else
1201
    int res = (int) strerror_r(err, ebuf, sizeof(ebuf));
1202
    if (res == 0) {
1203
      buf = estrdup(ebuf);
1204
    } else {
1205
      buf = estrdup("Unknown error");
1206
    }
1207
#  endif
1208
289
  } else {
1209
289
#  ifdef STRERROR_R_CHAR_P
1210
289
    buf = strerror_r(err, buf, bufsize);
1211
#  else
1212
    int res = (int) strerror_r(err, buf, bufsize);
1213
    if (res != 0) {
1214
      strncpy(buf, "Unknown error", bufsize);
1215
      buf[bufsize?(bufsize-1):0] = 0;
1216
    }
1217
#  endif
1218
289
  }
1219
# else
1220
  char *errstr = strerror(err);
1221
  if (buf == NULL) {
1222
    buf = estrdup(errstr);
1223
  } else {
1224
    strncpy(buf, errstr, bufsize);
1225
    buf[bufsize?(bufsize-1):0] = 0;
1226
  }
1227
# endif
1228
#else
1229
  char *sysbuf = php_win32_error_to_msg(err);
1230
  if (!sysbuf[0]) {
1231
    sysbuf = "Unknown Error";
1232
  }
1233
1234
  if (buf == NULL) {
1235
    buf = estrdup(sysbuf);
1236
  } else {
1237
    strncpy(buf, sysbuf, bufsize);
1238
    buf[bufsize?(bufsize-1):0] = 0;
1239
  }
1240
1241
  php_win32_error_msg_free(sysbuf);
1242
#endif
1243
289
  return buf;
1244
289
}
1245
/* }}} */
1246
1247
/* {{{ php_socket_error_str */
1248
PHPAPI zend_string *php_socket_error_str(long err)
1249
0
{
1250
0
#ifndef PHP_WIN32
1251
0
# ifdef HAVE_STRERROR_R
1252
0
  char ebuf[1024];
1253
0
#  ifdef STRERROR_R_CHAR_P
1254
0
  char *errstr = strerror_r(err, ebuf, sizeof(ebuf));
1255
#  else
1256
  const char *errstr;
1257
  int res = (int) strerror_r(err, ebuf, sizeof(ebuf));
1258
  if (res == 0) {
1259
    errstr = ebuf;
1260
  } else {
1261
    errstr = "Unknown error";
1262
  }
1263
#  endif
1264
# else
1265
  char *errstr = strerror(err);
1266
# endif
1267
0
  return zend_string_init(errstr, strlen(errstr), 0);
1268
#else
1269
  zend_string *ret;
1270
1271
  char *sysbuf = php_win32_error_to_msg(err);
1272
  if (!sysbuf[0]) {
1273
    sysbuf = "Unknown Error";
1274
  }
1275
1276
  ret = zend_string_init(sysbuf, strlen(sysbuf), 0);
1277
1278
  php_win32_error_msg_free(sysbuf);
1279
1280
  return ret;
1281
#endif
1282
0
}
1283
/* }}} */
1284
1285
/* deprecated */
1286
PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC)
1287
0
{
1288
0
  php_stream *stream;
1289
0
  php_netstream_data_t *sock;
1290
1291
0
  sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
1292
0
  memset(sock, 0, sizeof(php_netstream_data_t));
1293
1294
0
  sock->is_blocked = true;
1295
0
  sock->timeout.tv_sec = FG(default_socket_timeout);
1296
0
  sock->timeout.tv_usec = 0;
1297
0
  sock->socket = socket;
1298
1299
0
  stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
1300
1301
0
  if (stream == NULL) {
1302
0
    pefree(sock, persistent_id ? 1 : 0);
1303
0
  } else {
1304
0
    stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
1305
0
  }
1306
1307
0
  return stream;
1308
0
}
1309
1310
PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
1311
    int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC)
1312
0
{
1313
0
  char *res;
1314
0
  zend_long reslen;
1315
0
  php_stream *stream;
1316
1317
0
  reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
1318
1319
0
  stream = php_stream_xport_create(res, reslen, REPORT_ERRORS,
1320
0
      STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
1321
1322
0
  efree(res);
1323
1324
0
  return stream;
1325
0
}
1326
1327
PHPAPI zend_result php_set_sock_blocking(php_socket_t socketd, bool block)
1328
0
{
1329
0
  zend_result ret = SUCCESS;
1330
1331
#ifdef PHP_WIN32
1332
  u_long flags;
1333
1334
  /* with ioctlsocket, a non-zero sets nonblocking, a zero sets blocking */
1335
  flags = !block;
1336
  if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) {
1337
    ret = FAILURE;
1338
  }
1339
#else
1340
0
  int myflag = 0;
1341
0
  int flags = fcntl(socketd, F_GETFL);
1342
1343
0
#ifdef O_NONBLOCK
1344
0
  myflag = O_NONBLOCK; /* POSIX version */
1345
#elif defined(O_NDELAY)
1346
  myflag = O_NDELAY;   /* old non-POSIX version */
1347
#endif
1348
0
  if (!block) {
1349
0
    flags |= myflag;
1350
0
  } else {
1351
0
    flags &= ~myflag;
1352
0
  }
1353
0
  if (fcntl(socketd, F_SETFL, flags) == -1) {
1354
0
    ret = FAILURE;
1355
0
  }
1356
0
#endif
1357
0
  return ret;
1358
0
}
1359
1360
PHPAPI void _php_emit_fd_setsize_warning(int max_fd)
1361
0
{
1362
1363
#ifdef PHP_WIN32
1364
  php_error_docref(NULL, E_WARNING,
1365
    "PHP needs to be recompiled with a larger value of FD_SETSIZE.\n"
1366
    "If this binary is from an official www.php.net package, file a bug report\n"
1367
    "at https://github.com/php/php-src/issues, including the following information:\n"
1368
    "FD_SETSIZE=%d, but you are using %d.\n"
1369
    " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
1370
    "to match to maximum number of sockets each script will work with at\n"
1371
    "one time, in order to avoid seeing this error again at a later date.",
1372
    FD_SETSIZE, max_fd, (max_fd + 128) & ~127);
1373
#else
1374
0
  php_error_docref(NULL, E_WARNING,
1375
0
    "You MUST recompile PHP with a larger value of FD_SETSIZE.\n"
1376
0
    "It is set to %d, but you have descriptors numbered at least as high as %d.\n"
1377
0
    " --enable-fd-setsize=%d is recommended, but you may want to set it\n"
1378
0
    "to equal the maximum number of open files supported by your system,\n"
1379
0
    "in order to avoid seeing this error again at a later date.",
1380
0
    FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023);
1381
0
#endif
1382
0
}
1383
1384
#if defined(PHP_USE_POLL_2_EMULATION)
1385
1386
/* emulate poll(2) using select(2), safely. */
1387
1388
PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout)
1389
{
1390
  fd_set rset, wset, eset;
1391
  php_socket_t max_fd = SOCK_ERR; /* effectively unused on Windows */
1392
  unsigned int i;
1393
  int n;
1394
  struct timeval tv;
1395
1396
#ifndef PHP_WIN32
1397
  /* check the highest numbered descriptor */
1398
  for (i = 0; i < nfds; i++) {
1399
    if (ufds[i].fd > max_fd)
1400
      max_fd = ufds[i].fd;
1401
  }
1402
#endif
1403
1404
  if (!PHP_SAFE_MAX_FD(max_fd, nfds + 1)) {
1405
#ifdef PHP_WIN32
1406
    WSASetLastError(WSAEINVAL);
1407
#else
1408
    errno = ERANGE;
1409
#endif
1410
    return -1;
1411
  }
1412
1413
  FD_ZERO(&rset);
1414
  FD_ZERO(&wset);
1415
  FD_ZERO(&eset);
1416
1417
  for (i = 0; i < nfds; i++) {
1418
    if (ufds[i].events & PHP_POLLREADABLE) {
1419
      PHP_SAFE_FD_SET(ufds[i].fd, &rset);
1420
    }
1421
    if (ufds[i].events & POLLOUT) {
1422
      PHP_SAFE_FD_SET(ufds[i].fd, &wset);
1423
    }
1424
    if (ufds[i].events & POLLPRI) {
1425
      PHP_SAFE_FD_SET(ufds[i].fd, &eset);
1426
    }
1427
  }
1428
1429
  if (timeout >= 0) {
1430
    tv.tv_sec = timeout / 1000;
1431
    tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
1432
  }
1433
/* Resetting/initializing */
1434
#ifdef PHP_WIN32
1435
  WSASetLastError(0);
1436
#else
1437
  errno = 0;
1438
#endif
1439
  n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL);
1440
1441
  if (n >= 0) {
1442
    for (i = 0; i < nfds; i++) {
1443
      ufds[i].revents = 0;
1444
1445
      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &rset)) {
1446
        /* could be POLLERR or POLLHUP but can't tell without probing */
1447
        ufds[i].revents |= POLLIN;
1448
      }
1449
      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &wset)) {
1450
        ufds[i].revents |= POLLOUT;
1451
      }
1452
      if (PHP_SAFE_FD_ISSET(ufds[i].fd, &eset)) {
1453
        ufds[i].revents |= POLLPRI;
1454
      }
1455
    }
1456
  }
1457
  return n;
1458
}
1459
#endif
1460
1461
#if defined(HAVE_GETHOSTBYNAME_R)
1462
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_6
1463
static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1464
0
{
1465
0
  struct hostent *hp;
1466
0
  int herr,res;
1467
1468
0
  if (*hstbuflen == 0) {
1469
0
    *hstbuflen = 1024;
1470
0
    *tmphstbuf = (char *)pemalloc(*hstbuflen, true);
1471
0
  }
1472
1473
0
  while (( res =
1474
0
    gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr))
1475
0
    && (errno == ERANGE)) {
1476
    /* Enlarge the buffer. */
1477
0
    *hstbuflen *= 2;
1478
0
    *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true);
1479
0
  }
1480
1481
0
  if (res != 0) {
1482
0
    return NULL;
1483
0
  }
1484
1485
0
  return hp;
1486
0
}
1487
#endif
1488
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_5
1489
static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1490
{
1491
  struct hostent *hp;
1492
  int herr;
1493
1494
  if (*hstbuflen == 0) {
1495
    *hstbuflen = 1024;
1496
    *tmphstbuf = (char *)pemalloc(*hstbuflen, true);
1497
  }
1498
1499
  while ((NULL == ( hp =
1500
    gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&herr)))
1501
    && (errno == ERANGE)) {
1502
    /* Enlarge the buffer. */
1503
    *hstbuflen *= 2;
1504
    *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true);
1505
  }
1506
  return hp;
1507
}
1508
#endif
1509
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_3
1510
static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
1511
{
1512
  if (*hstbuflen == 0) {
1513
    *hstbuflen = sizeof(struct hostent_data);
1514
    *tmphstbuf = (char *)pemalloc(*hstbuflen, true);
1515
  } else {
1516
    if (*hstbuflen < sizeof(struct hostent_data)) {
1517
      *hstbuflen = sizeof(struct hostent_data);
1518
      *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true);
1519
    }
1520
  }
1521
  memset((void *)(*tmphstbuf),0,*hstbuflen);
1522
1523
  if (0 != gethostbyname_r(host,hostbuf,(struct hostent_data *)*tmphstbuf)) {
1524
    return NULL;
1525
  }
1526
1527
  return hostbuf;
1528
}
1529
#endif
1530
#endif
1531
1532
0
PHPAPI struct hostent*  php_network_gethostbyname(const char *name) {
1533
#if !defined(HAVE_GETHOSTBYNAME_R)
1534
  return gethostbyname(name);
1535
#else
1536
0
  if (FG(tmp_host_buf)) {
1537
0
    free(FG(tmp_host_buf));
1538
0
  }
1539
1540
0
  FG(tmp_host_buf) = NULL;
1541
0
  FG(tmp_host_buf_len) = 0;
1542
1543
0
  memset(&FG(tmp_host_info), 0, sizeof(struct hostent));
1544
1545
0
  return gethostname_re(name, &FG(tmp_host_info), &FG(tmp_host_buf), &FG(tmp_host_buf_len));
1546
0
#endif
1547
0
}