Coverage Report

Created: 2025-11-16 06:23

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