123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 |
- /*
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | https://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Author: Wez Furlong <wez@thebrainroom.com> |
- +----------------------------------------------------------------------+
- */
- #include "php.h"
- #include "ext/standard/file.h"
- #include "streams/php_streams_int.h"
- #include "php_network.h"
- #if defined(PHP_WIN32) || defined(__riscos__)
- # undef AF_UNIX
- #endif
- #ifdef AF_UNIX
- #include <sys/un.h>
- #endif
- #ifndef MSG_DONTWAIT
- # define MSG_DONTWAIT 0
- #endif
- #ifndef MSG_PEEK
- # define MSG_PEEK 0
- #endif
- #ifdef PHP_WIN32
- /* send/recv family on windows expects int */
- # define XP_SOCK_BUF_SIZE(sz) (((sz) > INT_MAX) ? INT_MAX : (int)(sz))
- #else
- # define XP_SOCK_BUF_SIZE(sz) (sz)
- #endif
- const php_stream_ops php_stream_generic_socket_ops;
- PHPAPI const php_stream_ops php_stream_socket_ops;
- const php_stream_ops php_stream_udp_socket_ops;
- #ifdef AF_UNIX
- const php_stream_ops php_stream_unix_socket_ops;
- const php_stream_ops php_stream_unixdg_socket_ops;
- #endif
- static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam);
- /* {{{ Generic socket stream operations */
- static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t count)
- {
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- ssize_t didwrite;
- struct timeval *ptimeout;
- if (!sock || sock->socket == -1) {
- return 0;
- }
- if (sock->timeout.tv_sec == -1)
- ptimeout = NULL;
- else
- ptimeout = &sock->timeout;
- retry:
- didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
- if (didwrite <= 0) {
- char *estr;
- int err = php_socket_errno();
- if (PHP_IS_TRANSIENT_ERROR(err)) {
- if (sock->is_blocked) {
- int retval;
- sock->timeout_event = 0;
- do {
- retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
- if (retval == 0) {
- sock->timeout_event = 1;
- break;
- }
- if (retval > 0) {
- /* writable now; retry */
- goto retry;
- }
- err = php_socket_errno();
- } while (err == EINTR);
- } else {
- /* EWOULDBLOCK/EAGAIN is not an error for a non-blocking stream.
- * Report zero byte write instead. */
- return 0;
- }
- }
- if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
- estr = php_socket_strerror(err, NULL, 0);
- php_error_docref(NULL, E_NOTICE,
- "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
- (zend_long)count, err, estr);
- efree(estr);
- }
- }
- if (didwrite > 0) {
- php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);
- }
- return didwrite;
- }
- static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock)
- {
- int retval;
- struct timeval *ptimeout;
- if (!sock || sock->socket == -1) {
- return;
- }
- sock->timeout_event = 0;
- if (sock->timeout.tv_sec == -1)
- ptimeout = NULL;
- else
- ptimeout = &sock->timeout;
- while(1) {
- retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
- if (retval == 0)
- sock->timeout_event = 1;
- if (retval >= 0)
- break;
- if (php_socket_errno() != EINTR)
- break;
- }
- }
- static ssize_t php_sockop_read(php_stream *stream, char *buf, size_t count)
- {
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- ssize_t nr_bytes = 0;
- int err;
- if (!sock || sock->socket == -1) {
- return -1;
- }
- if (sock->is_blocked) {
- php_sock_stream_wait_for_data(stream, sock);
- if (sock->timeout_event)
- return -1;
- }
- nr_bytes = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && sock->timeout.tv_sec != -1) ? MSG_DONTWAIT : 0);
- err = php_socket_errno();
- if (nr_bytes < 0) {
- if (PHP_IS_TRANSIENT_ERROR(err)) {
- nr_bytes = 0;
- } else {
- stream->eof = 1;
- }
- } else if (nr_bytes == 0) {
- stream->eof = 1;
- }
- if (nr_bytes > 0) {
- php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);
- }
- return nr_bytes;
- }
- static int php_sockop_close(php_stream *stream, int close_handle)
- {
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- #ifdef PHP_WIN32
- int n;
- #endif
- if (!sock) {
- return 0;
- }
- if (close_handle) {
- #ifdef PHP_WIN32
- if (sock->socket == -1)
- sock->socket = SOCK_ERR;
- #endif
- if (sock->socket != SOCK_ERR) {
- #ifdef PHP_WIN32
- /* prevent more data from coming in */
- shutdown(sock->socket, SHUT_RD);
- /* try to make sure that the OS sends all data before we close the connection.
- * Essentially, we are waiting for the socket to become writeable, which means
- * that all pending data has been sent.
- * We use a small timeout which should encourage the OS to send the data,
- * but at the same time avoid hanging indefinitely.
- * */
- do {
- n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
- } while (n == -1 && php_socket_errno() == EINTR);
- #endif
- closesocket(sock->socket);
- sock->socket = SOCK_ERR;
- }
- }
- pefree(sock, php_stream_is_persistent(stream));
- return 0;
- }
- static int php_sockop_flush(php_stream *stream)
- {
- #if 0
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- return fsync(sock->socket);
- #endif
- return 0;
- }
- static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb)
- {
- #ifdef ZEND_WIN32
- return 0;
- #else
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- return zend_fstat(sock->socket, &ssb->sb);
- #endif
- }
- static inline int sock_sendto(php_netstream_data_t *sock, const char *buf, size_t buflen, int flags,
- struct sockaddr *addr, socklen_t addrlen
- )
- {
- int ret;
- if (addr) {
- ret = sendto(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, addr, XP_SOCK_BUF_SIZE(addrlen));
- return (ret == SOCK_CONN_ERR) ? -1 : ret;
- }
- #ifdef PHP_WIN32
- return ((ret = send(sock->socket, buf, buflen > INT_MAX ? INT_MAX : (int)buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
- #else
- return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
- #endif
- }
- static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
- zend_string **textaddr,
- struct sockaddr **addr, socklen_t *addrlen
- )
- {
- int ret;
- int want_addr = textaddr || addr;
- if (want_addr) {
- php_sockaddr_storage sa;
- socklen_t sl = sizeof(sa);
- ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl);
- ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
- #ifdef PHP_WIN32
- /* POSIX discards excess bytes without signalling failure; emulate this on Windows */
- if (ret == -1 && WSAGetLastError() == WSAEMSGSIZE) {
- ret = buflen;
- }
- #endif
- if (sl) {
- php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
- textaddr, addr, addrlen);
- } else {
- if (textaddr) {
- *textaddr = ZSTR_EMPTY_ALLOC();
- }
- if (addr) {
- *addr = NULL;
- *addrlen = 0;
- }
- }
- } else {
- ret = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags);
- ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
- }
- return ret;
- }
- static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
- {
- int oldmode, flags;
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- php_stream_xport_param *xparam;
- if (!sock) {
- return PHP_STREAM_OPTION_RETURN_NOTIMPL;
- }
- switch(option) {
- case PHP_STREAM_OPTION_CHECK_LIVENESS:
- {
- struct timeval tv;
- char buf;
- int alive = 1;
- if (value == -1) {
- if (sock->timeout.tv_sec == -1) {
- tv.tv_sec = FG(default_socket_timeout);
- tv.tv_usec = 0;
- } else {
- tv = sock->timeout;
- }
- } else {
- tv.tv_sec = value;
- tv.tv_usec = 0;
- }
- if (sock->socket == -1) {
- alive = 0;
- } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
- #ifdef PHP_WIN32
- int ret;
- #else
- ssize_t ret;
- #endif
- int err;
- ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK);
- err = php_socket_errno();
- if (0 == ret || /* the counterpart did properly shutdown*/
- (0 > ret && err != EWOULDBLOCK && err != EAGAIN && err != EMSGSIZE)) { /* there was an unrecoverable error */
- alive = 0;
- }
- }
- return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
- }
- case PHP_STREAM_OPTION_BLOCKING:
- oldmode = sock->is_blocked;
- if (SUCCESS == php_set_sock_blocking(sock->socket, value)) {
- sock->is_blocked = value;
- return oldmode;
- }
- return PHP_STREAM_OPTION_RETURN_ERR;
- case PHP_STREAM_OPTION_READ_TIMEOUT:
- sock->timeout = *(struct timeval*)ptrparam;
- sock->timeout_event = 0;
- return PHP_STREAM_OPTION_RETURN_OK;
- case PHP_STREAM_OPTION_META_DATA_API:
- add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
- add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
- add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
- return PHP_STREAM_OPTION_RETURN_OK;
- case PHP_STREAM_OPTION_XPORT_API:
- xparam = (php_stream_xport_param *)ptrparam;
- switch (xparam->op) {
- case STREAM_XPORT_OP_LISTEN:
- xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ? 0: -1;
- return PHP_STREAM_OPTION_RETURN_OK;
- case STREAM_XPORT_OP_GET_NAME:
- xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
- xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
- xparam->want_addr ? &xparam->outputs.addr : NULL,
- xparam->want_addr ? &xparam->outputs.addrlen : NULL
- );
- return PHP_STREAM_OPTION_RETURN_OK;
- case STREAM_XPORT_OP_GET_PEER_NAME:
- xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
- xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
- xparam->want_addr ? &xparam->outputs.addr : NULL,
- xparam->want_addr ? &xparam->outputs.addrlen : NULL
- );
- return PHP_STREAM_OPTION_RETURN_OK;
- case STREAM_XPORT_OP_SEND:
- flags = 0;
- if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
- flags |= MSG_OOB;
- }
- xparam->outputs.returncode = sock_sendto(sock,
- xparam->inputs.buf, xparam->inputs.buflen,
- flags,
- xparam->inputs.addr,
- xparam->inputs.addrlen);
- if (xparam->outputs.returncode == -1) {
- char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
- php_error_docref(NULL, E_WARNING,
- "%s\n", err);
- efree(err);
- }
- return PHP_STREAM_OPTION_RETURN_OK;
- case STREAM_XPORT_OP_RECV:
- flags = 0;
- if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
- flags |= MSG_OOB;
- }
- if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
- flags |= MSG_PEEK;
- }
- xparam->outputs.returncode = sock_recvfrom(sock,
- xparam->inputs.buf, xparam->inputs.buflen,
- flags,
- xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
- xparam->want_addr ? &xparam->outputs.addr : NULL,
- xparam->want_addr ? &xparam->outputs.addrlen : NULL
- );
- return PHP_STREAM_OPTION_RETURN_OK;
- #ifdef HAVE_SHUTDOWN
- # ifndef SHUT_RD
- # define SHUT_RD 0
- # endif
- # ifndef SHUT_WR
- # define SHUT_WR 1
- # endif
- # ifndef SHUT_RDWR
- # define SHUT_RDWR 2
- # endif
- case STREAM_XPORT_OP_SHUTDOWN: {
- static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
- xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
- return PHP_STREAM_OPTION_RETURN_OK;
- }
- #endif
- default:
- break;
- }
- }
- return PHP_STREAM_OPTION_RETURN_NOTIMPL;
- }
- static int php_sockop_cast(php_stream *stream, int castas, void **ret)
- {
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- if (!sock) {
- return FAILURE;
- }
- switch(castas) {
- case PHP_STREAM_AS_STDIO:
- if (ret) {
- *(FILE**)ret = fdopen(sock->socket, stream->mode);
- if (*ret)
- return SUCCESS;
- return FAILURE;
- }
- return SUCCESS;
- case PHP_STREAM_AS_FD_FOR_SELECT:
- case PHP_STREAM_AS_FD:
- case PHP_STREAM_AS_SOCKETD:
- if (ret)
- *(php_socket_t *)ret = sock->socket;
- return SUCCESS;
- default:
- return FAILURE;
- }
- }
- /* }}} */
- /* These may look identical, but we need them this way so that
- * we can determine which type of socket we are dealing with
- * by inspecting stream->ops.
- * A "useful" side-effect is that the user's scripts can then
- * make similar decisions using stream_get_meta_data.
- * */
- const php_stream_ops php_stream_generic_socket_ops = {
- php_sockop_write, php_sockop_read,
- php_sockop_close, php_sockop_flush,
- "generic_socket",
- NULL, /* seek */
- php_sockop_cast,
- php_sockop_stat,
- php_sockop_set_option,
- };
- const php_stream_ops php_stream_socket_ops = {
- php_sockop_write, php_sockop_read,
- php_sockop_close, php_sockop_flush,
- "tcp_socket",
- NULL, /* seek */
- php_sockop_cast,
- php_sockop_stat,
- php_tcp_sockop_set_option,
- };
- const php_stream_ops php_stream_udp_socket_ops = {
- php_sockop_write, php_sockop_read,
- php_sockop_close, php_sockop_flush,
- "udp_socket",
- NULL, /* seek */
- php_sockop_cast,
- php_sockop_stat,
- php_tcp_sockop_set_option,
- };
- #ifdef AF_UNIX
- const php_stream_ops php_stream_unix_socket_ops = {
- php_sockop_write, php_sockop_read,
- php_sockop_close, php_sockop_flush,
- "unix_socket",
- NULL, /* seek */
- php_sockop_cast,
- php_sockop_stat,
- php_tcp_sockop_set_option,
- };
- const php_stream_ops php_stream_unixdg_socket_ops = {
- php_sockop_write, php_sockop_read,
- php_sockop_close, php_sockop_flush,
- "udg_socket",
- NULL, /* seek */
- php_sockop_cast,
- php_sockop_stat,
- php_tcp_sockop_set_option,
- };
- #endif
- /* network socket operations */
- #ifdef AF_UNIX
- static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr)
- {
- memset(unix_addr, 0, sizeof(*unix_addr));
- unix_addr->sun_family = AF_UNIX;
- /* we need to be binary safe on systems that support an abstract
- * namespace */
- if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
- /* On linux, when the path begins with a NUL byte we are
- * referring to an abstract namespace. In theory we should
- * allow an extra byte below, since we don't need the NULL.
- * BUT, to get into this branch of code, the name is too long,
- * so we don't care. */
- xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
- php_error_docref(NULL, E_NOTICE,
- "socket path exceeded the maximum allowed length of %lu bytes "
- "and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
- }
- memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
- return 1;
- }
- #endif
- static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err)
- {
- char *colon;
- char *host = NULL;
- #ifdef HAVE_IPV6
- char *p;
- if (*(str) == '[' && str_len > 1) {
- /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
- p = memchr(str + 1, ']', str_len - 2);
- if (!p || *(p + 1) != ':') {
- if (get_err) {
- *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);
- }
- return NULL;
- }
- *portno = atoi(p + 2);
- return estrndup(str + 1, p - str - 1);
- }
- #endif
- if (str_len) {
- colon = memchr(str, ':', str_len - 1);
- } else {
- colon = NULL;
- }
- if (colon) {
- *portno = atoi(colon + 1);
- host = estrndup(str, colon - str);
- } else {
- if (get_err) {
- *err = strpprintf(0, "Failed to parse address \"%s\"", str);
- }
- return NULL;
- }
- return host;
- }
- static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno)
- {
- return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text);
- }
- static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
- php_stream_xport_param *xparam)
- {
- char *host = NULL;
- int portno, err;
- long sockopts = STREAM_SOCKOP_NONE;
- zval *tmpzval = NULL;
- #ifdef AF_UNIX
- if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
- struct sockaddr_un unix_addr;
- sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
- if (sock->socket == SOCK_ERR) {
- if (xparam->want_errortext) {
- xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s",
- stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
- strerror(errno));
- }
- return -1;
- }
- parse_unix_address(xparam, &unix_addr);
- return bind(sock->socket, (const struct sockaddr *)&unix_addr,
- (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen);
- }
- #endif
- host = parse_ip_address(xparam, &portno);
- if (host == NULL) {
- return -1;
- }
- #ifdef IPV6_V6ONLY
- if (PHP_STREAM_CONTEXT(stream)
- && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "ipv6_v6only")) != NULL
- && Z_TYPE_P(tmpzval) != IS_NULL
- ) {
- sockopts |= STREAM_SOCKOP_IPV6_V6ONLY;
- sockopts |= STREAM_SOCKOP_IPV6_V6ONLY_ENABLED * zend_is_true(tmpzval);
- }
- #endif
- #ifdef SO_REUSEPORT
- if (PHP_STREAM_CONTEXT(stream)
- && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_reuseport")) != NULL
- && zend_is_true(tmpzval)
- ) {
- sockopts |= STREAM_SOCKOP_SO_REUSEPORT;
- }
- #endif
- #ifdef SO_BROADCAST
- if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
- && PHP_STREAM_CONTEXT(stream)
- && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
- && zend_is_true(tmpzval)
- ) {
- sockopts |= STREAM_SOCKOP_SO_BROADCAST;
- }
- #endif
- sock->socket = php_network_bind_socket_to_local_addr(host, portno,
- stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
- sockopts,
- xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &err
- );
- if (host) {
- efree(host);
- }
- return sock->socket == -1 ? -1 : 0;
- }
- static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
- php_stream_xport_param *xparam)
- {
- char *host = NULL, *bindto = NULL;
- int portno, bindport = 0;
- int err = 0;
- int ret;
- zval *tmpzval = NULL;
- long sockopts = STREAM_SOCKOP_NONE;
- #ifdef AF_UNIX
- if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
- struct sockaddr_un unix_addr;
- sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
- if (sock->socket == SOCK_ERR) {
- if (xparam->want_errortext) {
- xparam->outputs.error_text = strpprintf(0, "Failed to create unix socket");
- }
- return -1;
- }
- parse_unix_address(xparam, &unix_addr);
- ret = php_network_connect_socket(sock->socket,
- (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
- xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
- xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &err);
- xparam->outputs.error_code = err;
- goto out;
- }
- #endif
- host = parse_ip_address(xparam, &portno);
- if (host == NULL) {
- return -1;
- }
- if (PHP_STREAM_CONTEXT(stream) && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "bindto")) != NULL) {
- if (Z_TYPE_P(tmpzval) != IS_STRING) {
- if (xparam->want_errortext) {
- xparam->outputs.error_text = strpprintf(0, "local_addr context option is not a string.");
- }
- efree(host);
- return -1;
- }
- bindto = parse_ip_address_ex(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text);
- }
- #ifdef SO_BROADCAST
- if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
- && PHP_STREAM_CONTEXT(stream)
- && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
- && zend_is_true(tmpzval)
- ) {
- sockopts |= STREAM_SOCKOP_SO_BROADCAST;
- }
- #endif
- if (stream->ops != &php_stream_udp_socket_ops /* TCP_NODELAY is only applicable for TCP */
- #ifdef AF_UNIX
- && stream->ops != &php_stream_unix_socket_ops
- && stream->ops != &php_stream_unixdg_socket_ops
- #endif
- && PHP_STREAM_CONTEXT(stream)
- && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
- && zend_is_true(tmpzval)
- ) {
- sockopts |= STREAM_SOCKOP_TCP_NODELAY;
- }
- /* Note: the test here for php_stream_udp_socket_ops is important, because we
- * want the default to be TCP sockets so that the openssl extension can
- * re-use this code. */
- sock->socket = php_network_connect_socket_to_host(host, portno,
- stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
- xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
- xparam->inputs.timeout,
- xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &err,
- bindto,
- bindport,
- sockopts
- );
- ret = sock->socket == -1 ? -1 : 0;
- xparam->outputs.error_code = err;
- if (host) {
- efree(host);
- }
- if (bindto) {
- efree(bindto);
- }
- #ifdef AF_UNIX
- out:
- #endif
- if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
- /* indicates pending connection */
- return 1;
- }
- return ret;
- }
- static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
- php_stream_xport_param *xparam STREAMS_DC)
- {
- int clisock;
- bool nodelay = 0;
- zval *tmpzval = NULL;
- xparam->outputs.client = NULL;
- if ((NULL != PHP_STREAM_CONTEXT(stream)) &&
- (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
- zend_is_true(tmpzval)) {
- nodelay = 1;
- }
- clisock = php_network_accept_incoming(sock->socket,
- xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
- xparam->want_addr ? &xparam->outputs.addr : NULL,
- xparam->want_addr ? &xparam->outputs.addrlen : NULL,
- xparam->inputs.timeout,
- xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &xparam->outputs.error_code,
- nodelay);
- if (clisock >= 0) {
- php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));
- memcpy(clisockdata, sock, sizeof(*clisockdata));
- clisockdata->socket = clisock;
- #ifdef __linux__
- /* O_NONBLOCK is not inherited on Linux */
- clisockdata->is_blocked = 1;
- #endif
- xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
- if (xparam->outputs.client) {
- xparam->outputs.client->ctx = stream->ctx;
- if (stream->ctx) {
- GC_ADDREF(stream->ctx);
- }
- }
- }
- return xparam->outputs.client == NULL ? -1 : 0;
- }
- static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
- {
- php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
- php_stream_xport_param *xparam;
- switch(option) {
- case PHP_STREAM_OPTION_XPORT_API:
- xparam = (php_stream_xport_param *)ptrparam;
- switch(xparam->op) {
- case STREAM_XPORT_OP_CONNECT:
- case STREAM_XPORT_OP_CONNECT_ASYNC:
- xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam);
- return PHP_STREAM_OPTION_RETURN_OK;
- case STREAM_XPORT_OP_BIND:
- xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam);
- return PHP_STREAM_OPTION_RETURN_OK;
- case STREAM_XPORT_OP_ACCEPT:
- xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC);
- return PHP_STREAM_OPTION_RETURN_OK;
- default:
- /* fall through */
- ;
- }
- }
- return php_sockop_set_option(stream, option, value, ptrparam);
- }
- PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, size_t protolen,
- const char *resourcename, size_t resourcenamelen,
- const char *persistent_id, int options, int flags,
- struct timeval *timeout,
- php_stream_context *context STREAMS_DC)
- {
- php_stream *stream = NULL;
- php_netstream_data_t *sock;
- const php_stream_ops *ops;
- /* which type of socket ? */
- if (strncmp(proto, "tcp", protolen) == 0) {
- ops = &php_stream_socket_ops;
- } else if (strncmp(proto, "udp", protolen) == 0) {
- ops = &php_stream_udp_socket_ops;
- }
- #ifdef AF_UNIX
- else if (strncmp(proto, "unix", protolen) == 0) {
- ops = &php_stream_unix_socket_ops;
- } else if (strncmp(proto, "udg", protolen) == 0) {
- ops = &php_stream_unixdg_socket_ops;
- }
- #endif
- else {
- /* should never happen */
- return NULL;
- }
- sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
- memset(sock, 0, sizeof(php_netstream_data_t));
- sock->is_blocked = 1;
- sock->timeout.tv_sec = FG(default_socket_timeout);
- sock->timeout.tv_usec = 0;
- /* we don't know the socket until we have determined if we are binding or
- * connecting */
- sock->socket = -1;
- stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
- if (stream == NULL) {
- pefree(sock, persistent_id ? 1 : 0);
- return NULL;
- }
- if (flags == 0) {
- return stream;
- }
- return stream;
- }
|