Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/streams/xp_socket.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: Wez Furlong <wez@thebrainroom.com>                           |
12
  +----------------------------------------------------------------------+
13
*/
14
15
#include "php.h"
16
#include "ext/standard/file.h"
17
#include "php_streams.h"
18
#include "php_io.h"
19
20
#if defined(PHP_WIN32) || defined(__riscos__)
21
# undef AF_UNIX
22
#endif
23
24
#ifdef AF_UNIX
25
#include <sys/un.h>
26
#endif
27
28
#ifndef MSG_DONTWAIT
29
# define MSG_DONTWAIT 0
30
#endif
31
32
#ifndef MSG_PEEK
33
# define MSG_PEEK 0
34
#endif
35
36
#ifdef PHP_WIN32
37
/* send/recv family on windows expects int */
38
# define XP_SOCK_BUF_SIZE(sz) (((sz) > INT_MAX) ? INT_MAX : (int)(sz))
39
#else
40
0
# define XP_SOCK_BUF_SIZE(sz) (sz)
41
#endif
42
43
const php_stream_ops php_stream_generic_socket_ops;
44
PHPAPI const php_stream_ops php_stream_socket_ops;
45
static const php_stream_ops php_stream_udp_socket_ops;
46
#ifdef AF_UNIX
47
static const php_stream_ops php_stream_unix_socket_ops;
48
static const php_stream_ops php_stream_unixdg_socket_ops;
49
50
0
#define PHP_STREAM_XPORT_IS_UNIX_DG(stream) php_stream_is(stream, &php_stream_unixdg_socket_ops)
51
0
#define PHP_STREAM_XPORT_IS_UNIX_ST(stream) php_stream_is(stream, &php_stream_unix_socket_ops)
52
#define PHP_STREAM_XPORT_IS_UNIX(stream) \
53
0
  (PHP_STREAM_XPORT_IS_UNIX_DG(stream) || PHP_STREAM_XPORT_IS_UNIX_ST(stream))
54
#else
55
#define PHP_STREAM_XPORT_IS_UNIX_DG(stream) false
56
#define PHP_STREAM_XPORT_IS_UNIX_ST(stream) false
57
#define PHP_STREAM_XPORT_IS_UNIX(stream) false
58
#endif
59
0
#define PHP_STREAM_XPORT_IS_UDP(stream) (php_stream_is(stream, &php_stream_udp_socket_ops))
60
0
#define PHP_STREAM_XPORT_IS_TCP(stream) (!PHP_STREAM_XPORT_IS_UNIX(stream) && !PHP_STREAM_XPORT_IS_UDP(stream))
61
62
static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam);
63
64
/* {{{ Generic socket stream operations */
65
static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t count)
66
0
{
67
0
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
68
0
  ssize_t didwrite;
69
0
  struct timeval *ptimeout;
70
71
0
  if (!sock || sock->socket == -1) {
72
0
    return 0;
73
0
  }
74
75
0
  if (sock->timeout.tv_sec == -1)
76
0
    ptimeout = NULL;
77
0
  else
78
0
    ptimeout = &sock->timeout;
79
80
0
retry:
81
0
  didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
82
83
0
  if (didwrite <= 0) {
84
0
    char *estr;
85
0
    int err = php_socket_errno();
86
87
0
    if (PHP_IS_TRANSIENT_ERROR(err)) {
88
0
      if (sock->is_blocked) {
89
0
        int retval;
90
91
0
        sock->timeout_event = false;
92
93
0
        do {
94
0
          retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
95
96
0
          if (retval == 0) {
97
0
            sock->timeout_event = true;
98
0
            break;
99
0
          }
100
101
0
          if (retval > 0) {
102
            /* writable now; retry */
103
0
            goto retry;
104
0
          }
105
106
0
          err = php_socket_errno();
107
0
        } while (err == EINTR);
108
0
      } else {
109
        /* EWOULDBLOCK/EAGAIN is not an error for a non-blocking stream.
110
         * Report zero byte write instead. */
111
0
        return 0;
112
0
      }
113
0
    }
114
115
0
    if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
116
0
      estr = php_socket_strerror(err, NULL, 0);
117
0
      php_stream_warn(stream, NetworkSendFailed,
118
0
          "Send of %zu bytes failed with errno=%d %s", count, err, estr);
119
0
      efree(estr);
120
0
    }
121
0
  }
122
123
0
  if (didwrite > 0) {
124
0
    php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);
125
0
  }
126
127
0
  return didwrite;
128
0
}
129
130
static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock, bool has_buffered_data)
131
0
{
132
0
  int retval;
133
0
  struct timeval *ptimeout, zero_timeout;
134
135
0
  if (!sock || sock->socket == -1) {
136
0
    return;
137
0
  }
138
139
0
  sock->timeout_event = false;
140
141
0
  if (has_buffered_data) {
142
    /* If there is already buffered data, use no timeout. */
143
0
    zero_timeout.tv_sec = 0;
144
0
    zero_timeout.tv_usec = 0;
145
0
    ptimeout = &zero_timeout;
146
0
  } else if (sock->timeout.tv_sec == -1) {
147
0
    ptimeout = NULL;
148
0
  } else {
149
0
    ptimeout = &sock->timeout;
150
0
  }
151
152
0
  while(1) {
153
0
    retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
154
155
0
    if (retval == 0)
156
0
      sock->timeout_event = true;
157
158
0
    if (retval >= 0)
159
0
      break;
160
161
0
    if (php_socket_errno() != EINTR)
162
0
      break;
163
0
  }
164
0
}
165
166
static ssize_t php_sockop_read(php_stream *stream, char *buf, size_t count)
167
0
{
168
0
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
169
170
0
  if (!sock || sock->socket == -1) {
171
0
    return -1;
172
0
  }
173
174
0
  int recv_flags = 0;
175
  /* Special handling for blocking read. */
176
0
  if (sock->is_blocked) {
177
    /* Find out if there is any data buffered from the previous read. */
178
0
    bool has_buffered_data = stream->has_buffered_data;
179
    /* No need to wait if there is any data buffered or no timeout. */
180
0
    bool dont_wait = has_buffered_data ||
181
0
        (sock->timeout.tv_sec == 0 && sock->timeout.tv_usec == 0);
182
    /* Set MSG_DONTWAIT if no wait is needed or there is unlimited timeout which was
183
     * added by fix for #41984 committed in 9343c5404. */
184
0
    if (dont_wait || sock->timeout.tv_sec != -1) {
185
0
      recv_flags = MSG_DONTWAIT;
186
0
    }
187
    /* If the wait is needed or it is a platform without MSG_DONTWAIT support (e.g. Windows),
188
     * then poll for data. */
189
0
    if (!dont_wait || MSG_DONTWAIT == 0) {
190
0
      php_sock_stream_wait_for_data(stream, sock, has_buffered_data);
191
0
      if (sock->timeout_event) {
192
        /* It is ok to timeout if there is any data buffered so return 0, otherwise -1. */
193
0
        return has_buffered_data ? 0 : -1;
194
0
      }
195
0
    }
196
0
  }
197
198
0
  ssize_t nr_bytes = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(count), recv_flags);
199
0
  int err = php_socket_errno();
200
201
0
  if (nr_bytes < 0) {
202
0
    if (PHP_IS_TRANSIENT_ERROR(err)) {
203
0
      nr_bytes = 0;
204
0
    } else {
205
0
      stream->eof = 1;
206
0
    }
207
0
  } else if (nr_bytes == 0) {
208
0
    stream->eof = 1;
209
0
  }
210
211
0
  if (nr_bytes > 0) {
212
0
    php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);
213
0
  }
214
215
0
  return nr_bytes;
216
0
}
217
218
219
static int php_sockop_close(php_stream *stream, int close_handle)
220
0
{
221
0
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
222
#ifdef PHP_WIN32
223
  int n;
224
#endif
225
226
0
  if (!sock) {
227
0
    return 0;
228
0
  }
229
230
0
  if (close_handle) {
231
232
#ifdef PHP_WIN32
233
    if (sock->socket == -1)
234
      sock->socket = SOCK_ERR;
235
#endif
236
0
    if (sock->socket != SOCK_ERR) {
237
#ifdef PHP_WIN32
238
      /* prevent more data from coming in */
239
      shutdown(sock->socket, SHUT_RD);
240
241
      /* try to make sure that the OS sends all data before we close the connection.
242
       * Essentially, we are waiting for the socket to become writeable, which means
243
       * that all pending data has been sent.
244
       * We use a small timeout which should encourage the OS to send the data,
245
       * but at the same time avoid hanging indefinitely.
246
       * */
247
      do {
248
        n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
249
      } while (n == -1 && php_socket_errno() == EINTR);
250
#endif
251
0
      closesocket(sock->socket);
252
0
      sock->socket = SOCK_ERR;
253
0
    }
254
255
0
  }
256
257
0
  pefree(sock, php_stream_is_persistent(stream));
258
259
0
  return 0;
260
0
}
261
262
static int php_sockop_flush(php_stream *stream)
263
0
{
264
#if 0
265
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
266
  return fsync(sock->socket);
267
#endif
268
0
  return 0;
269
0
}
270
271
static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb)
272
0
{
273
#ifdef ZEND_WIN32
274
  return 0;
275
#else
276
0
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
277
278
0
  return zend_fstat(sock->socket, &ssb->sb);
279
0
#endif
280
0
}
281
282
static inline int sock_sendto(php_netstream_data_t *sock, const char *buf, size_t buflen, int flags,
283
    struct sockaddr *addr, socklen_t addrlen
284
    )
285
0
{
286
0
  int ret;
287
0
  if (addr) {
288
0
    ret = sendto(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, addr, XP_SOCK_BUF_SIZE(addrlen));
289
290
0
    return (ret == SOCK_CONN_ERR) ? -1 : ret;
291
0
  }
292
#ifdef PHP_WIN32
293
  return ((ret = send(sock->socket, buf, buflen > INT_MAX ? INT_MAX : (int)buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
294
#else
295
0
  return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
296
0
#endif
297
0
}
298
299
static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
300
    zend_string **textaddr,
301
    struct sockaddr **addr, socklen_t *addrlen
302
    )
303
0
{
304
0
  int ret;
305
0
  int want_addr = textaddr || addr;
306
307
0
  if (want_addr) {
308
0
    php_sockaddr_storage sa;
309
0
    socklen_t sl = sizeof(sa);
310
0
    ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl);
311
0
    ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
312
#ifdef PHP_WIN32
313
    /* POSIX discards excess bytes without signalling failure; emulate this on Windows */
314
    if (ret == -1 && WSAGetLastError() == WSAEMSGSIZE) {
315
      ret = buflen;
316
    }
317
#endif
318
0
    if (sl) {
319
0
      php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
320
0
          textaddr, addr, addrlen);
321
0
    } else {
322
0
      if (textaddr) {
323
0
        *textaddr = ZSTR_EMPTY_ALLOC();
324
0
      }
325
0
      if (addr) {
326
0
        *addr = NULL;
327
0
        *addrlen = 0;
328
0
      }
329
0
    }
330
0
  } else {
331
0
    ret = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags);
332
0
    ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
333
0
  }
334
335
0
  return ret;
336
0
}
337
338
static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
339
0
{
340
0
  int oldmode, flags;
341
0
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
342
0
  php_stream_xport_param *xparam;
343
344
0
  if (!sock) {
345
0
    return PHP_STREAM_OPTION_RETURN_NOTIMPL;
346
0
  }
347
348
0
  switch(option) {
349
0
    case PHP_STREAM_OPTION_CHECK_LIVENESS:
350
0
      {
351
0
        struct timeval tv;
352
0
        char buf;
353
0
        int alive = 1;
354
355
0
        if (value == -1) {
356
0
          if (sock->timeout.tv_sec == -1) {
357
0
            tv.tv_sec = FG(default_socket_timeout);
358
0
            tv.tv_usec = 0;
359
0
          } else {
360
0
            tv = sock->timeout;
361
0
          }
362
0
        } else {
363
0
          tv.tv_sec = value;
364
0
          tv.tv_usec = 0;
365
0
        }
366
367
0
        if (sock->socket == -1) {
368
0
          alive = 0;
369
0
        } else if (
370
0
          (
371
0
            value == 0 &&
372
0
            !(stream->flags & PHP_STREAM_FLAG_NO_IO) &&
373
0
            ((MSG_DONTWAIT != 0) || !sock->is_blocked)
374
0
          ) ||
375
0
          php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0
376
0
        ) {
377
          /* the poll() call was skipped if the socket is non-blocking (or MSG_DONTWAIT is available) and if the timeout is zero */
378
#ifdef PHP_WIN32
379
          int ret;
380
#else
381
0
          ssize_t ret;
382
0
#endif
383
384
0
          ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK|MSG_DONTWAIT);
385
0
          if (0 == ret) {
386
            /* the counterpart did properly shutdown */
387
0
            alive = 0;
388
0
          } else if (0 > ret) {
389
0
            int err = php_socket_errno();
390
0
            if (err != EWOULDBLOCK && err != EMSGSIZE && err != EAGAIN) {
391
              /* there was an unrecoverable error */
392
0
              alive = 0;
393
0
            }
394
0
          }
395
0
        }
396
0
        return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
397
0
      }
398
399
0
    case PHP_STREAM_OPTION_BLOCKING:
400
0
      oldmode = sock->is_blocked;
401
0
      if (SUCCESS == php_set_sock_blocking(sock->socket, value)) {
402
0
        sock->is_blocked = value;
403
0
        return oldmode;
404
0
      }
405
0
      return PHP_STREAM_OPTION_RETURN_ERR;
406
407
0
    case PHP_STREAM_OPTION_READ_TIMEOUT:
408
0
      sock->timeout = *(struct timeval*)ptrparam;
409
0
      sock->timeout_event = false;
410
0
      return PHP_STREAM_OPTION_RETURN_OK;
411
412
0
    case PHP_STREAM_OPTION_META_DATA_API:
413
0
      add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
414
0
      add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
415
0
      add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
416
0
      return PHP_STREAM_OPTION_RETURN_OK;
417
418
0
    case PHP_STREAM_OPTION_XPORT_API:
419
0
      xparam = (php_stream_xport_param *)ptrparam;
420
421
0
      switch (xparam->op) {
422
0
        case STREAM_XPORT_OP_LISTEN:
423
0
          xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ?  0: -1;
424
0
          return PHP_STREAM_OPTION_RETURN_OK;
425
426
0
        case STREAM_XPORT_OP_GET_NAME:
427
0
          xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
428
0
              xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
429
0
              xparam->want_addr ? &xparam->outputs.addr : NULL,
430
0
              xparam->want_addr ? &xparam->outputs.addrlen : NULL
431
0
              );
432
0
          return PHP_STREAM_OPTION_RETURN_OK;
433
434
0
        case STREAM_XPORT_OP_GET_PEER_NAME:
435
0
          xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
436
0
              xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
437
0
              xparam->want_addr ? &xparam->outputs.addr : NULL,
438
0
              xparam->want_addr ? &xparam->outputs.addrlen : NULL
439
0
              );
440
0
          return PHP_STREAM_OPTION_RETURN_OK;
441
442
0
        case STREAM_XPORT_OP_SEND:
443
0
          flags = 0;
444
0
          if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
445
0
            flags |= MSG_OOB;
446
0
          }
447
0
          xparam->outputs.returncode = sock_sendto(sock,
448
0
              xparam->inputs.buf, xparam->inputs.buflen,
449
0
              flags,
450
0
              xparam->inputs.addr,
451
0
              xparam->inputs.addrlen);
452
0
          if (xparam->outputs.returncode == -1) {
453
0
            char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
454
0
            php_stream_warn(stream, NetworkSendFailed, "%s", err);
455
0
            efree(err);
456
0
          }
457
0
          return PHP_STREAM_OPTION_RETURN_OK;
458
459
0
        case STREAM_XPORT_OP_RECV:
460
0
          flags = 0;
461
0
          if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
462
0
            flags |= MSG_OOB;
463
0
          }
464
0
          if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
465
0
            flags |= MSG_PEEK;
466
0
          }
467
0
          xparam->outputs.returncode = sock_recvfrom(sock,
468
0
              xparam->inputs.buf, xparam->inputs.buflen,
469
0
              flags,
470
0
              xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
471
0
              xparam->want_addr ? &xparam->outputs.addr : NULL,
472
0
              xparam->want_addr ? &xparam->outputs.addrlen : NULL
473
0
              );
474
0
          return PHP_STREAM_OPTION_RETURN_OK;
475
476
477
0
#ifdef HAVE_SHUTDOWN
478
# ifndef SHUT_RD
479
#  define SHUT_RD 0
480
# endif
481
# ifndef SHUT_WR
482
#  define SHUT_WR 1
483
# endif
484
# ifndef SHUT_RDWR
485
#  define SHUT_RDWR 2
486
# endif
487
0
        case STREAM_XPORT_OP_SHUTDOWN: {
488
0
          static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
489
490
0
          xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
491
0
          return PHP_STREAM_OPTION_RETURN_OK;
492
0
        }
493
0
#endif
494
495
0
        default:
496
0
          break;
497
0
      }
498
0
  }
499
500
0
  return PHP_STREAM_OPTION_RETURN_NOTIMPL;
501
0
}
502
503
static int php_sockop_cast(php_stream *stream, int castas, void **ret)
504
0
{
505
0
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
506
507
0
  if (!sock) {
508
0
    return FAILURE;
509
0
  }
510
511
0
  switch(castas)  {
512
0
    case PHP_STREAM_AS_STDIO:
513
0
      if (ret) {
514
0
        *(FILE**)ret = fdopen(sock->socket, stream->mode);
515
0
        if (*ret)
516
0
          return SUCCESS;
517
0
        return FAILURE;
518
0
      }
519
0
      return SUCCESS;
520
0
    case PHP_STREAM_AS_FD_FOR_SELECT:
521
0
    case PHP_STREAM_AS_FD_FOR_POLL:
522
0
    case PHP_STREAM_AS_FD:
523
0
    case PHP_STREAM_AS_SOCKETD:
524
0
      if (ret)
525
0
        *(php_socket_t *)ret = sock->socket;
526
0
      return SUCCESS;
527
0
    case PHP_STREAM_AS_FD_FOR_COPY:
528
0
      if (ret) {
529
0
        php_io_fd *copy_fd = (php_io_fd *) ret;
530
0
        copy_fd->socket = sock->socket;
531
0
        copy_fd->fd_type = PHP_IO_FD_SOCKET;
532
0
        copy_fd->timeout = sock->timeout;
533
0
        copy_fd->is_blocked = sock->is_blocked;
534
0
      }
535
0
      return SUCCESS;
536
0
    default:
537
0
      return FAILURE;
538
0
  }
539
0
}
540
541
/* These may look identical, but we need them this way so that
542
 * we can determine which type of socket we are dealing with
543
 * by inspecting stream->ops.
544
 * A "useful" side-effect is that the user's scripts can then
545
 * make similar decisions using stream_get_meta_data.
546
 * */
547
const php_stream_ops php_stream_generic_socket_ops = {
548
  php_sockop_write, php_sockop_read,
549
  php_sockop_close, php_sockop_flush,
550
  "generic_socket",
551
  NULL, /* seek */
552
  php_sockop_cast,
553
  php_sockop_stat,
554
  php_sockop_set_option,
555
};
556
557
558
const php_stream_ops php_stream_socket_ops = {
559
  php_sockop_write, php_sockop_read,
560
  php_sockop_close, php_sockop_flush,
561
  "tcp_socket",
562
  NULL, /* seek */
563
  php_sockop_cast,
564
  php_sockop_stat,
565
  php_tcp_sockop_set_option,
566
};
567
568
static const php_stream_ops php_stream_udp_socket_ops = {
569
  php_sockop_write, php_sockop_read,
570
  php_sockop_close, php_sockop_flush,
571
  "udp_socket",
572
  NULL, /* seek */
573
  php_sockop_cast,
574
  php_sockop_stat,
575
  php_tcp_sockop_set_option,
576
};
577
578
#ifdef AF_UNIX
579
static const php_stream_ops php_stream_unix_socket_ops = {
580
  php_sockop_write, php_sockop_read,
581
  php_sockop_close, php_sockop_flush,
582
  "unix_socket",
583
  NULL, /* seek */
584
  php_sockop_cast,
585
  php_sockop_stat,
586
  php_tcp_sockop_set_option,
587
};
588
static const php_stream_ops php_stream_unixdg_socket_ops = {
589
  php_sockop_write, php_sockop_read,
590
  php_sockop_close, php_sockop_flush,
591
  "udg_socket",
592
  NULL, /* seek */
593
  php_sockop_cast,
594
  php_sockop_stat,
595
  php_tcp_sockop_set_option,
596
};
597
#endif
598
599
600
/* network socket operations */
601
602
#ifdef AF_UNIX
603
static inline int parse_unix_address(php_stream *stream, php_stream_xport_param *xparam,
604
    struct sockaddr_un *unix_addr)
605
0
{
606
0
  memset(unix_addr, 0, sizeof(*unix_addr));
607
0
  unix_addr->sun_family = AF_UNIX;
608
609
  /* Abstract namespace does not need to be NUL-terminated, while path-based
610
   * sockets should be. */
611
0
  bool is_abstract_ns = xparam->inputs.namelen > 0 && xparam->inputs.name[0] == '\0';
612
0
  unsigned long max_length = is_abstract_ns ? sizeof(unix_addr->sun_path) : sizeof(unix_addr->sun_path) - 1;
613
614
  /* we need to be binary safe on systems that support an abstract
615
   * namespace */
616
0
  if (xparam->inputs.namelen > max_length) {
617
    /* On linux, when the path begins with a NUL byte we are
618
     * referring to an abstract namespace.  In theory we should
619
     * allow an extra byte below, since we don't need the NULL.
620
     * BUT, to get into this branch of code, the name is too long,
621
     * so we don't care. */
622
0
    xparam->inputs.namelen = max_length;
623
0
    php_stream_notice(stream, InvalidPath,
624
0
        "socket path exceeded the maximum allowed length of %lu bytes and was truncated",
625
0
        max_length);
626
0
  }
627
628
0
  memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
629
630
0
  return 1;
631
0
}
632
#endif
633
634
static inline bool parse_port(const char *str, int *portno)
635
0
{
636
  /* Like atoi(), but reject ports that do not fit in 16 bits */
637
0
  zend_long port = ZEND_STRTOL(str, NULL, 10);
638
0
  if (port < 0 || port > 65535) {
639
0
    return false;
640
0
  }
641
0
  *portno = (int) port;
642
0
  return true;
643
0
}
644
645
static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err)
646
0
{
647
0
  const char *colon;
648
0
  char *host = NULL;
649
650
0
  if (memchr(str, '\0', str_len)) {
651
0
    *err = ZSTR_INIT_LITERAL("The hostname must not contain null bytes", 0);
652
0
    return NULL;
653
0
  }
654
655
0
#ifdef HAVE_IPV6
656
0
  if (*(str) == '[' && str_len > 1) {
657
    /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
658
0
    const char *p = memchr(str + 1, ']', str_len - 2);
659
0
    if (!p || *(p + 1) != ':') {
660
0
      if (get_err) {
661
0
        *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);
662
0
      }
663
0
      return NULL;
664
0
    }
665
0
    if (!parse_port(p + 2, portno)) {
666
0
      if (get_err) {
667
0
        *err = strpprintf(0, "Failed to parse port of address \"%s\"", str);
668
0
      }
669
0
      return NULL;
670
0
    }
671
0
    return estrndup(str + 1, p - str - 1);
672
0
  }
673
0
#endif
674
0
  if (str_len) {
675
0
    colon = memchr(str, ':', str_len - 1);
676
0
  } else {
677
0
    colon = NULL;
678
0
  }
679
0
  if (colon) {
680
0
    if (!parse_port(colon + 1, portno)) {
681
0
      if (get_err) {
682
0
        *err = strpprintf(0, "Failed to parse port of address \"%s\"", str);
683
0
      }
684
0
      return NULL;
685
0
    }
686
0
    host = estrndup(str, colon - str);
687
0
  } else {
688
0
    if (get_err) {
689
0
      *err = strpprintf(0, "Failed to parse address \"%s\"", str);
690
0
    }
691
0
    return NULL;
692
0
  }
693
694
0
  return host;
695
0
}
696
697
static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno)
698
0
{
699
0
  return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text);
700
0
}
701
702
static int php_sockop_parse_sockvals(php_stream *stream, php_stream_xport_param *xparam,
703
    php_sockvals *sockvals)
704
0
{
705
0
  zval *tmpzval;
706
707
0
  if (!PHP_STREAM_CONTEXT(stream)) {
708
0
    return 0;
709
0
  }
710
711
0
#ifdef SO_LINGER
712
0
  if (PHP_STREAM_XPORT_IS_TCP(stream)
713
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_linger")) != NULL
714
0
  ) {
715
0
    sockvals->mask |= PHP_SOCKVAL_SO_LINGER;
716
0
    sockvals->linger = (int)zval_get_long(tmpzval);
717
0
  }
718
0
#endif
719
720
0
#ifdef SO_RCVBUF
721
0
  if ((tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_rcvbuf")) != NULL) {
722
0
    zend_long bufsize = zval_get_long(tmpzval);
723
724
0
    if (bufsize < 1 || bufsize > INT_MAX) {
725
0
      if (xparam->want_errortext) {
726
0
        xparam->outputs.error_text = strpprintf(0, "so_rcvbuf context option must be between 1 and %d", INT_MAX);
727
0
      }
728
0
      return -1;
729
0
    }
730
731
0
    sockvals->mask |= PHP_SOCKVAL_SO_RCVBUF;
732
0
    sockvals->rcvbuf = (int) bufsize;
733
0
  }
734
0
#endif
735
736
0
#ifdef SO_SNDBUF
737
0
  if ((tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_sndbuf")) != NULL) {
738
0
    zend_long bufsize = zval_get_long(tmpzval);
739
740
0
    if (bufsize < 1 || bufsize > INT_MAX) {
741
0
      if (xparam->want_errortext) {
742
0
        xparam->outputs.error_text = strpprintf(0, "so_sndbuf context option must be between 1 and %d", INT_MAX);
743
0
      }
744
0
      return -1;
745
0
    }
746
747
0
    sockvals->mask |= PHP_SOCKVAL_SO_SNDBUF;
748
0
    sockvals->sndbuf = (int) bufsize;
749
0
  }
750
0
#endif
751
752
0
  return 0;
753
0
}
754
755
static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
756
    php_stream_xport_param *xparam)
757
0
{
758
0
  char *host = NULL;
759
0
  int portno, err;
760
0
  long sockopts = STREAM_SOCKOP_NONE;
761
0
  zval *tmpzval = NULL;
762
0
  php_sockvals sockvals = {0};
763
764
0
  if (php_sockop_parse_sockvals(stream, xparam, &sockvals) == -1) {
765
0
    return -1;
766
0
  }
767
768
0
#ifdef AF_UNIX
769
0
  if (PHP_STREAM_XPORT_IS_UNIX(stream)) {
770
0
    struct sockaddr_un unix_addr;
771
772
0
    sock->socket = socket(PF_UNIX, PHP_STREAM_XPORT_IS_UNIX_ST(stream) ? SOCK_STREAM : SOCK_DGRAM, 0);
773
774
0
    if (sock->socket == SOCK_ERR) {
775
0
      if (xparam->want_errortext) {
776
0
        char errstr[256];
777
0
        xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s",
778
0
            PHP_STREAM_XPORT_IS_UNIX_ST(stream) ? "" : " datagram",
779
0
            php_socket_strerror_s(errno, errstr, sizeof(errstr)));
780
0
      }
781
0
      return -1;
782
0
    }
783
784
0
    php_network_apply_sockvals(sock->socket, &sockvals);
785
786
0
    parse_unix_address(stream, xparam, &unix_addr);
787
788
0
    int result = bind(sock->socket, (const struct sockaddr *)&unix_addr,
789
0
      (socklen_t) offsetof(struct sockaddr_un, sun_path) + xparam->inputs.namelen);
790
0
    if (result == -1 && xparam->want_errortext) {
791
0
      char errstr[256];
792
0
      xparam->outputs.error_text = strpprintf(0, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
793
0
    }
794
0
    return result;
795
0
  }
796
0
#endif
797
798
0
  host = parse_ip_address(xparam, &portno);
799
800
0
  if (host == NULL) {
801
0
    return -1;
802
0
  }
803
804
0
#ifdef IPV6_V6ONLY
805
0
  if (PHP_STREAM_CONTEXT(stream)
806
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "ipv6_v6only")) != NULL
807
0
    && Z_TYPE_P(tmpzval) != IS_NULL
808
0
  ) {
809
0
    sockopts |= STREAM_SOCKOP_IPV6_V6ONLY;
810
0
    sockopts |= STREAM_SOCKOP_IPV6_V6ONLY_ENABLED * zend_is_true(tmpzval);
811
0
  }
812
0
#endif
813
814
0
#ifdef SO_REUSEPORT
815
0
  if (PHP_STREAM_CONTEXT(stream)
816
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_reuseport")) != NULL
817
0
    && zend_is_true(tmpzval)
818
0
  ) {
819
0
    sockopts |= STREAM_SOCKOP_SO_REUSEPORT;
820
0
  }
821
0
#endif
822
823
0
#ifdef SO_REUSEADDR
824
  /* SO_REUSEADDR is enabled by default so this option is just to disable it if set to false. */
825
0
  if (!PHP_STREAM_CONTEXT(stream)
826
0
    || (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_reuseaddr")) == NULL
827
0
    || zend_is_true(tmpzval)
828
0
  ) {
829
0
    sockopts |= STREAM_SOCKOP_SO_REUSEADDR;
830
0
  }
831
0
#endif
832
833
0
#ifdef SO_BROADCAST
834
0
  if (PHP_STREAM_XPORT_IS_UDP(stream) /* SO_BROADCAST is only applicable for UDP */
835
0
    && PHP_STREAM_CONTEXT(stream)
836
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
837
0
    && zend_is_true(tmpzval)
838
0
  ) {
839
0
    sockopts |= STREAM_SOCKOP_SO_BROADCAST;
840
0
  }
841
0
#endif
842
843
0
#ifdef SO_KEEPALIVE
844
0
  if (PHP_STREAM_XPORT_IS_TCP(stream) /* SO_KEEPALIVE is only applicable for TCP */
845
0
    && PHP_STREAM_CONTEXT(stream)
846
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_keepalive")) != NULL
847
0
    && zend_is_true(tmpzval)
848
0
  ) {
849
0
    sockopts |= STREAM_SOCKOP_SO_KEEPALIVE;
850
0
  }
851
0
#endif
852
853
  /* Parse TCP keepalive parameters - only for TCP streams */
854
0
  if (PHP_STREAM_XPORT_IS_TCP(stream)) {
855
0
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
856
0
    if (PHP_STREAM_CONTEXT(stream)
857
0
      && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepidle")) != NULL
858
0
    ) {
859
0
      sockvals.mask |= PHP_SOCKVAL_TCP_KEEPIDLE;
860
0
      sockvals.keepalive.keepidle = (int)zval_get_long(tmpzval);
861
0
    }
862
0
#endif
863
864
0
#ifdef TCP_KEEPINTVL
865
0
    if (PHP_STREAM_CONTEXT(stream)
866
0
      && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepintvl")) != NULL
867
0
    ) {
868
0
      sockvals.mask |= PHP_SOCKVAL_TCP_KEEPINTVL;
869
0
      sockvals.keepalive.keepintvl = (int)zval_get_long(tmpzval);
870
0
    }
871
0
#endif
872
873
0
#ifdef TCP_KEEPCNT
874
0
    if (PHP_STREAM_CONTEXT(stream)
875
0
      && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepcnt")) != NULL
876
0
    ) {
877
0
      sockvals.mask |= PHP_SOCKVAL_TCP_KEEPCNT;
878
0
      sockvals.keepalive.keepcnt = (int)zval_get_long(tmpzval);
879
0
    }
880
0
#endif
881
0
  }
882
883
0
  sock->socket = php_network_bind_socket_to_local_addr_ex(host, portno,
884
0
      PHP_STREAM_XPORT_IS_UDP(stream) ? SOCK_DGRAM : SOCK_STREAM,
885
0
      sockopts,
886
0
      sockvals.mask ? &sockvals : NULL,
887
0
      xparam->want_errortext ? &xparam->outputs.error_text : NULL,
888
0
      &err
889
0
      );
890
891
0
  if (host) {
892
0
    efree(host);
893
0
  }
894
895
0
  return sock->socket == -1 ? -1 : 0;
896
0
}
897
898
static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
899
    php_stream_xport_param *xparam)
900
0
{
901
0
  char *host = NULL, *bindto = NULL;
902
0
  int portno, bindport = 0;
903
0
  int err = 0;
904
0
  int ret;
905
0
  zval *tmpzval = NULL;
906
0
  long sockopts = STREAM_SOCKOP_NONE;
907
0
  php_sockvals sockvals = {0};
908
909
0
  if (php_sockop_parse_sockvals(stream, xparam, &sockvals) == -1) {
910
0
    return -1;
911
0
  }
912
913
0
#ifdef AF_UNIX
914
0
  if (PHP_STREAM_XPORT_IS_UNIX(stream)) {
915
0
    struct sockaddr_un unix_addr;
916
917
0
    sock->socket = socket(PF_UNIX, PHP_STREAM_XPORT_IS_UNIX_ST(stream) ? SOCK_STREAM : SOCK_DGRAM, 0);
918
919
0
    if (sock->socket == SOCK_ERR) {
920
0
      if (xparam->want_errortext) {
921
0
        xparam->outputs.error_text = strpprintf(0, "Failed to create unix socket");
922
0
      }
923
0
      return -1;
924
0
    }
925
926
0
    php_network_apply_sockvals(sock->socket, &sockvals);
927
928
0
    parse_unix_address(stream, xparam, &unix_addr);
929
930
0
    ret = php_network_connect_socket(sock->socket,
931
0
        (const struct sockaddr *)&unix_addr, (socklen_t) offsetof(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
932
0
        xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
933
0
        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
934
0
        &err);
935
936
0
    xparam->outputs.error_code = err;
937
938
0
    goto out;
939
0
  }
940
0
#endif
941
942
0
  host = parse_ip_address(xparam, &portno);
943
944
0
  if (host == NULL) {
945
0
    return -1;
946
0
  }
947
948
0
  if (PHP_STREAM_CONTEXT(stream) && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "bindto")) != NULL) {
949
0
    if (Z_TYPE_P(tmpzval) != IS_STRING) {
950
0
      if (xparam->want_errortext) {
951
0
        xparam->outputs.error_text = strpprintf(0, "local_addr context option is not a string.");
952
0
      }
953
0
      efree(host);
954
0
      return -1;
955
0
    }
956
0
    bindto = parse_ip_address_ex(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text);
957
0
  }
958
959
0
#ifdef SO_BROADCAST
960
0
  if (PHP_STREAM_XPORT_IS_UDP(stream) /* SO_BROADCAST is only applicable for UDP */
961
0
    && PHP_STREAM_CONTEXT(stream)
962
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
963
0
    && zend_is_true(tmpzval)
964
0
  ) {
965
0
    sockopts |= STREAM_SOCKOP_SO_BROADCAST;
966
0
  }
967
0
#endif
968
969
0
  if (PHP_STREAM_XPORT_IS_TCP(stream) /* TCP_NODELAY is only applicable for TCP */
970
0
    && PHP_STREAM_CONTEXT(stream)
971
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
972
0
    && zend_is_true(tmpzval)
973
0
  ) {
974
0
    sockopts |= STREAM_SOCKOP_TCP_NODELAY;
975
0
  }
976
977
0
#ifdef SO_KEEPALIVE
978
0
  if (PHP_STREAM_XPORT_IS_TCP(stream) /* SO_KEEPALIVE is only applicable for TCP */
979
0
    && PHP_STREAM_CONTEXT(stream)
980
0
    && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_keepalive")) != NULL
981
0
    && zend_is_true(tmpzval)
982
0
  ) {
983
0
    sockopts |= STREAM_SOCKOP_SO_KEEPALIVE;
984
0
  }
985
0
#endif
986
987
  /* Parse TCP keepalive parameters - only for TCP streams */
988
0
  if (PHP_STREAM_XPORT_IS_TCP(stream)) {
989
0
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
990
0
    if (PHP_STREAM_CONTEXT(stream)
991
0
      && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepidle")) != NULL
992
0
    ) {
993
0
      sockvals.mask |= PHP_SOCKVAL_TCP_KEEPIDLE;
994
0
      sockvals.keepalive.keepidle = (int)zval_get_long(tmpzval);
995
0
    }
996
0
#endif
997
998
0
#ifdef TCP_KEEPINTVL
999
0
    if (PHP_STREAM_CONTEXT(stream)
1000
0
      && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepintvl")) != NULL
1001
0
    ) {
1002
0
      sockvals.mask |= PHP_SOCKVAL_TCP_KEEPINTVL;
1003
0
      sockvals.keepalive.keepintvl = (int)zval_get_long(tmpzval);
1004
0
    }
1005
0
#endif
1006
1007
0
#ifdef TCP_KEEPCNT
1008
0
    if (PHP_STREAM_CONTEXT(stream)
1009
0
      && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepcnt")) != NULL
1010
0
    ) {
1011
0
      sockvals.mask |= PHP_SOCKVAL_TCP_KEEPCNT;
1012
0
      sockvals.keepalive.keepcnt = (int)zval_get_long(tmpzval);
1013
0
    }
1014
0
#endif
1015
0
  }
1016
1017
  /* Note: the test here for php_stream_udp_socket_ops is important, because we
1018
   * want the default to be TCP sockets so that the openssl extension can
1019
   * re-use this code. */
1020
1021
0
  sock->socket = php_network_connect_socket_to_host_ex(host, portno,
1022
0
      PHP_STREAM_XPORT_IS_UDP(stream) ? SOCK_DGRAM : SOCK_STREAM,
1023
0
      xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
1024
0
      xparam->inputs.timeout,
1025
0
      xparam->want_errortext ? &xparam->outputs.error_text : NULL,
1026
0
      &err,
1027
0
      bindto,
1028
0
      bindport,
1029
0
      sockopts,
1030
0
      sockvals.mask ? &sockvals : NULL
1031
0
      );
1032
1033
0
  ret = sock->socket == -1 ? -1 : 0;
1034
0
  xparam->outputs.error_code = err;
1035
1036
0
  if (host) {
1037
0
    efree(host);
1038
0
  }
1039
0
  if (bindto) {
1040
0
    efree(bindto);
1041
0
  }
1042
1043
0
#ifdef AF_UNIX
1044
0
out:
1045
0
#endif
1046
1047
0
  if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
1048
    /* indicates pending connection */
1049
0
    return 1;
1050
0
  }
1051
1052
0
  return ret;
1053
0
}
1054
1055
static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
1056
    php_stream_xport_param *xparam STREAMS_DC)
1057
0
{
1058
0
  php_sockvals sockvals = {0};
1059
0
  zval *tmpzval = NULL;
1060
1061
0
  xparam->outputs.client = NULL;
1062
1063
0
  if (PHP_STREAM_CONTEXT(stream)) {
1064
0
    tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay");
1065
0
    if (tmpzval != NULL && zend_is_true(tmpzval)) {
1066
0
      sockvals.mask |= PHP_SOCKVAL_TCP_NODELAY;
1067
0
      sockvals.tcp_nodelay = 1;
1068
0
    }
1069
0
    tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_keepidle");
1070
0
    if (tmpzval != NULL) {
1071
0
      sockvals.mask |= PHP_SOCKVAL_TCP_KEEPIDLE;
1072
0
      sockvals.keepalive.keepidle = (int)zval_get_long(tmpzval);
1073
0
    }
1074
0
  }
1075
1076
0
  php_socket_t clisock = php_network_accept_incoming_ex(sock->socket,
1077
0
    xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
1078
0
    xparam->want_addr ? &xparam->outputs.addr : NULL,
1079
0
    xparam->want_addr ? &xparam->outputs.addrlen : NULL,
1080
0
    xparam->inputs.timeout,
1081
0
    xparam->want_errortext ? &xparam->outputs.error_text : NULL,
1082
0
    &xparam->outputs.error_code,
1083
0
    &sockvals);
1084
1085
0
  if (clisock != SOCK_ERR) {
1086
0
    php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));
1087
1088
0
    memcpy(clisockdata, sock, sizeof(*clisockdata));
1089
0
    clisockdata->socket = clisock;
1090
0
#ifdef __linux__
1091
    /* O_NONBLOCK is not inherited on Linux */
1092
0
    clisockdata->is_blocked = true;
1093
0
#endif
1094
1095
0
    xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
1096
0
    if (xparam->outputs.client) {
1097
0
      xparam->outputs.client->ctx = stream->ctx;
1098
0
      if (stream->ctx) {
1099
0
        GC_ADDREF(stream->ctx);
1100
0
      }
1101
0
    }
1102
0
  }
1103
1104
0
  return xparam->outputs.client == NULL ? -1 : 0;
1105
0
}
1106
1107
static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
1108
0
{
1109
0
  php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
1110
0
  php_stream_xport_param *xparam;
1111
1112
0
  switch(option) {
1113
0
    case PHP_STREAM_OPTION_XPORT_API:
1114
0
      xparam = (php_stream_xport_param *)ptrparam;
1115
1116
0
      switch(xparam->op) {
1117
0
        case STREAM_XPORT_OP_CONNECT:
1118
0
        case STREAM_XPORT_OP_CONNECT_ASYNC:
1119
0
          xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam);
1120
0
          return PHP_STREAM_OPTION_RETURN_OK;
1121
1122
0
        case STREAM_XPORT_OP_BIND:
1123
0
          xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam);
1124
0
          return PHP_STREAM_OPTION_RETURN_OK;
1125
1126
1127
0
        case STREAM_XPORT_OP_ACCEPT:
1128
0
          xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC);
1129
0
          return PHP_STREAM_OPTION_RETURN_OK;
1130
0
        default:
1131
          /* fall through */
1132
0
          ;
1133
0
      }
1134
0
  }
1135
0
  return php_sockop_set_option(stream, option, value, ptrparam);
1136
0
}
1137
1138
1139
PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, size_t protolen,
1140
    const char *resourcename, size_t resourcenamelen,
1141
    const char *persistent_id, int options, int flags,
1142
    struct timeval *timeout,
1143
    php_stream_context *context STREAMS_DC)
1144
0
{
1145
0
  php_stream *stream = NULL;
1146
0
  php_netstream_data_t *sock;
1147
0
  const php_stream_ops *ops;
1148
1149
  /* which type of socket ? */
1150
0
  if (strncmp(proto, "tcp", protolen) == 0) {
1151
0
    ops = &php_stream_socket_ops;
1152
0
  } else if (strncmp(proto, "udp", protolen) == 0) {
1153
0
    ops = &php_stream_udp_socket_ops;
1154
0
  }
1155
0
#ifdef AF_UNIX
1156
0
  else if (strncmp(proto, "unix", protolen) == 0) {
1157
0
    ops = &php_stream_unix_socket_ops;
1158
0
  } else if (strncmp(proto, "udg", protolen) == 0) {
1159
0
    ops = &php_stream_unixdg_socket_ops;
1160
0
  }
1161
0
#endif
1162
0
  else {
1163
    /* should never happen */
1164
0
    return NULL;
1165
0
  }
1166
1167
0
  sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
1168
0
  memset(sock, 0, sizeof(php_netstream_data_t));
1169
1170
0
  sock->is_blocked = true;
1171
0
  sock->timeout.tv_sec = FG(default_socket_timeout);
1172
0
  sock->timeout.tv_usec = 0;
1173
1174
  /* we don't know the socket until we have determined if we are binding or
1175
   * connecting */
1176
0
  sock->socket = -1;
1177
1178
0
  stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
1179
1180
0
  if (stream == NULL) {
1181
0
    pefree(sock, persistent_id ? 1 : 0);
1182
0
    return NULL;
1183
0
  }
1184
1185
0
  return stream;
1186
0
}