summaryrefslogtreecommitdiff
path: root/ext/standard/fsock.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard/fsock.c')
-rw-r--r--ext/standard/fsock.c520
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;
}