Coverage Report

Created: 2025-06-13 06:43

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