From a18b96ed5f1a010d990f44bd924261d204af4185 Mon Sep 17 00:00:00 2001 From: Bastian Schneider Date: Tue, 5 Dec 2017 12:42:13 +0100 Subject: [PATCH] #776 - socket_recvfrom / socket_sendto support --- classes/socket.h | 51 ++++++++++++++ src/socket.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) diff --git a/classes/socket.h b/classes/socket.h index 8cca35ca..090054a7 100644 --- a/classes/socket.h +++ b/classes/socket.h @@ -31,6 +31,8 @@ PHP_METHOD(Socket, select); PHP_METHOD(Socket, read); PHP_METHOD(Socket, write); PHP_METHOD(Socket, send); +PHP_METHOD(Socket, recvfrom); +PHP_METHOD(Socket, sendto); PHP_METHOD(Socket, setBlocking); PHP_METHOD(Socket, getPeerName); @@ -85,6 +87,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_send, 0, 3, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(Socket_recvfrom, 0, 0, 4) + ZEND_ARG_TYPE_INFO(1, buffer, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(1, name, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(1, port, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(Socket_sendto, 0, 0, 4) + ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, addr, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_setBlocking, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, blocking, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -124,6 +142,8 @@ zend_function_entry pthreads_socket_methods[] = { PHP_ME(Socket, read, Socket_read, ZEND_ACC_PUBLIC) PHP_ME(Socket, write, Socket_write, ZEND_ACC_PUBLIC) PHP_ME(Socket, send, Socket_send, ZEND_ACC_PUBLIC) + PHP_ME(Socket, recvfrom, Socket_recvfrom, ZEND_ACC_PUBLIC) + PHP_ME(Socket, sendto, Socket_sendto, ZEND_ACC_PUBLIC) PHP_ME(Socket, setBlocking, Socket_setBlocking, ZEND_ACC_PUBLIC) PHP_ME(Socket, getPeerName, Socket_getHost, ZEND_ACC_PUBLIC) PHP_ME(Socket, getSockName, Socket_getHost, ZEND_ACC_PUBLIC) @@ -270,6 +290,37 @@ PHP_METHOD(Socket, send) { pthreads_socket_send(getThis(), buffer, length, flags, return_value); } /* }}} */ +/* {{{ proto bool Socket::recvfrom(string &buf, int length, int flags, string &name [, int &port ]) */ +PHP_METHOD(Socket, recvfrom) { + zval *buffer, *name, *port = NULL; + zend_long len, flags; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/llz/|z/", &buffer, &len, &flags, &name, &port) == FAILURE) { + return; + } + + /* overflow check */ + if ((len + 2) < 3) { + RETURN_FALSE; + } + + pthreads_socket_recvfrom(getThis(), buffer, len, flags, name, port, return_value); +} /* }}} */ + +/* {{{ proto bool Socket::sendto(string buf, int length, int flags, string addr [, int port ]) */ +PHP_METHOD(Socket, sendto) { + size_t buf_len, addr_len; + zend_long len, flags, port = 0; + char *buf, *addr; + int argc = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(argc, "slls|l", &buf, &buf_len, &len, &flags, &addr, &addr_len, &port) == FAILURE) { + return; + } + + pthreads_socket_sendto(getThis(), argc, buf, buf_len, len, flags, addr, addr_len, port, return_value); +} /* }}} */ + /* {{{ proto bool Socket::setBlocking(bool blocking) */ PHP_METHOD(Socket, setBlocking) { zend_bool blocking = 0; diff --git a/src/socket.c b/src/socket.c index cc37adbf..04f68cb3 100644 --- a/src/socket.c +++ b/src/socket.c @@ -696,4 +696,179 @@ void pthreads_socket_free(pthreads_socket_t *socket, zend_bool closing) { efree(socket); } + +void pthreads_socket_recvfrom(zval *object, zval *buffer, zend_long len, zend_long flags, zval *name, zval *port, zval *return_value) { + pthreads_object_t *threaded = + PTHREADS_FETCH_FROM(Z_OBJ_P(object)); + + struct sockaddr_un s_un; + struct sockaddr_in sin; +#if HAVE_IPV6 + struct sockaddr_in6 sin6; + char addr6[INET6_ADDRSTRLEN]; +#endif + socklen_t slen; + int retval; + char *address; + zend_string *recv_buf; + + recv_buf = zend_string_alloc(len + 1, 0); + + switch (threaded->store.sock->type) { + case AF_UNIX: + slen = sizeof(s_un); + s_un.sun_family = AF_UNIX; + retval = recvfrom(threaded->store.sock->fd, ZSTR_VAL(recv_buf), len, flags, (struct sockaddr *)&s_un, (socklen_t *)&slen); + + if (retval < 0) { + PHP_SOCKET_ERROR(threaded->store.sock, "unable to recvfrom", errno); + zend_string_free(recv_buf); + RETURN_FALSE; + } + ZSTR_LEN(recv_buf) = retval; + ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0'; + + zval_dtor(buffer); + zval_dtor(name); + + ZVAL_NEW_STR(buffer, recv_buf); + ZVAL_STRING(name, s_un.sun_path); + break; + + case AF_INET: + slen = sizeof(sin); + memset(&sin, 0, slen); + sin.sin_family = AF_INET; + + if (port == NULL) { + zend_string_free(recv_buf); + WRONG_PARAM_COUNT; + } + + retval = recvfrom(threaded->store.sock->fd, ZSTR_VAL(recv_buf), len, flags, (struct sockaddr *)&sin, (socklen_t *)&slen); + + if (retval < 0) { + PHP_SOCKET_ERROR(threaded->store.sock, "unable to recvfrom", errno); + zend_string_free(recv_buf); + RETURN_FALSE; + } + ZSTR_LEN(recv_buf) = retval; + ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0'; + + zval_dtor(buffer); + zval_dtor(name); + zval_dtor(port); + + address = inet_ntoa(sin.sin_addr); + + ZVAL_NEW_STR(buffer, recv_buf); + ZVAL_STRING(name, address ? address : "0.0.0.0"); + ZVAL_LONG(port, ntohs(sin.sin_port)); + break; +#if HAVE_IPV6 + case AF_INET6: + slen = sizeof(sin6); + memset(&sin6, 0, slen); + sin6.sin6_family = AF_INET6; + + if (port == NULL) { + efree(recv_buf); + WRONG_PARAM_COUNT; + } + + retval = recvfrom(threaded->store.sock->fd, ZSTR_VAL(recv_buf), len, flags, (struct sockaddr *)&sin6, (socklen_t *)&slen); + + if (retval < 0) { + PHP_SOCKET_ERROR(threaded->store.sock, "unable to recvfrom", errno); + zend_string_free(recv_buf); + RETURN_FALSE; + } + ZSTR_LEN(recv_buf) = retval; + ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0'; + + zval_dtor(buffer); + zval_dtor(name); + zval_dtor(port); + + memset(addr6, 0, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &sin6.sin6_addr, addr6, INET6_ADDRSTRLEN); + + ZVAL_NEW_STR(buffer, recv_buf); + ZVAL_STRING(name, addr6[0] ? addr6 : "::"); + ZVAL_LONG(port, ntohs(sin6.sin6_port)); + break; +#endif + default: + php_error_docref(NULL, E_WARNING, "Unsupported socket type %d", threaded->store.sock->type); + RETURN_FALSE; + } + + RETURN_LONG(retval); +} + +void pthreads_socket_sendto(zval *object, int argc, char *buf, size_t buf_len, zend_long len, zend_long flags, char *addr, size_t addr_len, zend_long port, zval *return_value) { + pthreads_object_t *threaded = + PTHREADS_FETCH_FROM(Z_OBJ_P(object)); + + struct sockaddr_un s_un; + struct sockaddr_in sin; +#if HAVE_IPV6 + struct sockaddr_in6 sin6; + char addr6[INET6_ADDRSTRLEN]; +#endif + int retval; + + switch (threaded->store.sock->type) { + case AF_UNIX: + memset(&s_un, 0, sizeof(s_un)); + s_un.sun_family = AF_UNIX; + snprintf(s_un.sun_path, 108, "%s", addr); + + retval = sendto(threaded->store.sock->fd, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &s_un, SUN_LEN(&s_un)); + break; + + case AF_INET: + if (argc != 6) { + WRONG_PARAM_COUNT; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons((unsigned short) port); + + if (! php_set_inet_addr(&sin, addr, threaded->store.sock)) { + RETURN_FALSE; + } + + retval = sendto(threaded->store.sock->fd, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin)); + break; +#if HAVE_IPV6 + case AF_INET6: + if (argc != 6) { + WRONG_PARAM_COUNT; + } + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons((unsigned short) port); + + if (! php_set_inet6_addr(&sin6, addr, threaded->store.sock)) { + RETURN_FALSE; + } + + retval = sendto(threaded->store.sock->fd, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6)); + break; +#endif + default: + php_error_docref(NULL, E_WARNING, "Unsupported socket type %d", threaded->store.sock->type); + RETURN_FALSE; + } + + if (retval == -1) { + PHP_SOCKET_ERROR(threaded->store.sock, "unable to write to socket", errno); + RETURN_FALSE; + } + + RETURN_LONG(retval); +} #endif