Coverage Report

Created: 2025-12-31 07:28

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