diff options
author | Chris Vandomelen <chrisv@php.net> | 2000-07-03 04:35:57 +0000 |
---|---|---|
committer | Chris Vandomelen <chrisv@php.net> | 2000-07-03 04:35:57 +0000 |
commit | 21abde5ca18f087173f986c6b5c2647e90962648 (patch) | |
tree | 627f942064bc1fcf95428c9743deca3e33804b8b /ext | |
parent | 7a4d4bcd6ba6da60252f77ab01588e5ec5c54a09 (diff) | |
download | php-git-21abde5ca18f087173f986c6b5c2647e90962648.tar.gz |
* Makefile.in
config.m4
php_sockets.h
sockets.c
sockets.php:
- Added files needed for Unix-style sockets support in PHP.
Diffstat (limited to 'ext')
-rw-r--r-- | ext/sockets/Makefile.in | 7 | ||||
-rw-r--r-- | ext/sockets/config.m4 | 38 | ||||
-rw-r--r-- | ext/sockets/php_sockets.h | 110 | ||||
-rw-r--r-- | ext/sockets/sockets.c | 955 | ||||
-rw-r--r-- | ext/sockets/sockets.php | 10 |
5 files changed, 1120 insertions, 0 deletions
diff --git a/ext/sockets/Makefile.in b/ext/sockets/Makefile.in new file mode 100644 index 0000000000..acfca2cd39 --- /dev/null +++ b/ext/sockets/Makefile.in @@ -0,0 +1,7 @@ +# $Id$ + +LTLIBRARY_NAME = libsockets.la +LTLIBRARY_SOURCES = sockets.c +LTLIBRARY_SHARED_NAME = sockets.la + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 new file mode 100644 index 0000000000..8fcb0d610c --- /dev/null +++ b/ext/sockets/config.m4 @@ -0,0 +1,38 @@ +dnl $Id$ +dnl config.m4 for extension sockets +dnl don't forget to call PHP_EXTENSION(sockets) + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(sockets, for sockets support, +dnl Make sure that the comment is aligned: +dnl [ --with-sockets Include sockets support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(sockets, whether to enable sockets support, +dnl Make sure that the comment is aligned: +[ --enable-sockets Enable sockets support]) + +if test "$PHP_SOCKETS" != "no"; then + dnl If you will not be testing anything external, like existence of + dnl headers, libraries or functions in them, just uncomment the + dnl following line and you are ready to go. + + dnl --------------------------------- + dnl Headers + dnl --------------------------------- + + AC_HEADER_STDC + AC_UNISTD_H + AC_CHECK_HEADERS(sys/types.h sys/socket.h netdb.h netinet/in.h netinet/tcp.h sys/un.h arpa/inet.h) + AC_CHECK_HEADERS(sys/time.h errno.h fcntl.h) + + AC_DEFINE(HAVE_SOCKETS, 1, [ ]) + dnl Write more examples of tests here... + PHP_EXTENSION(sockets, $ext_shared) +fi diff --git a/ext/sockets/php_sockets.h b/ext/sockets/php_sockets.h new file mode 100644 index 0000000000..adea0f7498 --- /dev/null +++ b/ext/sockets/php_sockets.h @@ -0,0 +1,110 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.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. | + +----------------------------------------------------------------------+ + | Authors: | + | | + +----------------------------------------------------------------------+ + */ + +#ifndef _PHP_SOCKETS_H +#define _PHP_SOCKETS_H + +/* $Id$ */ + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. +*/ +#if HAVE_SOCKETS + +extern zend_module_entry sockets_module_entry; +#define phpext_sockets_ptr &sockets_module_entry + +#ifdef PHP_WIN32 +#define PHP_SOCKETS_API __declspec(dllexport) +#else +#define PHP_SOCKETS_API +#endif + +PHP_MINIT_FUNCTION(sockets); +PHP_MSHUTDOWN_FUNCTION(sockets); +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); +PHP_FUNCTION(fd_set); +PHP_FUNCTION(fd_isset); +PHP_FUNCTION(fd_clear); +PHP_FUNCTION(fd_zero); +PHP_FUNCTION(select); +PHP_FUNCTION(open_listen_sok); +PHP_FUNCTION(accept_connect); +PHP_FUNCTION(set_nonblock); +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); + +/* Fill in this structure and use entries in it + for thread safety instead of using true globals. +*/ +typedef struct { + /* You can use the next one as type if your module registers any + resources. Oh, you can of course rename it to something more + suitable, add list entry types or remove it if it not needed. + It's just an example. + */ + int le_sockets; +} php_sockets_globals; + +/* In every function that needs to use variables in php_sockets_globals, + do call SOCKETSLS_FETCH(); after declaring other variables used by + that function, and always refer to them as SOCKETSG(variable). + You are encouraged to rename these macros something shorter, see + examples in any other php module directory. +*/ + +#ifdef ZTS +#define SOCKETSG(v) (sockets_globals->v) +#define SOCKETSLS_FETCH() php_sockets_globals *sockets_globals = ts_resource(gd_sockets_id) +#else +#define SOCKETSG(v) (sockets_globals.v) +#define SOCKETSLS_FETCH() +#endif + +#else + +#define phpext_sockets_ptr NULL + +#endif + +#endif /* _PHP_SOCKETS_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c new file mode 100644 index 0000000000..27966340e2 --- /dev/null +++ b/ext/sockets/sockets.c @@ -0,0 +1,955 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.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. | + +----------------------------------------------------------------------+ + | Authors: | + | Chris Vandomelen <chrisv@b0rked.dhs.org> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_ini.h" +#include "php_sockets.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/un.h> +#include <arpa/inet.h> +#include <sys/time.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. + */ +#if HAVE_SOCKETS + +#ifdef ZTS +int sockets_globals_id; +#else +php_sockets_globals sockets_globals; +#endif + +#define ZVAL(arg, type) ((*(arg))->value.type) + +/* Perform convert_to_long_ex on a list of items */ + +void v_convert_to_long_ex(int items,...) +{ + va_list ap; + zval **arg; + int i; + + va_start(ap, items); + + for (i = 0; i < items; i++) { + arg = va_arg(ap, zval **); + convert_to_long_ex(arg); + } + va_end(ap); + +} + +/* *INDENT-OFF* */ +static unsigned char second_and_third_args_force_ref[] = +{3, BYREF_NONE, BYREF_FORCE, BYREF_FORCE}; + +/* Every user visible function must have an entry in sockets_functions[]. + */ +function_entry sockets_functions[] = +{ + PHP_FE(confirm_sockets_compiled, NULL) /* For testing, remove later. */ + PHP_FE(fd_alloc, NULL) /* OK */ + PHP_FE(fd_dealloc, NULL) /* OK */ + PHP_FE(fd_set, NULL) /* OK */ + PHP_FE(fd_isset, NULL) /* OK */ + PHP_FE(fd_clear, NULL) /* OK */ + PHP_FE(fd_zero, NULL) /* OK */ + PHP_FE(select, NULL) /* OK */ + PHP_FE(open_listen_sok, NULL) /* OK */ + PHP_FE(accept_connect, NULL) /* OK */ + PHP_FE(set_nonblock, NULL) /* OK */ + PHP_FE(listen, NULL) /* OK */ + PHP_FE(close, NULL) /* OK */ + PHP_FE(write, NULL) /* OK */ + PHP_FE(read, second_arg_force_ref) /* OK */ +#if 0 +/* If and when asynchronous context switching is avaliable, this will work. Otherwise, forget it. */ + PHP_FE(signal, NULL) +#endif +#if 0 +/* These are defined elsewhere.. would these be more appropriate? */ + PHP_FE(gethostbyname, second_arg_force_ref) /* OK */ + PHP_FE(gethostbyaddr, second_arg_force_ref) /* OK */ +#endif + PHP_FE(getsockname, second_and_third_args_force_ref) /* OK */ + PHP_FE(getpeername, second_and_third_args_force_ref) /* OK */ + PHP_FE(socket, NULL) /* OK */ + PHP_FE(connect, NULL) /* OK */ + PHP_FE(strerror, NULL) /* OK */ + PHP_FE(bind, NULL) + {NULL, NULL, NULL} /* Must be the last line in sockets_functions[] */ +}; + +/* *INDENT-ON* */ + +zend_module_entry sockets_module_entry = +{ + "sockets", + sockets_functions, + PHP_MINIT(sockets), + PHP_MSHUTDOWN(sockets), + NULL, + NULL, + PHP_MINFO(sockets), + STANDARD_MODULE_PROPERTIES +}; + +int le_destroy; + +#ifdef COMPILE_DL_SOCKETS +ZEND_GET_MODULE(sockets) +#endif + +/* Remove comments and fill if you need to have entries in php.ini + PHP_INI_BEGIN() + PHP_INI_END() + */ + +static void destroy_fd_sets(fd_set * set) +{ + efree(set); +} + +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); + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(sockets) +{ + return SUCCESS; +} + +PHP_MINFO_FUNCTION(sockets) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "sockets support", "enabled"); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ +} + +/* Remove the following function when you have succesfully modified config.m4 + so that your module can be compiled into PHP, it exists only for testing + purposes. */ + +/* Every user-visible function in PHP should document itself in the source */ +/* {{{ proto string confirm_sockets_compiled(string arg) + Return a string to confirm that the module is compiled in */ +PHP_FUNCTION(confirm_sockets_compiled) +{ + zval **arg; + int len; + char string[256]; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(arg); + + len = sprintf(string, "Congratulations, you have successfully modified ext/sockets/config.m4, module %s is compiled in PHP", Z_STRVAL_PP(arg)); + RETVAL_STRINGL(string, len, 1); +} +/* }}} */ +/* The previous line is meant for emacs, so it can correctly fold and unfold + functions in source code. See the corresponding marks just before function + definition, where the functions purpose is also documented. Please follow + this convention for the convenience of others editing your code. + */ + +/* {{{ proto resource fd_alloc(void) + Allocate a file descriptor set + */ +PHP_FUNCTION(fd_alloc) +{ + fd_set *set; + zval *new_resource; + int ret; + + set = emalloc(sizeof(fd_set)); + if (!set) { + 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); +} +/* }}} */ + +/* {{{ proto void fd_dealloc(void) + De-allocate a file descriptor set + ** BUG: This is currently a no-op! + */ +PHP_FUNCTION(fd_dealloc) +{ + RETVAL_TRUE; +} +/* }}} */ + +/* {{{ proto bool fd_set(long fd, resource set) + Add a file descriptor to a set */ +PHP_FUNCTION(fd_set) +{ + zval **set, **fd; + fd_set *the_set; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fd, &set) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(2, set, fd); + + if ((*set)->type != IS_RESOURCE) { + (*set)->type = IS_RESOURCE; + } + ZEND_FETCH_RESOURCE(the_set, fd_set *, set, -1, "File descriptor set", le_destroy); + + FD_SET((*fd)->value.lval, the_set); + + RETVAL_TRUE; +} +/* }}} */ + +/* {{{ proto bool fd_clear(long fd, resource set) + Clear a file descriptor from a set + */ +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; + } + v_convert_to_long_ex(2, set, fd); + + (*set)->type = IS_RESOURCE; + + ZEND_FETCH_RESOURCE(the_set, fd_set *, set, -1, "File descriptor set", le_destroy); + + FD_CLR((*fd)->value.lval, the_set); + + RETVAL_TRUE; +} +/* }}} */ + +/* {{{ proto bool fd_isset(long fd, resource 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; + } + v_convert_to_long_ex(2, set, fd); + + (*set)->type = IS_RESOURCE; + + ZEND_FETCH_RESOURCE(the_set, fd_set *, set, -1, "File descriptor set", le_destroy); + + if (FD_ISSET((*fd)->value.lval, the_set)) { + RETVAL_TRUE; + } + RETVAL_FALSE; +} +/* }}} */ + +/* {{{ proto void fd_zero(resource set) + Clear a file descriptor set */ +PHP_FUNCTION(fd_zero) +{ + zval **set; + fd_set *the_set; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(2, &set) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_long_ex(set); + + (*set)->type = IS_RESOURCE; + + ZEND_FETCH_RESOURCE(the_set, fd_set *, set, -1, "File descriptor set", le_destroy); + + FD_ZERO(the_set); + + RETVAL_TRUE; +} +/* }}} */ + +/* {{{ 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. + + 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 charac- + ters 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. + */ + +PHP_FUNCTION(select) +{ + 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) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(6, max_fd, readfds, writefds, exceptfds, tv_sec, tv_usec); + + tv.tv_sec = ZVAL(tv_sec, lval); + tv.tv_usec = ZVAL(tv_usec, lval); + + (*readfds)->type = IS_RESOURCE; + (*writefds)->type = IS_RESOURCE; + (*exceptfds)->type = IS_RESOURCE; + + if (ZVAL(readfds, lval) == 0) { + rfds = NULL; + } else { + ZEND_FETCH_RESOURCE(rfds, fd_set *, readfds, -1, "File descriptor set", le_destroy); + } + + if (ZVAL(writefds, lval) == 0) { + wfds = NULL; + } else { + ZEND_FETCH_RESOURCE(wfds, fd_set *, writefds, -1, "File descriptor set", le_destroy); + } + + if (ZVAL(exceptfds, lval) == 0) { + xfds = NULL; + } else { + ZEND_FETCH_RESOURCE(xfds, fd_set *, exceptfds, -1, "File descriptor set", le_destroy); + } + + ret = select(ZVAL(max_fd, lval), rfds, wfds, xfds, &tv); + + if (ret < 0) { + RETVAL_LONG(-errno); + } else { + RETVAL_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long open_listen_sok(long port) + Opens a socket on port to accept connections */ + +int open_listen_sok(int port) +{ + int fd; + struct sockaddr_in la; + struct hostent *hp; + + if ((hp = gethostbyname("0.0.0.0")) == NULL) { + return -1; + } + bcopy(hp->h_addr, (char *) &la.sin_addr, hp->h_length); + la.sin_family = hp->h_addrtype; + la.sin_port = htons(port); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + if ((bind(fd, (struct sockaddr *) &la, sizeof(la)) < 0)) { + return -1; + } + listen(fd, 128); + return fd; +} + +PHP_FUNCTION(open_listen_sok) +{ + zval **port; + int ret; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &port) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_long_ex(port); + + ret = open_listen_sok(ZVAL(port, lval)); + + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto int accept_connect(long 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; + } + return foo; +} + +PHP_FUNCTION(accept_connect) +{ + zval **fd; + int ret; + struct sockaddr_in sa; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &fd) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_long_ex(fd); + + ret = accept_connect(ZVAL(fd, lval), (struct sockaddr *) &sa); + + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto bool set_nonblock(long fd) + sets nonblocking mode for file descriptor fd + */ +PHP_FUNCTION(set_nonblock) +{ + zval **fd; + int ret; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &fd) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_long_ex(fd); + + ret = fcntl(ZVAL(fd, lval), F_SETFL, O_NONBLOCK); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto bool listen(long fd, long backlog) + sets the maximum number of connections allowed to be waited for on + the socket specified by fd */ +PHP_FUNCTION(listen) +{ + zval **fd, **backlog; + int ret; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fd, &backlog) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(2, fd, backlog); + + ret = listen(ZVAL(fd, lval), ZVAL(backlog, lval)); + + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto bool close(long fd) + Close a file descriptor */ +PHP_FUNCTION(close) +{ + zval **arg; + int ret; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_long_ex(arg); + ret = close(ZVAL(arg, lval)); + + if (ret == -1) { + zend_error(E_WARNING, "Invalid file descriptor"); + RETVAL_FALSE; + } + RETVAL_TRUE; +} +/* }}} */ + +/* {{{ proto long write(long fd, string buf, long length) + Writes length bytes of buf to the file descriptor fd */ +PHP_FUNCTION(write) +{ + zval **fd, **buf, **length; + int ret; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &fd, &buf, &length) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(2, fd, length); + convert_to_string_ex(buf); + + if (ZVAL(buf, str.len) < ZVAL(length, lval)) { + ret = write(ZVAL(fd, lval), (void *) ZVAL(buf, str.val), ZVAL(buf, str.len)); + } else { + ret = write(ZVAL(fd, lval), (void *) ZVAL(buf, str.val), ZVAL(length, lval)); + } + + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long read(long fd, string &buf, long length) + Reads length bytes from fd into buf */ +PHP_FUNCTION(read) +{ + zval **fd, **buf, **length; + char *tmp; + int ret; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &fd, &buf, &length) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(2, fd, length); + convert_to_string_ex(buf); + + tmp = emalloc(ZVAL(length, lval)); + ret = read(ZVAL(fd, lval), tmp, ZVAL(length, lval)); + + if (ret < 0) { + RETURN_LONG(-errno); + } else { + ZVAL(buf, str.val) = tmp; + ZVAL(buf, str.len) = ret; + + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ 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. */ + +/* A lock to prevent inet_ntoa() from causing problems in threading */ +volatile int inet_ntoa_lock = 0; + +PHP_FUNCTION(getsockname) +{ + zval **fd, **addr, **port; + char *tmp; + struct sockaddr_in sa; + int salen = sizeof(struct sockaddr_in); + int ret; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &fd, &addr, &port) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(2, fd, port); + convert_to_string_ex(addr); + + ret = getsockname(ZVAL(fd, lval), &sa, &salen); + if (ret < 0) { + 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); + strncpy(tmp, addr_string, strlen(addr_string)); + inet_ntoa_lock = 0; + + ZVAL(addr, str.val) = tmp; + ZVAL(addr, str.len) = strlen(tmp); + ZVAL(port, lval) = htons(sa.sin_port); + + RETURN_LONG(ret); + } +} +/* }}} */ + +#if 0 +/* {{{ proto long gethostbyname(string name, string &addr) + Given a hostname, sets addr to be a human-readable version of the host's address */ + +/* Another lock to prevent multiple threads from grabbing gethostbyname() */ +volatile int gethostbyname_lock = 0; + +PHP_FUNCTION(gethostbyname) +{ + zval **name, **addr; + int ret; + char *tmp, *addr_string; + struct hostent *host_struct; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &name, &addr) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(name); + convert_to_string_ex(addr); + + while (gethostbyname_lock == 1); + gethostbyname_lock = 1; + + host_struct = gethostbyname(ZVAL(name, str.val)); + if (!host_struct) { + gethostbyname_lock = 0; + RETURN_LONG(-(h_errno) - 10000); /* return a value that is out of range for errno */ + } + if (host_struct->h_addrtype != AF_INET) { + gethostbyname_lock = 0; + RETURN_LONG(-EINVAL); + } + while (inet_ntoa_lock == 1); + inet_ntoa_lock = 1; + + addr_string = inet_ntoa((struct in_addr) host_struct->h_addr); + tmp = emalloc(strlen(addr_string) + 1); + strncpy(tmp, addr_string, strlen(addr_string)); + + inet_ntoa_lock = 0; + gethostbyname_lock = 0; + + ZVAL(addr, str.val) = tmp; + ZVAL(addr, str.len) = strlen(tmp); + + RETURN_LONG(0); +} + +/* }}} */ + +#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. */ + +PHP_FUNCTION(getpeername) +{ + zval **fd, **addr, **port; + char *tmp; + struct sockaddr_in sa; + int salen = sizeof(struct sockaddr_in); + int ret; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &fd, &addr, &port) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(2, fd, port); + convert_to_string_ex(addr); + + ret = getpeername(ZVAL(fd, lval), &sa, &salen); + if (ret < 0) { + 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; + + ZVAL(addr, str.val) = tmp; + ZVAL(addr, str.len) = strlen(tmp); + ZVAL(port, lval) = htons(sa.sin_port); + + RETURN_LONG(ret); + } +} +/* }}} */ + +#if 0 +/* {{{ proto long gethostbyaddr(string addr, string &name) + Given a human-readable address, sets name to be the host's name */ + +/* Another lock to prevent multiple threads from grabbing gethostbyname() */ +volatile int gethostbyaddr_lock = 0; + +PHP_FUNCTION(gethostbyaddr) +{ + zval **name, **addr; + int ret; + char *tmp, *addr_string; + struct hostent *host_struct; + struct in_addr addr_buf; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &addr, &name) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(name); + convert_to_string_ex(addr); + + ret = inet_aton(ZVAL(addr, str.val), &addr_buf); + if (ret < 0) { + RETURN_LONG(-EINVAL); + } + while (gethostbyaddr_lock == 1); + gethostbyaddr_lock = 1; + + host_struct = gethostbyname((char *) &addr_buf, sizeof(addr_buf), AF_INET); + if (!host_struct) { + gethostbyaddr_lock = 0; + RETURN_LONG(-(h_errno) - 10000); + } + if (host_struct->h_addrtype != AF_INET) { + gethostbyaddr_lock = 0; + RETURN_LONG(-EINVAL); + } + addr_string = host_struct->h_name; + tmp = emalloc(strlen(addr_string) + 1); + strncpy(tmp, addr_string, strlen(addr_string)); + + gethostbyaddr_lock = 0; + + ZVAL(addr, str.val) = tmp; + ZVAL(addr, str.len) = strlen(tmp); + + RETURN_LONG(0); +} +/* }}} */ + +#endif + +/* {{{ proto long socket(string domain, string 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); + + 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 { + zend_error(E_WARNING, "invalid socket type specified - assuming SOCK_STREAM"); + my_type = SOCK_STREAM; + } + + ret = socket(my_domain, my_type, ZVAL(protocol, lval)); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto long connect(long sockfd, string addr, long port) + Opens a connection to addr:port on the socket specified by sockfd */ +PHP_FUNCTION(connect) +{ + zval **sockfd, **addr, **port; + struct sockaddr_in sa; + int salen = sizeof(sa); + int ret; + struct in_addr addr_buf; + struct hostent *host_struct; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &sockfd, &addr, &port) == FAILURE) { + WRONG_PARAM_COUNT; + } + v_convert_to_long_ex(2, sockfd, port); + convert_to_string_ex(addr); + + bzero(&sa, sizeof(sa)); + sa.sin_port = ZVAL(port, lval); + + if (inet_aton(ZVAL(addr, str.val), &addr_buf) == 0) { + sa.sin_addr.s_addr = addr_buf.s_addr; + } else { + host_struct = gethostbyname(ZVAL(addr, str.val)); + if (host_struct->h_addrtype != AF_INET) { + RETURN_LONG(-EINVAL); + } + sa.sin_addr.s_addr = (int) *(host_struct->h_addr_list[0]); + } + + ret = connect(ZVAL(sockfd, lval), (struct sockaddr *) &sa, salen); + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +/* {{{ proto string strerror(long errno) + Returns a string describing an error */ +PHP_FUNCTION(strerror) +{ + zval **error; + const char *buf; + char *obuf; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &error) == FAILURE) { + WRONG_PARAM_COUNT; + } + if (ZVAL(error, lval) < -10000) { + ZVAL(error, lval) += 10000; + buf = hstrerror(-(ZVAL(error, lval))); + } else { + buf = strerror(-(ZVAL(error, lval))); + } + + obuf = estrndup(buf, strlen(buf)); + ZVAL(&return_value, str.val) = obuf; + ZVAL(&return_value, str.len) = strlen(buf); + return_value->type = IS_STRING; +} +/* }}} */ + +/* {{{ proto long bind(long sockfd, string domain, ...) + 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 */ +PHP_FUNCTION(bind) +{ + zval **arg0, **arg1, **arg2, **arg3; + long ret; + void **p; + int arg_count; + va_list ptr; + ELS_FETCH(); + + 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--); + + convert_to_long_ex(arg0); + convert_to_string_ex(arg1); + + if (!strcasecmp(ZVAL(arg1, str.val), "af_unix")) { + struct sockaddr_un sa; + if (ZEND_NUM_ARGS() != 3) { + 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")) { + struct sockaddr_in sa; + struct in_addr addr_buf; + if (ZEND_NUM_ARGS() != 4) { + 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)); + if (host_struct == NULL) { + RETURN_LONG(-(h_errno) - 10000); + } + sa.sin_addr.s_addr = (int) *(host_struct->h_addr_list[0]); + } else { + sa.sin_addr.s_addr = addr_buf.s_addr; + } + ret = bind(ZVAL(arg0, lval), &sa, sizeof(sa)); + } else { + RETURN_LONG(-EPROTONOSUPPORT); + } + + if (ret < 0) { + RETURN_LONG(-errno); + } else { + RETURN_LONG(ret); + } +} +/* }}} */ + +#endif /* HAVE_SOCKETS */ + + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/sockets/sockets.php b/ext/sockets/sockets.php new file mode 100644 index 0000000000..3bb624cd57 --- /dev/null +++ b/ext/sockets/sockets.php @@ -0,0 +1,10 @@ +<? +$module = 'sockets'; +$function = 'confirm_' . $module . '_compiled'; +if (extension_loaded($module)) { + $str = $function($module); +} else { + $str = "Module $module is not compiled in PHP"; +} +echo "$str\n"; +?> |