diff options
-rw-r--r-- | ext/sockets/php_sockets.h | 18 | ||||
-rw-r--r-- | ext/sockets/sockets.c | 1103 |
2 files changed, 1037 insertions, 84 deletions
diff --git a/ext/sockets/php_sockets.h b/ext/sockets/php_sockets.h index 41fd1f2b73..832ad01d7d 100644 --- a/ext/sockets/php_sockets.h +++ b/ext/sockets/php_sockets.h @@ -42,7 +42,6 @@ PHP_RINIT_FUNCTION(sockets); PHP_RSHUTDOWN_FUNCTION(sockets); PHP_MINFO_FUNCTION(sockets); -PHP_FUNCTION(confirm_sockets_compiled); /* For testing, remove later. */ PHP_FUNCTION(confirm_sockets_compiled); PHP_FUNCTION(fd_alloc); PHP_FUNCTION(fd_dealloc); @@ -58,13 +57,28 @@ PHP_FUNCTION(listen); PHP_FUNCTION(close); PHP_FUNCTION(write); PHP_FUNCTION(read); -PHP_FUNCTION(signal); PHP_FUNCTION(getsockname); PHP_FUNCTION(getpeername); PHP_FUNCTION(socket); PHP_FUNCTION(connect); PHP_FUNCTION(strerror); PHP_FUNCTION(bind); +PHP_FUNCTION(recv); +PHP_FUNCTION(send); +PHP_FUNCTION(recvfrom); +PHP_FUNCTION(sendto); +PHP_FUNCTION(build_iovec); +PHP_FUNCTION(fetch_iovec); +PHP_FUNCTION(free_iovec); +PHP_FUNCTION(add_iovec); +PHP_FUNCTION(delete_iovec); +PHP_FUNCTION(set_iovec); +PHP_FUNCTION(recvmsg); +PHP_FUNCTION(sendmsg); +PHP_FUNCTION(readv); +PHP_FUNCTION(writev); +PHP_FUNCTION(getsockopt); +PHP_FUNCTION(setsockopt); /* Fill in this structure and use entries in it for thread safety instead of using true globals. diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index ef513cf4a7..7e7eeb8e81 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -23,6 +23,8 @@ #include "php_ini.h" #include "php_sockets.h" +#include "../standard/info.h" + #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> @@ -35,6 +37,7 @@ #include <errno.h> #include <fcntl.h> #include <signal.h> +#include <sys/uio.h> /* You should tweak config.m4 so this symbol (or some else suitable) gets defined. @@ -43,8 +46,10 @@ #ifdef ZTS int sockets_globals_id; + #else php_sockets_globals sockets_globals; + #endif #define ZVAL(arg, type) ((*(arg))->value.type) @@ -66,11 +71,19 @@ void v_convert_to_long_ex(int items,...) va_end(ap); } - /* *INDENT-OFF* */ static unsigned char second_and_third_args_force_ref[] = {3, BYREF_NONE, BYREF_FORCE, BYREF_FORCE}; +static unsigned char second_fifth_and_sixth_args_force_ref[] = +{6, BYREF_NONE, BYREF_FORCE, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE}; + +static unsigned char fourth_arg_force_ref[] = +{4, BYREF_NONE, BYREF_NONE, BYREF_NONE, BYREF_FORCE}; + +static unsigned char third_through_seventh_args_force_ref[] = +{7, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE}; + /* Every user visible function must have an entry in sockets_functions[]. */ function_entry sockets_functions[] = @@ -105,11 +118,30 @@ function_entry sockets_functions[] = PHP_FE(connect, NULL) /* OK */ PHP_FE(strerror, NULL) /* OK */ PHP_FE(bind, NULL) + PHP_FE(recv, second_arg_force_ref) + PHP_FE(send, NULL) + PHP_FE(recvfrom, second_fifth_and_sixth_args_force_ref) + PHP_FE(sendto, NULL) + PHP_FE(build_iovec, NULL) + PHP_FE(fetch_iovec, NULL) + PHP_FE(free_iovec, NULL) + PHP_FE(add_iovec, NULL) + PHP_FE(delete_iovec, NULL) + PHP_FE(set_iovec, NULL) + PHP_FE(recvmsg, third_through_seventh_args_force_ref) + PHP_FE(sendmsg, NULL) + PHP_FE(readv, NULL) + PHP_FE(writev, NULL) + PHP_FE(getsockopt, fourth_arg_force_ref) + PHP_FE(setsockopt, NULL) {NULL, NULL, NULL} /* Must be the last line in sockets_functions[] */ }; /* *INDENT-ON* */ + + + zend_module_entry sockets_module_entry = { "sockets", @@ -123,6 +155,7 @@ zend_module_entry sockets_module_entry = }; int le_destroy; +int le_iov; #ifdef COMPILE_DL_SOCKETS ZEND_GET_MODULE(sockets) @@ -133,17 +166,75 @@ ZEND_GET_MODULE(sockets) PHP_INI_END() */ +typedef struct php_iovec { + struct iovec *iov_array; + unsigned int count; +} php_iovec_t; + static void destroy_fd_sets(fd_set * set) { efree(set); } +static void destroy_iovec(php_iovec_t * iov) +{ + int i; + + if (iov->count && iov->iov_array) { + for (i = 0; i < iov->count; i++) { + efree(iov->iov_array[i].iov_base); + } + + efree(iov->iov_array); + efree(iov); + } +} + PHP_MINIT_FUNCTION(sockets) { /* Remove comments if you have entries in php.ini REGISTER_INI_ENTRIES(); */ le_destroy = register_list_destructors(destroy_fd_sets, NULL); + le_iov = register_list_destructors(destroy_iovec, NULL); + + REGISTER_LONG_CONSTANT("AF_UNIX", AF_UNIX, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("AF_INET", AF_INET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_STREAM", SOCK_STREAM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_DGRAM", SOCK_DGRAM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_RAW", SOCK_RAW, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_SEQPACKET", SOCK_SEQPACKET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_RDM", SOCK_RDM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_OOB", MSG_OOB, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_WAITALL", MSG_WAITALL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_PEEK", MSG_PEEK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_DONTROUTE", MSG_DONTROUTE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_DEBUG", SO_DEBUG, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_REUSEADDR", SO_REUSEADDR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_KEEPALIVE", SO_KEEPALIVE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_DONTROUTE", SO_DONTROUTE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_LINGER", SO_LINGER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_BROADCAST", SO_BROADCAST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_OOBINLINE", SO_OOBINLINE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_SNDBUF", SO_SNDBUF, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_RCVBUF", SO_RCVBUF, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_SNDLOWAT", SO_SNDLOWAT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_RCVLOWAT", SO_RCVLOWAT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_SNDTIMEO", SO_SNDTIMEO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_RCVTIMEO", SO_RCVTIMEO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_TYPE", SO_TYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_ERROR", SO_ERROR, CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("SOL_SOCKET", SOL_SOCKET, CONST_PERSISTENT); + { + struct protoent *pe; + pe = getprotobyname("tcp"); + if (pe) + REGISTER_LONG_CONSTANT("SOL_TCP", pe->p_proto, CONST_PERSISTENT); + pe = getprotobyname("udp"); + if (pe) + REGISTER_LONG_CONSTANT("SOL_UDP", pe->p_proto, CONST_PERSISTENT); + } return SUCCESS; } @@ -196,7 +287,6 @@ PHP_FUNCTION(confirm_sockets_compiled) PHP_FUNCTION(fd_alloc) { fd_set *set; - zval *new_resource; int ret; set = emalloc(sizeof(fd_set)); @@ -204,8 +294,7 @@ PHP_FUNCTION(fd_alloc) zend_error(E_ERROR, "Can't allocate memory for fd_set"); RETVAL_FALSE; } - ret = ZEND_REGISTER_RESOURCE(new_resource, set, le_destroy); - RETURN_RESOURCE(ret); + ret = ZEND_REGISTER_RESOURCE(return_value, set, le_destroy); } /* }}} */ @@ -247,7 +336,6 @@ PHP_FUNCTION(fd_clear) { zval **set, **fd; fd_set *the_set; - int ret; if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fd, &set) == FAILURE) { WRONG_PARAM_COUNT; @@ -265,12 +353,11 @@ PHP_FUNCTION(fd_clear) /* }}} */ /* {{{ proto bool fd_isset(long fd, resource set) - Check to see if a file descriptor is set within the file descriptor set */ + Check to see if a file descriptor is set within the file descrirptor set */ PHP_FUNCTION(fd_isset) { zval **set, **fd; fd_set *the_set; - int ret; if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fd, &set) == FAILURE) { WRONG_PARAM_COUNT; @@ -311,34 +398,33 @@ PHP_FUNCTION(fd_zero) /* }}} */ /* {{{ proto void select(long max_fd, resource readfds, resource writefds, resource exceptfds, long tv_sec, long tv_usec) - Runs the select() system call on the sets mentioned with a timeout specified by tv_sec and tv_usec */ -/* - See select(2) man page for details. + Runs the select() system call on the sets mentioned with a timeout specified by tv_sec and tv_usec. */ +/* See select(2) man page for details. From man page: select waits for a number of file descriptors to change status. - Three independent sets of descriptors are watched. Those in - readfds will be watched to see if characters become avaliable for - reading, those in writefds will be watched to see if it is ok to - immediately write on them, and those in exceptfds will be watched - for exceptions. On exit, the sets are modified in place to + Three independent sets of descriptors are watched. Those in + readfds will be watched to see if characters become avaliable for + reading, those in writefds will be watched to see if it is ok to + immediately write on them, and those in exceptfds will be watched + for exceptions. On exit, the sets are modified in place to indicate which descriptors actually changed status. - -1 is passed for any sets for which NULL would be passed to the - system call. + -1 is passed for any sets for which NULL would be passed to the + system call. */ PHP_FUNCTION(select) { - zval **max_fd, **readfds, **writefds, **exceptfds, **tv_sec, - **tv_usec; + zval **max_fd, **readfds, **writefds, **exceptfds, **tv_sec, + **tv_usec; struct timeval tv; fd_set *rfds, *wfds, *xfds; int ret = 0; if (ZEND_NUM_ARGS() != 6 || zend_get_parameters_ex(6, &max_fd, &readfds, &writefds, &exceptfds, - &tv_sec, &tv_usec) == FAILURE) { + &tv_sec, &tv_usec) == FAILURE) { WRONG_PARAM_COUNT; } v_convert_to_long_ex(6, max_fd, readfds, writefds, exceptfds, tv_sec, tv_usec); @@ -425,10 +511,11 @@ PHP_FUNCTION(open_listen_sok) /* }}} */ /* {{{ proto int accept_connect(long fd) - Accepts a connection on the listening socket fd */ + accepts a connection on the listening socket fd */ int accept_connect(int fd, struct sockaddr *la) { int foo, m; + m = sizeof(*la); if ((foo = accept(fd, la, &m)) < 0) { return -1; @@ -458,7 +545,7 @@ PHP_FUNCTION(accept_connect) /* }}} */ /* {{{ proto bool set_nonblock(long fd) - Sets nonblocking mode for file descriptor fd */ + sets nonblocking mode for file descriptor fd */ PHP_FUNCTION(set_nonblock) { zval **fd; @@ -479,7 +566,7 @@ PHP_FUNCTION(set_nonblock) /* }}} */ /* {{{ proto bool listen(long fd, long backlog) - Sets the maximum number of connections allowed to be waited for on the socket specified by fd */ + sets the maximum number of connections allowed to be waited for on the socket specified by fd */ PHP_FUNCTION(listen) { zval **fd, **backlog; @@ -577,7 +664,7 @@ PHP_FUNCTION(read) /* }}} */ /* {{{ proto long getsockname(long fd, string &addr, long &port) - Given an fd, stores a string representing sa.sin_addr and the value of sa.sin_port int addr and port describing the local side of a socket */ + Given an fd, stores a string representing sa.sin_addr and the value of sa.sin_port into addr and port describing the local side of a socket. */ /* A lock to prevent inet_ntoa() from causing problems in threading */ volatile int inet_ntoa_lock = 0; @@ -601,10 +688,12 @@ PHP_FUNCTION(getsockname) RETURN_LONG(-errno); } else { char *addr_string; + while (inet_ntoa_lock == 1); inet_ntoa_lock = 1; addr_string = inet_ntoa(sa.sin_addr); tmp = emalloc(strlen(addr_string) + 1); + bzero(tmp, strlen(addr_string) + 1); strncpy(tmp, addr_string, strlen(addr_string)); inet_ntoa_lock = 0; @@ -670,7 +759,7 @@ PHP_FUNCTION(gethostbyname) #endif /* {{{ proto long getpeername(long fd, string &addr, long &port) - Given an fd, stores a string representing sa.sin_addr and the value of sa.sin_port int addr and port describing the remote side of a socket */ + Given an fd, stores a string representing sa.sin_addr and the value of sa.sin_port into addr and port describing the remote side of a socket. */ PHP_FUNCTION(getpeername) { @@ -691,6 +780,7 @@ PHP_FUNCTION(getpeername) RETURN_LONG(-errno); } else { char *addr_string; + while (inet_ntoa_lock == 1); inet_ntoa_lock = 1; addr_string = inet_ntoa(sa.sin_addr); @@ -760,46 +850,27 @@ PHP_FUNCTION(gethostbyaddr) #endif -/* {{{ proto long socket(string domain, string type, long protocol) +/* {{{ proto long socket(long domain, long type, long protocol) Creates an endpoint for communication in the domain specified by domain, of type specified by type */ PHP_FUNCTION(socket) { zval **domain, **type, **protocol; int ret; - int my_type, my_domain; if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &domain, &type, &protocol) == FAILURE) { WRONG_PARAM_COUNT; } - convert_to_string_ex(domain); - convert_to_string_ex(type); - convert_to_long_ex(protocol); + v_convert_to_long_ex(3, domain, type, protocol); - if (!strcasecmp(ZVAL(domain, str.val), "af_inet")) { - my_domain = AF_INET; - } else if (!strcasecmp(ZVAL(domain, str.val), "af_unix")) { - my_domain = AF_UNIX; - } else { - zend_error(E_WARNING, "invalid communications domain specified - assuming AF_INET"); - my_domain = AF_INET; - } - - if (!strcasecmp(ZVAL(type, str.val), "sock_stream")) { - my_type = SOCK_STREAM; - } else if (!strcasecmp(ZVAL(type, str.val), "sock_dgram")) { - my_type = SOCK_DGRAM; - } else if (!strcasecmp(ZVAL(type, str.val), "sock_raw")) { - my_type = SOCK_RAW; - } else if (!strcasecmp(ZVAL(type, str.val), "sock_seqpacket")) { - my_type = SOCK_SEQPACKET; - } else if (!strcasecmp(ZVAL(type, str.val), "sock_rdm")) { - my_type = SOCK_RDM; - } else { + if (ZVAL(domain, lval) != AF_INET && ZVAL(domain, lval) != AF_UNIX) { + zend_error(E_WARNING, "invalid socket domain specified - assuming AF_INET"); + ZVAL(domain, lval) = AF_INET; + } + if (ZVAL(type, lval) > 10) { zend_error(E_WARNING, "invalid socket type specified - assuming SOCK_STREAM"); - my_type = SOCK_STREAM; + ZVAL(type, lval) = SOCK_STREAM; } - - ret = socket(my_domain, my_type, ZVAL(protocol, lval)); + ret = socket(ZVAL(domain, lval), ZVAL(type, lval), ZVAL(protocol, lval)); if (ret < 0) { RETURN_LONG(-errno); } else { @@ -872,51 +943,59 @@ PHP_FUNCTION(strerror) } /* }}} */ -/* {{{ proto long bind(long sockfd, string domain [, string ...]) - Binds an open socket to a listening port */ -/* -- domain = "af_unix", 3rd arg is path to socket - -- domain = "af_inet", 3rd arg is address to bind to, 4th arg is port */ +/* {{{ proto long bind(long sockfd, long addr [, long port]) + Binds an open socket to a listening port. */ +/* Port is only specified if sockfd is in the AF_INET family. */ PHP_FUNCTION(bind) { - zval **arg0, **arg1, **arg2, **arg3; + zval **arg0, **arg1, **arg2; long ret; - void **p; - int arg_count; - va_list ptr; - ELS_FETCH(); + struct sockaddr sock_type; + socklen_t length = sizeof(sock_type); if (ZEND_NUM_ARGS() < 2) { WRONG_PARAM_COUNT; } - - p = EG(argument_stack).top_element-2; - arg_count = (ulong) *p; - - arg0 = (zval **) p-(arg_count --); - arg1 = (zval **) p-(arg_count--); + switch (ZEND_NUM_ARGS()) { + case 2: + if (zend_get_parameters_ex(2, &arg0, &arg1) == FAILURE) { + WRONG_PARAM_COUNT; + } + break; + case 3: + if (zend_get_parameters_ex(3, &arg0, &arg1, &arg2) == FAILURE) { + WRONG_PARAM_COUNT; + } + break; + default: + WRONG_PARAM_COUNT; + } convert_to_long_ex(arg0); - convert_to_string_ex(arg1); - if (!strcasecmp(ZVAL(arg1, str.val), "af_unix")) { + ret = getsockname(ZVAL(arg0, lval), &sock_type, &length); + if (ret < 0) { + RETURN_LONG(-errno); + } + if (sock_type.sa_family == AF_UNIX) { struct sockaddr_un sa; - if (ZEND_NUM_ARGS() != 3) { + + if (ZEND_NUM_ARGS() < 2) { WRONG_PARAM_COUNT; } - arg2 = (zval **) p-(arg_count--); - snprintf(sa.sun_path, 108, "%s", ZVAL(arg2, str.val)); - ret = bind(ZVAL(arg0, lval), &sa, sizeof(sa)); - } else if (!strcasecmp(ZVAL(arg1, str.val), "af_inet")) { + snprintf(sa.sun_path, 108, "%s", ZVAL(arg1, str.val)); + ret = bind(ZVAL(arg0, lval), &sa, SUN_LEN(&sa)); + } else if (sock_type.sa_family == AF_INET) { struct sockaddr_in sa; struct in_addr addr_buf; - if (ZEND_NUM_ARGS() != 4) { + + if (ZEND_NUM_ARGS() != 3) { WRONG_PARAM_COUNT; } - arg2 = (zval **) p-(arg_count--); - arg3 = (zval **) p-(arg_count--); - sa.sin_port = htons(ZVAL(arg3, lval)); - if (inet_aton(ZVAL(arg2, str.val), &addr_buf) < 0) { - struct hostent *host_struct = gethostbyname(ZVAL(arg2, str.val)); + sa.sin_port = htons(ZVAL(arg2, lval)); + if (inet_aton(ZVAL(arg1, str.val), &addr_buf) < 0) { + struct hostent *host_struct = gethostbyname(ZVAL(arg1, str.val)); + if (host_struct == NULL) { RETURN_LONG(-(h_errno) - 10000); } @@ -937,10 +1016,870 @@ PHP_FUNCTION(bind) } /* }}} */ -#endif /* HAVE_SOCKETS */ +/* {{{ proto resource build_iovec(int num_vectors, ...) + Build a 'struct iovec' for use with sendmsg, recvmsg, writev, and readv. */ +/* First parameter is number of vectors, each additional parameter is the + length of the vector to create. + */ + +PHP_FUNCTION(build_iovec) +{ + zval **num_vectors, **vector_len; + php_iovec_t *vector; + struct iovec *vector_array; + void **p; + int arg_count; + int i; + + ELS_FETCH(); + + if (ZEND_NUM_ARGS() < 1) { + WRONG_PARAM_COUNT; + } + p = EG(argument_stack).top_element - 2; + arg_count = (ulong) * p; + + num_vectors = (zval **) p - (arg_count--); + + convert_to_long_ex(num_vectors); + + vector_array = emalloc(sizeof(struct iovec) * (ZVAL(num_vectors, lval) + 1)); + + for (i = 0; i < ZVAL(num_vectors, lval); i++) { + if (arg_count < 1) { + WRONG_PARAM_COUNT; + } + vector_len = (zval **) p - (arg_count--); + + convert_to_long_ex(vector_len); + + vector_array[i].iov_base = emalloc(ZVAL(vector_len, lval)); + vector_array[i].iov_len = ZVAL(vector_len, lval); + } + + vector = emalloc(sizeof(php_iovec_t)); + vector->iov_array = vector_array; + vector->count = ZVAL(num_vectors, lval); + + ZEND_REGISTER_RESOURCE(return_value, vector, le_iov); +} +/* }}} */ + +/* {{{ proto string fetch_iovec(resource iovec_id, long iovec_position) + Returns the data held in the iovec specified by iovec_id[iovec_position]. */ +PHP_FUNCTION(fetch_iovec) +{ + zval **iovec_id, **iovec_position; + php_iovec_t *vector; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &iovec_id, &iovec_position) == FAILURE) { + WRONG_PARAM_COUNT; + } + (*iovec_id)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(vector, php_iovec_t *, iovec_id, -1, "IO vector table", le_iov); + + if (ZVAL(iovec_position, lval) > vector->count) { + zend_error(E_WARNING, "Can't access a vector position past the amount of vectors set in the array"); + RETURN_NULL(); + } + RETURN_STRINGL(vector->iov_array[ZVAL(iovec_position, lval)].iov_base, + vector->iov_array[ZVAL(iovec_position, lval)].iov_len, + 1); +} +/* }}} */ + +/* {{{ proto bool set_iovec(resource iovec_id, long iovec_position, string new_val) + Sets the data held in iovec_id[iovec_position] to new_val. */ +PHP_FUNCTION(set_iovec) +{ + zval **iovec_id, **iovec_position, **new_val; + php_iovec_t *vector; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &iovec_id, &iovec_position, &new_val) == FAILURE) { + WRONG_PARAM_COUNT; + } + (*iovec_id)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(vector, php_iovec_t *, iovec_id, -1, "IO vector table", le_iov); + + if (ZVAL(iovec_position, lval) > vector->count) { + zend_error(E_WARNING, "Can't access a vector position outside of the vector array bounds"); + RETURN_FALSE; + } + if (vector->iov_array[ZVAL(iovec_position, lval)].iov_base) { + efree(vector->iov_array[ZVAL(iovec_position, lval)].iov_base); + } + vector->iov_array[ZVAL(iovec_position, lval)].iov_base = estrdup(ZVAL(new_val, str.val)); + vector->iov_array[ZVAL(iovec_position, lval)].iov_len = strlen(ZVAL(new_val, str.val)); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool add_iovec(resource iovec_id, long iov_len) + Adds a new vector to the scatter/gather array */ +PHP_FUNCTION(add_iovec) +{ + zval **iovec_id, **iov_len; + php_iovec_t *vector; + struct iovec *vector_array; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &iovec_id, &iov_len) == FAILURE) { + WRONG_PARAM_COUNT; + } + (*iovec_id)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(vector, php_iovec_t *, iovec_id, -1, "IO vector table", le_iov); + + vector_array = emalloc(sizeof(struct iovec) * (vector->count + 2)); + bcopy(vector->iov_array, vector_array, sizeof(struct iovec) * vector->count); + + vector_array[vector->count].iov_base = emalloc(ZVAL(iov_len, lval)); + vector_array[vector->count].iov_len = ZVAL(iov_len, lval); + efree(vector->iov_array); + vector->iov_array = vector_array; + vector->count++; + + RETURN_TRUE; +} + +/* }}} */ + +/* {{{ proto bool delete_iovec(resource iovec_id, long iov_pos) + Deletes a vector from an array of vectors */ +PHP_FUNCTION(delete_iovec) +{ + zval **iovec_id, **iov_pos; + php_iovec_t *vector; + struct iovec *vector_array; + int i; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &iovec_id, &iov_pos) == FAILURE) { + WRONG_PARAM_COUNT; + } + (*iovec_id)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(vector, php_iovec_t *, iovec_id, -1, "IO vector table", le_iov); + + if (ZVAL(iov_pos, lval) > vector->count) { + zend_error(E_WARNING, "Can't delete an IO vector that is out of array bounds"); + RETURN_FALSE; + } + vector_array = emalloc(vector->count * sizeof(struct iovec)); + + for (i = 0; i < vector->count; i++) { + if (i < ZVAL(iov_pos, lval)) { + bcopy(&(vector_array[i]), &(vector->iov_array[i]), sizeof(struct iovec)); + } else if (i > ZVAL(iov_pos, lval)) { + bcopy(&(vector_array[i - 1]), &(vector->iov_array[i]), sizeof(struct iovec)); + } + } + + efree(vector->iov_array); + vector->iov_array = vector_array; + + RETURN_TRUE; +} + +/* }}} */ + +/* {{{ proto bool free_iovec(resource iovec_id) + Frees the iovec specified by iovec_id. */ +PHP_FUNCTION(free_iovec) +{ + zval **iovec_id; + php_iovec_t *vector; + int pos; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &iovec_id) == FAILURE) { + WRONG_PARAM_COUNT; + } + (*iovec_id)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(vector, php_iovec_t *, iovec_id, -1, "IO vector table", le_iov); + + for (pos = 0; pos < vector->count; pos++) { + efree(vector->iov_array[pos].iov_base); + } + + efree(vector->iov_array); + + vector->iov_array = NULL; + vector->count = 0; + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto long readv(long fd, resource iovec_id) + Reads from an fd, using the scatter-gather array defined by iovec_id. */ +PHP_FUNCTION(readv) +{ + zval **fd, **iovec_id; + php_iovec_t *vector; + int ret; + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fd, &iovec_id) == FAILURE) { + WRONG_PARAM_COUNT; + } + (*iovec_id)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(vector, php_iovec_t *, iovec_id, -1, "IO vector table", le_iov); + + ret = readv(ZVAL(fd, lval), vector->iov_array, vector->count); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long writev(long fd, resource iovec_id) + Writes to a fd, using the scatter-gather array defined by iovec_id. */ +PHP_FUNCTION(writev) +{ + zval **fd, **iovec_id; + php_iovec_t *vector; + int ret; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fd, &iovec_id) == FAILURE) { + WRONG_PARAM_COUNT; + } + (*iovec_id)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(vector, php_iovec_t *, iovec_id, -1, "IO vector table", le_iov); + + ret = writev(ZVAL(fd, lval), vector->iov_array, vector->count); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long recv(long fd, string buf, long len, long flags) + Receive data from a connected socket. */ +/* May be used with SOCK_DGRAM sockets. */ +PHP_FUNCTION(recv) +{ + zval **fd, **buf, **len, **flags; + int ret; + char *recv_buf; + + if (ZEND_NUM_ARGS() != 4 || zend_get_parameters_ex(4, &fd, &buf, &len, &flags) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(3, fd, len, flags); + convert_to_string_ex(buf); + recv_buf = emalloc(ZVAL(len, lval) + 2); + bzero(recv_buf, ZVAL(len, lval) + 2); + ret = recv(ZVAL(fd, lval), recv_buf, ZVAL(len, lval), ZVAL(flags, lval)); + if (ret < 0) { + efree(recv_buf); + RETURN_LONG(-errno); + } else { + if (ZVAL(buf, str.val) != NULL) { + efree(ZVAL(buf, str.val)); + } + ZVAL(buf, str.val) = estrndup(recv_buf, strlen(recv_buf)); + ZVAL(buf, str.len) = strlen(recv_buf); + + efree(recv_buf); + + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long send(long fd, string buf, long len, long flags) + Send data to a connected socket. */ +/* May be used with SOCK_DGRAM sockets. */ +PHP_FUNCTION(send) +{ + zval **fd, **buf, **len, **flags; + int ret; + + if (ZEND_NUM_ARGS() != 4 || zend_get_parameters_ex(4, &fd, &buf, &len, &flags) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(3, fd, len, flags); + convert_to_string_ex(buf); + + ret = send(ZVAL(fd, lval), ZVAL(buf, str.val), + (ZVAL(buf, str.len) < ZVAL(len, lval) ? ZVAL(buf, str.len) : ZVAL(len, lval)), + ZVAL(flags, lval)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long recvfrom(long fd, string &buf, long len, long flags, string &name [, long &port]) + Receive data from a socket, connected or not. */ +PHP_FUNCTION(recvfrom) +{ + zval **fd, **buf, **len, **flags, **name, **port; + int ret; + struct sockaddr sa; + socklen_t salen; + + if (ZEND_NUM_ARGS() < 5) { + WRONG_PARAM_COUNT; + } + switch (ZEND_NUM_ARGS()) { + case 5: + if (zend_get_parameters_ex(5, &fd, &buf, &len, &flags, &name) == FAILURE) { + WRONG_PARAM_COUNT; + } + break; + case 6: + if (zend_get_parameters_ex(6, &fd, &buf, &len, &flags, &name, &port) == FAILURE) { + WRONG_PARAM_COUNT; + } + break; + default: + WRONG_PARAM_COUNT; + } + + salen = sizeof(sa); + + ret = getsockname(ZVAL(fd, lval), &sa, &salen); + if (ret < 0) { + RETURN_LONG(-errno); + } + switch (sa.sa_family) { + case AF_UNIX: + { + struct sockaddr_un sun; + char *recv_buf = emalloc(ZVAL(len, lval) + 2); + socklen_t sun_length = sizeof(sun); + + if (ZEND_NUM_ARGS() != 5) { + WRONG_PARAM_COUNT; + } + bzero(recv_buf, ZVAL(len, lval) + 2); + + ret = recvfrom(ZVAL(fd, lval), recv_buf, ZVAL(len, lval), ZVAL(flags, lval), + (struct sockaddr *) &sun, (socklen_t *) & sun_length); + + if (ret < 0) { + efree(recv_buf); + RETURN_LONG(-errno); + } + if (ZVAL(buf, str.val) != NULL) { + efree(ZVAL(buf, str.val)); + } + ZVAL(buf, str.val) = estrndup(recv_buf, strlen(recv_buf)); + ZVAL(buf, str.len) = strlen(recv_buf); + + if (ZVAL(name, str.val) != NULL) { + efree(ZVAL(name, str.val)); + } + ZVAL(name, str.val) = estrdup(sun.sun_path); + ZVAL(name, str.len) = strlen(sun.sun_path); + + efree(recv_buf); + + RETURN_LONG(ret); + } + break; + case AF_INET: + { + struct sockaddr_in sin; + char *recv_buf = emalloc(ZVAL(len, lval) + 2); + socklen_t sin_length = sizeof(sin); + char *address; + + if (ZEND_NUM_ARGS() != 6) { + WRONG_PARAM_COUNT; + } + bzero(recv_buf, ZVAL(len, lval) + 2); + + ret = recvfrom(ZVAL(fd, lval), recv_buf, ZVAL(len, lval), ZVAL(flags, lval), + (struct sockaddr *) &sin, (socklen_t *) & sin_length); + + if (ret < 0) { + efree(recv_buf); + RETURN_LONG(-errno); + } + if (ZVAL(buf, str.val) != NULL) { + efree(ZVAL(buf, str.val)); + } + if (ZVAL(name, str.val) != NULL) { + efree(ZVAL(name, str.val)); + } + ZVAL(buf, str.val) = estrdup(recv_buf); + ZVAL(buf, str.len) = strlen(recv_buf); + + address = inet_ntoa(sin.sin_addr); + if (address == NULL) { + ZVAL(name, str.val) = estrdup("0.0.0.0"); + ZVAL(name, str.len) = strlen(ZVAL(name, str.val)); + } else { + ZVAL(name, str.val) = estrdup(address); + ZVAL(name, str.len) = strlen(address); + } + + ZVAL(port, lval) = ntohs(sin.sin_port); + + efree(recv_buf); + RETURN_LONG(ret); + } + break; + default: + RETURN_LONG(-EPROTONOSUPPORT); + + } +} +/* }}} */ + +/* {{{ proto long sendto(long fd, string buf, long len, long flags, string addr [, long port]) + Sends a message to a socket, whether it is connected or not. */ +PHP_FUNCTION(sendto) +{ + zval **fd, **buf, **len, **flags, **addr, **port; + struct sockaddr sa; + socklen_t salen = sizeof(sa); + int ret; + + switch (ZEND_NUM_ARGS()) { + case 5: + ret = zend_get_parameters_ex(5, &fd, &buf, &len, &flags, &addr); + break; + case 6: + ret = zend_get_parameters_ex(6, &fd, &buf, &len, &flags, &addr, &port); + break; + default: + ret = FAILURE; + } + + if (ret == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(3, fd, len, flags); + convert_to_string_ex(buf); + + if (ZEND_NUM_ARGS() == 6) { + convert_to_long_ex(port); + } + ret = getsockname(ZVAL(fd, lval), &sa, &salen); + if (ret < 0) { + RETURN_LONG(-errno); + } + switch (sa.sa_family) { + case AF_UNIX: + { + struct sockaddr_un sun; + + if (ZEND_NUM_ARGS() != 5) { + WRONG_PARAM_COUNT; + } + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + snprintf(sun.sun_path, 108, "%s", ZVAL(port, str.val)); + ret = sendto(ZVAL(fd, lval), ZVAL(buf, str.val), + (ZVAL(buf, str.len) > ZVAL(len, lval) ? ZVAL(len, lval) : ZVAL(buf, str.len)), + ZVAL(flags, lval), &sun, SUN_LEN(&sun)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } + } + break; + case AF_INET: + { + struct sockaddr_in sin; + struct in_addr addr_buf; + + if (ZEND_NUM_ARGS() != 6) { + WRONG_PARAM_COUNT; + } + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + + if (inet_aton(ZVAL(addr, str.val), &addr_buf) == 0) { + sin.sin_addr.s_addr = addr_buf.s_addr; + } else { + struct hostent *he; + + he = gethostbyname(ZVAL(addr, str.val)); + if (he == NULL) { + RETURN_LONG(-(h_errno) - 10000); + } + sin.sin_addr.s_addr = *(int *) (he->h_addr_list[0]); + } + sin.sin_port = htons(ZVAL(port, lval)); + ret = sendto(ZVAL(fd, lval), ZVAL(buf, str.val), + (ZVAL(buf, str.len) > ZVAL(len, lval) ? ZVAL(len, lval) : ZVAL(buf, str.len)), + ZVAL(flags, lval), &sin, sizeof(sin)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } + } + break; + default: + RETURN_LONG(-EPROTONOSUPPORT); + break; + } +} +/* }}} */ + +/* {{{ proto long recvmsg(long fd, resource iovec, array &control, long &controllen, long &flags, string &addr [, long &port]) + Used to receive messages on a socket, whether connection-oriented or not. */ +PHP_FUNCTION(recvmsg) +{ + zval **fd, **iovec, **control, **controllen, **flags, **addr, **port; + zval *control_array = NULL; + php_iovec_t *iov; + struct msghdr hdr; + struct sockaddr sa; + struct sockaddr_in *sin = (struct sockaddr_in *) &sa; + struct sockaddr_un *sun = (struct sockaddr_un *) &sa; + struct cmsghdr *ctl_buf; + socklen_t salen = sizeof(sa); + int ret; + + switch (ZEND_NUM_ARGS()) { + case 6: + ret = zend_get_parameters_ex(6, &fd, &iovec, &control, &controllen, &flags, &addr); + break; + case 7: + ret = zend_get_parameters_ex(7, &fd, &iovec, &control, &controllen, &flags, &addr, &port); + break; + default: + WRONG_PARAM_COUNT; + } + + if (ret == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(4, fd, iovec, controllen, flags); + convert_to_string_ex(control); + convert_to_string_ex(addr); + if (ZEND_NUM_ARGS() == 7) { + convert_to_long_ex(port); + } + (*iovec)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(iov, php_iovec_t *, iovec, -1, "IO vector table", le_iov); + + ret = getsockname(ZVAL(fd, lval), &sa, &salen); + if (ret < 0) { + RETURN_LONG(-errno); + } + if (ZVAL(controllen, lval) > sizeof(struct cmsghdr)) { + ctl_buf = emalloc(ZVAL(controllen, lval)); + } else { + ctl_buf = NULL; + } + + switch (sa.sa_family) { + case AF_INET: + { + if (ZEND_NUM_ARGS() != 7) { + efree(ctl_buf); + WRONG_PARAM_COUNT; + } + bzero(&sa, sizeof(sa)); + hdr.msg_name = sin; + hdr.msg_namelen = sizeof(sa); + hdr.msg_iov = iov->iov_array; + hdr.msg_iovlen = iov->count; + + if (ctl_buf) { + hdr.msg_control = ctl_buf; + hdr.msg_controllen = ZVAL(controllen, lval); + } else { + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + } + + hdr.msg_flags = 0; + + ret = recvmsg(ZVAL(fd, lval), &hdr, ZVAL(flags, lval)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + struct cmsghdr *mhdr = (struct cmsghdr *) hdr.msg_control; + + /* copy values as appropriate... */ + array_init(control_array); + add_assoc_long(control_array, "cmsg_level", mhdr->cmsg_level); + add_assoc_long(control_array, "cmsg_type", mhdr->cmsg_type); + add_assoc_string(control_array, "cmsg_data", CMSG_DATA(mhdr), 1); + *control = control_array; + ZVAL(controllen, lval) = hdr.msg_controllen; + ZVAL(flags, lval) = hdr.msg_flags; + if (ZVAL(addr, str.val) != NULL) { + efree(ZVAL(addr, str.val)); + } { + char *tmp = inet_ntoa(sin->sin_addr); + + if (tmp == NULL) { + ZVAL(addr, str.val) = estrdup("0.0.0.0"); + } else { + ZVAL(addr, str.val) = estrdup(tmp); + } + } + ZVAL(addr, str.len) = strlen(ZVAL(addr, str.val)); + ZVAL(port, lval) = ntohs(sin->sin_port); + RETURN_LONG(ret); + } + } + break; + case AF_UNIX: + { + if (ZEND_NUM_ARGS() != 6) { + efree(ctl_buf); + WRONG_PARAM_COUNT; + } + bzero(&sa, sizeof(sa)); + hdr.msg_name = sun; + hdr.msg_namelen = sizeof(sa); + hdr.msg_iov = iov->iov_array; + hdr.msg_iovlen = iov->count; + + if (ctl_buf) { + hdr.msg_control = ctl_buf; + hdr.msg_controllen = ZVAL(controllen, lval); + } else { + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + } + + hdr.msg_flags = 0; + + ret = recvmsg(ZVAL(fd, lval), &hdr, ZVAL(flags, lval)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + struct cmsghdr *mhdr = (struct cmsghdr *) hdr.msg_control; + + /* copy values as appropriate... */ + array_init(control_array); + add_assoc_long(control_array, "cmsg_level", mhdr->cmsg_level); + add_assoc_long(control_array, "cmsg_type", mhdr->cmsg_type); + add_assoc_string(control_array, "cmsg_data", CMSG_DATA(mhdr), 1); + *control = control_array; + ZVAL(controllen, lval) = hdr.msg_controllen; + ZVAL(flags, lval) = hdr.msg_flags; + if (ZVAL(addr, str.val) != NULL) { + efree(ZVAL(addr, str.val)); + } + ZVAL(addr, str.val) = estrdup(sun->sun_path); + RETURN_LONG(ret); + } + } + break; + default: + RETURN_LONG(-EPROTONOSUPPORT); + } +} +/* }}} */ + +/* {{{ proto long sendmsg(long fd, resource iovec, long flags, string addr [, long port]) + Sends a message to a socket, regardless of whether it is connection-oriented or not */ +PHP_FUNCTION(sendmsg) +{ + zval **fd, **iovec, **flags, **addr, **port; + php_iovec_t *iov; + int ret; + struct sockaddr sa; + int salen; + + if (ZEND_NUM_ARGS() == 4) { + ret = zend_get_parameters_ex(4, &fd, &iovec, &flags, &addr); + } else if (ZEND_NUM_ARGS() == 5) { + ret = zend_get_parameters_ex(5, &fd, &iovec, &flags, &addr, &port); + } + + if (ret == FAILURE) { + WRONG_PARAM_COUNT; + } + + salen = sizeof(sa); + ret = getsockname(ZVAL(fd, lval), &sa, &salen); + if (ret < 0) { + RETURN_LONG(-errno); + } + + (*iovec)->type = IS_RESOURCE; + ZEND_FETCH_RESOURCE(iov, php_iovec_t *, iovec, -1, "IO vector table", le_iov); + + switch(sa.sa_family) + { + case AF_INET: + { + struct msghdr hdr; + struct sockaddr_in *sin = (struct sockaddr_in *) &sa; + h_errno = 0; + errno = 0; + bzero(&hdr, sizeof(hdr)); + hdr.msg_name = &sa; + hdr.msg_namelen = sizeof(sa); + hdr.msg_iov = iov->iov_array; + hdr.msg_iovlen = iov->count; + + if (inet_aton(ZVAL(addr, str.val), &sin->sin_addr) != 0) { + struct hostent *he = gethostbyname(ZVAL(addr, str.val)); + if (!he) { + RETURN_LONG(h_errno > 0 ? -(h_errno) - 10000 : -errno) + } + sin->sin_addr.s_addr = *(int *)(he->h_addr_list[0]); + } + + sin->sin_port = htons(ZVAL(port, lval)); + ret = sendmsg(ZVAL(fd, lval), &hdr, ZVAL(flags, lval)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } + } + break; + case AF_UNIX: + { + struct msghdr hdr; + struct sockaddr_un *sun = (struct sockaddr_un *) &sa; + errno = 0; + hdr.msg_name = sun; + hdr.msg_iov = iov->iov_array; + hdr.msg_iovlen = iov->count; + + snprintf(sun->sun_path, 108, "%s", ZVAL(addr, str.val)); + + hdr.msg_namelen = SUN_LEN(sun); + + ret = sendmsg(ZVAL(fd, lval), &hdr, ZVAL(flags, lval)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } + } + break; + default: + RETURN_LONG(-EPROTONOSUPPORT); + } +} +/* }}} */ + +/* {{{ proto long getsockopt(long fd, long level, long optname, array/long &optval) + Gets socket options for the socket. */ +/* If optname is SO_LINGER, optval is returned as an array with members + "l_onoff" and "l_linger", otherwise it is an integer. +*/ +PHP_FUNCTION(getsockopt) +{ + zval **fd, **level, **optname, **optval; + struct linger linger_val; + int other_val; + int optlen; + int ret; + + fd = level = optname = optval = NULL; + + if (ZEND_NUM_ARGS() != 4 || zend_get_parameters_ex(4, &fd, &level, &optname, &optval) == FAILURE) { + WRONG_PARAM_COUNT; + } + + v_convert_to_long_ex(3, fd, level, optname); + /* optname is set on the way out .. */ + + if (ZVAL(level, lval) == SO_LINGER) { + zval *optval_array = NULL; + optlen = sizeof(struct linger); + ret = getsockopt(ZVAL(fd, lval), ZVAL(level, lval), ZVAL(optname, lval), &linger_val, &optlen); + + if (ret < 0) { + RETURN_LONG(-errno); + } + + array_init(optval_array); + add_assoc_long(optval_array, "l_onoff", linger_val.l_onoff); + add_assoc_long(optval_array, "l_linger", linger_val.l_linger); + + *optval = optval_array; + RETURN_LONG(ret); + } else { + optlen = sizeof(other_val); + ret = getsockopt(ZVAL(fd, lval), ZVAL(level, lval), ZVAL(optname, lval), &other_val, &optlen); + if (ret < 0) { + RETURN_LONG(-errno); + } + + ZVAL_LONG(*optval, other_val); + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long setsockopt(long fd, long level, long optname, long/array optval) + Sets socket options for the socket. */ +/* If optname is SO_LINGER, optval is expected to be an array + with members "l_onoff" and "l_linger", otherwise it should be an integer. */ +PHP_FUNCTION(setsockopt) +{ + zval **fd, **level, **optname, **optval; + int ret; + struct linger lv; + int ov; + int optlen; + + errno = 0; + + if (ZEND_NUM_ARGS() != 4 || zend_get_parameters_ex(4, &fd, &level, &optname, &optval) == FAILURE) { + WRONG_PARAM_COUNT; + } + + v_convert_to_long_ex(3, fd, level, optname); + + if (ZVAL(optname, lval) == SO_LINGER) { + HashTable *ht; + zval **l_onoff; + zval **l_linger; + + convert_to_array_ex(optval); + ht = HASH_OF(*optval); + + if (zend_hash_find(ht, "l_onoff", strlen("l_onoff") + 1, (void **) &l_onoff) == FAILURE) { + zend_error(E_ERROR, "No key \"l_onoff\" passed in optval"); + return; + } + if (zend_hash_find(ht, "l_linger", strlen("l_linger") + 1, (void **) &l_linger) == FAILURE) { + zend_error(E_ERROR, "No key \"l_linger\" passed in optval"); + return; + } + + convert_to_long_ex(l_onoff); + convert_to_long_ex(l_linger); + + lv.l_onoff = ZVAL(l_onoff, lval); + lv.l_linger = ZVAL(l_linger, lval); + + optlen = sizeof(lv); + ret = setsockopt(ZVAL(fd, lval), ZVAL(level, lval), ZVAL(optname, lval), &lv, optlen); + } else { + convert_to_long_ex(optval); + + optlen = sizeof(ov); + ov = ZVAL(optval, lval); + + ret = setsockopt(ZVAL(fd, lval), ZVAL(level, lval), ZVAL(optname, lval), &ov, optlen); + } + + printf("returning: ret = %i, errno = %i\n", ret, errno); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +#endif /* HAVE_SOCKETS */ /* * Local variables: |