Coverage Report

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