Coverage Report

Created: 2026-06-02 06:39

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