diff options
Diffstat (limited to 'ext/standard/fsock.c')
| -rw-r--r-- | ext/standard/fsock.c | 520 |
1 files changed, 394 insertions, 126 deletions
diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 29bb65ff66..e77de96dbb 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -30,13 +30,23 @@ /* $Id$ */ #include "php.h" +#include "php_globals.h" #include <stdlib.h> -#if HAVE_UNISTD_H +#include <stddef.h> +#ifdef HAVE_UNISTD_H #include <unistd.h> #endif +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + #include <sys/types.h> -#if HAVE_SYS_SOCKET_H +#ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif #ifdef WIN32 @@ -62,7 +72,7 @@ #include "url.h" #include "fsock.h" -#ifndef THREAD_SAFE +#ifndef ZTS extern int le_fp; #endif @@ -74,40 +84,53 @@ extern int le_fp; #include "build-defs.h" #endif +#ifndef ZTS +static HashTable ht_keys; +static HashTable ht_socks; +#endif + static unsigned char third_and_fourth_args_force_ref[] = { 4, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE }; function_entry fsock_functions[] = { - PHP_FE(fsockopen, third_and_fourth_args_force_ref) - PHP_FE(pfsockopen, third_and_fourth_args_force_ref) - {NULL, NULL, NULL} + PHP_FE(fsockopen, third_and_fourth_args_force_ref) + PHP_FE(pfsockopen, third_and_fourth_args_force_ref) + {NULL, NULL, NULL} }; struct php3i_sockbuf { int socket; - char *readbuf; + unsigned char *readbuf; size_t readbuflen; size_t readpos; size_t writepos; struct php3i_sockbuf *next; + struct php3i_sockbuf *prev; + char eof; + char persistent; + char is_blocked; }; static struct php3i_sockbuf *phpsockbuf; typedef struct php3i_sockbuf php3i_sockbuf; -static int php3_minit_fsock(INIT_FUNC_ARGS); -static int php3_mshutdown_fsock(SHUTDOWN_FUNC_ARGS); -static int php3_rshutdown_fsock(SHUTDOWN_FUNC_ARGS); - php3_module_entry fsock_module_entry = { - "Socket functions", fsock_functions, php3_minit_fsock, php3_mshutdown_fsock, NULL, php3_rshutdown_fsock, NULL, STANDARD_MODULE_PROPERTIES + "Socket functions", + fsock_functions, + php3_minit_fsock, + php3_mshutdown_fsock, + NULL, + php3_rshutdown_fsock, + NULL, + STANDARD_MODULE_PROPERTIES }; - -#ifndef THREAD_SAFE + +#ifndef ZTS static HashTable ht_keys; static HashTable ht_socks; #endif + /* {{{ lookup_hostname */ /* @@ -117,7 +140,7 @@ int lookup_hostname(const char *addr, struct in_addr *in) { struct hostent *host_info; - if(!inet_aton(addr, in)) { + if (!inet_aton(addr, in)) { host_info = gethostbyname(addr); if (host_info == 0) { /* Error: unknown host */ @@ -141,6 +164,76 @@ int _php3_is_persistent_sock(int sock) return 0; } /* }}} */ +/* {{{ connect_nonb */ +PHPAPI int connect_nonb(int sockfd, + struct sockaddr *addr, + int addrlen, + struct timeval *timeout) +{ +/* probably won't work on Win32, someone else might try it (read: fix it ;) */ +#if !defined(WIN32) && (defined(O_NONBLOCK) || defined(O_NDELAY)) + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + + int flags; + int n; + int error = 0; + int len; + int ret = 0; + fd_set rset; + fd_set wset; + + flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + + if ((n = connect(sockfd, addr, addrlen)) < 0) { + if (errno != EINPROGRESS) { + return -1; + } + } + + if (n == 0) { + goto ok; + } + + FD_ZERO(&rset); + FD_SET(sockfd, &rset); + + wset = rset; + + if ((n = select(sockfd + 1, &rset, &wset, NULL, timeout)) == 0) { + error = ETIMEDOUT; + } + + if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + len = sizeof(error); + /* + BSD-derived systems set errno correctly + Solaris returns -1 from getsockopt in case of error + */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + ret = -1; + } + } else { + /* whoops: sockfd has disappeared */ + ret = -1; + } + + ok: + fcntl(sockfd, F_SETFL, flags); + + if(error) { + errno = error; + ret = -1; + } + return ret; +#else /* !defined(WIN32) && ... */ + return connect(sockfd, addr, addrlen); +#endif +} +/* }}} */ /* {{{ _php3_fsockopen() */ /* @@ -149,19 +242,25 @@ int _php3_is_persistent_sock(int sock) to this variable. */ static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) { - pval *args[4]; + pval *args[5]; int *sock=emalloc(sizeof(int)); int *sockp; int id, arg_count=ARG_COUNT(ht); int socketd = -1; + struct timeval timeout = { 60, 0 }; unsigned short portno; char *key = NULL; + PLS_FETCH(); - if (arg_count > 4 || arg_count < 2 || getParametersArray(ht,arg_count,args)==FAILURE) { + if (arg_count > 5 || arg_count < 2 || getParametersArray(ht,arg_count,args)==FAILURE) { FREE_SOCK; WRONG_PARAM_COUNT; } switch(arg_count) { + case 5: + convert_to_long(args[4]); + timeout.tv_sec = args[4]->value.lval; + /* fall-through */ case 4: if(!ParameterPassedByReference(ht,4)) { php3_error(E_WARNING,"error string argument to fsockopen not passed by reference"); @@ -212,7 +311,7 @@ static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) { server.sin_port = htons(portno); - if (connect(socketd, (struct sockaddr *)&server, sizeof(server)) == SOCK_CONN_ERR) { + if (connect_nonb(socketd, (struct sockaddr *)&server, sizeof(server), &timeout) == SOCK_CONN_ERR) { FREE_SOCK; if(arg_count>2) args[2]->value.lval = errno; if(arg_count>3) { @@ -235,7 +334,7 @@ static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) { unix_addr.sun_family = AF_UNIX; strcpy(unix_addr.sun_path, args[0]->value.str.val); - if (connect(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr)) == SOCK_CONN_ERR) { + if (connect_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), &timeout) == SOCK_CONN_ERR) { FREE_SOCK; if(arg_count>2) args[2]->value.lval = errno; if(arg_count>3) { @@ -320,168 +419,337 @@ int _php3_sock_eof(int socket) return ret; } -/* {{{ _php3_sock_fgets() */ -int _php3_sock_fgets(char *buf, int maxlen, int socket) +#define SOCK_DESTROY(sock) \ + if(sock->readbuf) pefree(sock->readbuf, sock->persistent); \ + if(sock->prev) sock->prev->next = sock->next; \ + if(sock->next) sock->next->prev = sock->prev; \ + if(sock == phpsockbuf) \ + phpsockbuf = sock->next; \ + pefree(sock, sock->persistent) + +static void php_cleanup_sockbuf(int persistent) { - struct php3i_sockbuf *sockbuf; - int bytesread, toread, len, buflen, count = 0; - char *nl; - - sockbuf = _php3_sock_findsock(socket); - - if (sockbuf) { - toread = sockbuf->writepos - sockbuf->readpos; - if (toread > maxlen) { - toread = maxlen; + php3i_sockbuf *now, *next; + + for(now = phpsockbuf; now; now = next) { + next = now->next; + if(now->persistent == persistent) { + SOCK_DESTROY(now); } - if ((nl = memchr(sockbuf->readbuf + sockbuf->readpos, '\n', toread)) != NULL) { - toread = (nl - (sockbuf->readbuf + sockbuf->readpos)) + 1; + } +} + +#define TOREAD(sock) ((sock)->writepos - (sock)->readpos) +#define READPTR(sock) ((sock)->readbuf + (sock)->readpos) +#define WRITEPTR(sock) ((sock)->readbuf + (sock)->writepos) +#define SOCK_FIND(sock,socket) \ + php3i_sockbuf *sock; \ + sock = _php3_sock_find(socket); \ + if(!sock) sock = _php3_sock_create(socket) + +static php3i_sockbuf *_php3_sock_find(int socket) +{ + php3i_sockbuf *buf = NULL, *tmp; + + for (tmp = phpsockbuf; tmp; tmp = tmp->next) { + if (tmp->socket == socket) { + buf = tmp; + break; } - memcpy(buf, sockbuf->readbuf + sockbuf->readpos, toread); - sockbuf->readpos += toread; - count += toread; - buf += toread; - if (sockbuf->readpos >= sockbuf->writepos) { - sockbuf->readpos = sockbuf->writepos = 0; + } + return buf; +} + +static php3i_sockbuf *_php3_sock_create(int socket) +{ + php3i_sockbuf *sock; + int persistent = _php3_is_persistent_sock(socket); + + sock = pecalloc(sizeof(*sock), 1, persistent); + sock->socket = socket; + if ((sock->next = phpsockbuf)) { + phpsockbuf->prev = sock; + } + sock->persistent = persistent; + sock->is_blocked = 1; + phpsockbuf = sock; + + return sock; +} + +int _php3_sock_destroy(int socket) +{ + int ret = 0; + php3i_sockbuf *sock; + + sock = _php3_sock_find(socket); + if (sock) { + ret = 1; + SOCK_DESTROY(sock); + } + + return ret; +} + +int _php3_sock_close(int socket) +{ + int ret = 0; + php3i_sockbuf *sock; + + sock = _php3_sock_find(socket); + if (sock) { + if (!sock->persistent) { +#if HAVE_SHUTDOWN + shutdown(sock->socket, 0); +#endif +#if WIN32||WINNT + closesocket(sock->socket); +#else + close(sock->socket); +#endif + SOCK_DESTROY(sock); } - if (nl != NULL) { - /* if a newline was found, skip the recv() loop */ - goto sock_fgets_exit; + } + + return ret; +} + +#define CHUNK_SIZE 2048 +#define MAX_CHUNKS_PER_READ 10 + + +static size_t _php3_sock_read_limited(php3i_sockbuf *sock, size_t max) +{ + char buf[CHUNK_SIZE]; + int nr_bytes; + size_t nr_read = 0; + + if (sock->eof || max > CHUNK_SIZE) { + return nr_read; + } + + nr_bytes = recv(sock->socket, buf, max, 0); + if (nr_bytes > 0) { + if (sock->writepos + nr_bytes > sock->readbuflen) { + sock->readbuflen += CHUNK_SIZE; + sock->readbuf = perealloc(sock->readbuf, sock->readbuflen, + sock->persistent); } + memcpy(WRITEPTR(sock), buf, nr_bytes); + sock->writepos += nr_bytes; + nr_read = nr_bytes; + } else if (nr_bytes == 0 || (nr_bytes < 0 && errno != EWOULDBLOCK)) { + sock->eof = 1; } - nl = NULL; - buflen = 0; - while (count < maxlen && nl == NULL) { - toread = maxlen - count; - bytesread = recv(socket, buf, toread, 0); - if (bytesread <= 0) { - break; + return nr_read; +} + +#if !defined(MSG_DONTWAIT) && defined(__GLIBC__) && __GLIBC__ == 2 +# define MSG_DONTWAIT 0x40 +#endif + +/* only for blocking sockets */ +static size_t _php3_sock_queued_data(php3i_sockbuf *sock) +{ + int nr_bytes; + char buf[CHUNK_SIZE]; + int flags = MSG_PEEK; + +#ifdef MSG_DONTWAIT + flags |= MSG_DONTWAIT; +#else + _php3_set_sock_blocking(sock->socket, 1); +#endif + + nr_bytes = recv(sock->socket, buf, CHUNK_SIZE, flags); + +#ifndef MSG_DONTWAIT + _php3_set_sock_blocking(sock->socket, 0); +#endif + + return (nr_bytes < 0 ? 0 : nr_bytes); +} + +static size_t _php3_sock_read(php3i_sockbuf *sock) +{ + size_t nr_bytes; + size_t nr_read = 0; + int i; + + for (i = 0; !sock->eof && i < MAX_CHUNKS_PER_READ; i++) { + nr_bytes = _php3_sock_read_limited(sock, CHUNK_SIZE); + if(nr_bytes == 0) break; + nr_read += nr_bytes; + } + + return nr_read; +} + +int _php3_sock_set_blocking(int socket, int mode) +{ + int old; + SOCK_FIND(sock, socket); + + old = sock->is_blocked; + + sock->is_blocked = mode; + + return old; +} + +#define SOCK_FIND_AND_READ_MAX(max) \ + SOCK_FIND(sock, socket); \ + if(sock->is_blocked) _php3_sock_read_limited(sock, max); else _php3_sock_read(sock) + +/* {{{ _php3_sock_fgets() */ +/* + * FIXME: fgets depends on '\n' as line delimiters + */ +char *_php3_sock_fgets(char *buf, size_t maxlen, int socket) +{ + char *p = NULL; + char *ret = NULL; + size_t amount = 0; + size_t nr_read; + size_t nr_toread; + SOCK_FIND(sock, socket); + + if (maxlen < 0) { + return ret; + } + + if (sock->is_blocked) { + nr_toread = _php3_sock_queued_data(sock); + if (!nr_toread) { + nr_toread = 1; } - if ((nl = memchr(buf, '\n', bytesread)) != NULL) { - len = (nl - buf) + 1; - count += len; - buf += len; - if (len < bytesread) { - buflen = bytesread - len; + for (nr_read = 1; !sock->eof && nr_read < maxlen; ) { + nr_read += _php3_sock_read_limited(sock, nr_toread); + if ((p = memchr(READPTR(sock), '\n', TOREAD(sock))) != NULL) { break; } - } else { - count += bytesread; - buf += bytesread; + nr_toread = 1; } + } else { + _php3_sock_read(sock); + p = memchr(READPTR(sock), '\n', MIN(TOREAD(sock), maxlen - 1)); } - if (buflen > 0) { /* there was data after the "\n" ... */ - if (sockbuf == NULL) { - sockbuf = emalloc(sizeof(struct php3i_sockbuf)); - sockbuf->socket = socket; - sockbuf->readbuf = emalloc(maxlen); - sockbuf->readbuflen = maxlen; - sockbuf->readpos = sockbuf->writepos = 0; - sockbuf->next = phpsockbuf; - phpsockbuf = sockbuf; - } else { - uint needlen = sockbuf->writepos + buflen; - - if (needlen > sockbuf->readbuflen) { - sockbuf->readbuflen += maxlen; - sockbuf->readbuf = erealloc(sockbuf->readbuf, sockbuf->readbuflen); - } - } - memcpy(sockbuf->readbuf + sockbuf->writepos, buf, buflen); - sockbuf->writepos += buflen; + if (p) { + amount = (ptrdiff_t) p - (ptrdiff_t) READPTR(sock) + 1; + } else { + amount = MIN(TOREAD(sock), maxlen - 1); + } + + if (amount > 0) { + memcpy(buf, READPTR(sock), amount); + sock->readpos += amount; + } + buf[amount] = '\0'; + + /* signal error only, if we don't return data from this call and + if there is no data to read and if the eof flag is set */ + if (amount || TOREAD(sock) || !sock->eof) { + ret = buf; } - sock_fgets_exit: - *buf = '\0'; - return count; + return ret; } /* }}} */ + +/* + * FIXME: fgetc returns EOF, if no data is available on a nonblocking socket. + * I don't have any documentation on the semantics of fgetc in this case. + * + * ss@2ns.de 19990528 + */ + +int _php3_sock_fgetc(int socket) +{ + int ret = EOF; + SOCK_FIND_AND_READ_MAX(1); + + if (TOREAD(sock) > 0) { + ret = *READPTR(sock); + sock->readpos++; + } + + return ret; +} + +int _php3_sock_feof(int socket) +{ + int ret = 0; + SOCK_FIND_AND_READ_MAX(1); + + if (!TOREAD(sock) && sock->eof) { + ret = 1; + } + + return ret; +} + /* {{{ _php3_sock_fread() */ -int _php3_sock_fread(char *buf, int maxlen, int socket) +size_t _php3_sock_fread(char *ptr, size_t size, int socket) { - struct php3i_sockbuf *sockbuf = phpsockbuf; - int bytesread, toread, count = 0; - - while (sockbuf) { - if (sockbuf->socket == socket) { - toread = sockbuf->writepos - sockbuf->readpos; - if (toread > maxlen) { - toread = maxlen; - } - memcpy(buf, sockbuf->readbuf + sockbuf->readpos, toread); - sockbuf->readpos += toread; - count += toread; - buf += toread; - break; - } - sockbuf = sockbuf->next; + size_t ret = 0; + SOCK_FIND_AND_READ_MAX(size); + + if (size < 0) { + return ret; } - while (count < maxlen) { - toread = maxlen - count; - bytesread = recv(socket, buf, toread, 0); - if (bytesread <= 0) { - break; - } - count += bytesread; - buf += bytesread; + ret = MIN(TOREAD(sock), size); + if (ret) { + memcpy(ptr, READPTR(sock), ret); + sock->readpos += ret; } - *buf = '\0'; - return count; + return ret; } /* }}} */ /* {{{ module start/shutdown functions */ /* {{{ _php3_sock_destroy */ -#ifndef THREAD_SAFE -static void _php3_sock_destroy(void *data) +#ifndef ZTS +static void _php3_msock_destroy(int *data) { - int *sock = (int *) data; - close(*sock); + close(*data); } #endif /* }}} */ /* {{{ php3_minit_fsock */ -static int php3_minit_fsock(INIT_FUNC_ARGS) +int php3_minit_fsock(INIT_FUNC_ARGS) { -#ifndef THREAD_SAFE +#ifndef ZTS _php3_hash_init(&ht_keys, 0, NULL, NULL, 1); - _php3_hash_init(&ht_socks, 0, NULL, _php3_sock_destroy, 1); + _php3_hash_init(&ht_socks, 0, NULL, (void (*)(void *))_php3_msock_destroy, 1); #endif return SUCCESS; } /* }}} */ /* {{{ php3_mshutdown_fsock */ -static int php3_mshutdown_fsock(SHUTDOWN_FUNC_ARGS) +int php3_mshutdown_fsock(SHUTDOWN_FUNC_ARGS) { -#ifndef THREAD_SAFE +#ifndef ZTS _php3_hash_destroy(&ht_socks); _php3_hash_destroy(&ht_keys); #endif + php_cleanup_sockbuf(1); return SUCCESS; } /* }}} */ /* {{{ php3_rshutdown_fsock() */ -static int php3_rshutdown_fsock(SHUTDOWN_FUNC_ARGS) +int php3_rshutdown_fsock(SHUTDOWN_FUNC_ARGS) { - struct php3i_sockbuf *sockbuf = phpsockbuf, *this; - - while (sockbuf) { - this = sockbuf; - sockbuf = this->next; - efree(this->readbuf); - efree(this); - } - phpsockbuf = NULL; + php_cleanup_sockbuf(0); return SUCCESS; } |
