Coverage Report

Created: 2025-12-14 06:05

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