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.c445
1 files changed, 133 insertions, 312 deletions
diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c
index c1115f9a9a..29bb65ff66 100644
--- a/ext/standard/fsock.c
+++ b/ext/standard/fsock.c
@@ -28,22 +28,13 @@
+----------------------------------------------------------------------+
*/
/* $Id$ */
+
#include "php.h"
-#include "php_globals.h"
#include <stdlib.h>
-#include <stddef.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
-#if HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-
-#if HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
#include <sys/types.h>
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
@@ -93,15 +84,11 @@ function_entry fsock_functions[] = {
struct php3i_sockbuf {
int socket;
- unsigned char *readbuf;
+ 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;
@@ -154,102 +141,27 @@ 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
-//#warning "compiling without nonblocking connect support"
-{
- return connect(sockfd, addr, addrlen);
-}
-#endif
-/* }}} */
-
-
/* {{{ _php3_fsockopen() */
+
/*
This function takes an optional third argument which should be
passed by reference. The error code from the connect call is written
to this variable.
*/
static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
- pval *args[5];
+ pval *args[4];
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 > 5 || arg_count < 2 || getParametersArray(ht,arg_count,args)==FAILURE) {
+ if (arg_count > 4 || 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;
case 4:
if(!ParameterPassedByReference(ht,4)) {
php3_error(E_WARNING,"error string argument to fsockopen not passed by reference");
@@ -276,7 +188,7 @@ static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
if (persistent && _php3_hash_find(&ht_keys, key, strlen(key) + 1,
(void *) &sockp) == SUCCESS) {
- FREE_SOCK;
+ efree(key);
*sock = *sockp;
RETURN_LONG(php3_list_insert(sock, wsa_fp));
}
@@ -300,7 +212,7 @@ static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
server.sin_port = htons(portno);
- if (connect_nonb(socketd, (struct sockaddr *)&server, sizeof(server), &timeout) == SOCK_CONN_ERR) {
+ if (connect(socketd, (struct sockaddr *)&server, sizeof(server)) == SOCK_CONN_ERR) {
FREE_SOCK;
if(arg_count>2) args[2]->value.lval = errno;
if(arg_count>3) {
@@ -323,7 +235,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_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), &timeout) == SOCK_CONN_ERR) {
+ if (connect(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr)) == SOCK_CONN_ERR) {
FREE_SOCK;
if(arg_count>2) args[2]->value.lval = errno;
if(arg_count>3) {
@@ -355,19 +267,19 @@ static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
key, strlen(key) + 1, NULL);
}
if(key) efree(key);
- id = php3_list_insert(sock, wsa_fp);
+ id = php3_list_insert(sock,wsa_fp);
RETURN_LONG(id);
}
/* }}} */
-/* {{{ proto int fsockopen(string hostname, int port [, int errno [, string errstr [, int timeout]]])
+/* {{{ proto int fsockopen(string hostname, int port [, int errno [, string errstr]])
Open Internet or Unix domain socket connection */
PHP_FUNCTION(fsockopen)
{
_php3_fsockopen(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
-/* {{{ proto int pfsockopen(string hostname, int port [, int errno [, string errstr [, int timeout]]])
+/* {{{ proto int pfsockopen(string hostname, int port [, int errno [, string errstr]])
Open persistent Internet or Unix domain socket connection */
PHP_FUNCTION(pfsockopen)
{
@@ -375,37 +287,16 @@ PHP_FUNCTION(pfsockopen)
}
/* }}} */
-#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)
+/* Known issues with the socket buffering code:
+ * - does not work reliably with persistent sockets yet
+ * (buffered data is not persistent)
+ * - php3_fopen_url_wrapper() is still doing single-byte lookahead/read
+ */
-static void php_cleanup_sockbuf(int persistent)
+static php3i_sockbuf *_php3_sock_findsock(int socket)
{
- php3i_sockbuf *now, *next;
-
- for(now = phpsockbuf; now; now = next) {
- next = now->next;
- if(now->persistent == persistent) {
- SOCK_DESTROY(now);
- }
- }
-}
-
-#define TOREAD(sock) ((sock)->writepos - (sock)->readpos)
-#define READPTR(sock) ((sock)->readbuf + (sock)->readpos)
-#define WRITEPTR(sock) ((sock)->readbuf + (sock)->writepos)
+ /* FIXME: O(n) could be improved */
-#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)
@@ -415,223 +306,146 @@ static php3i_sockbuf *_php3_sock_find(int socket)
}
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 _php3_sock_eof(int socket)
{
+ php3i_sockbuf *sockbuf;
int ret = 0;
- php3i_sockbuf *sock;
- sock = _php3_sock_find(socket);
- if(sock) {
- ret = 1;
- SOCK_DESTROY(sock);
+ sockbuf = _php3_sock_findsock(socket);
+ if(sockbuf) {
+ ret = (sockbuf->writepos - sockbuf->readpos) == 0 ? 1 : 0;
}
-
- 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);
- }
- }
-
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)
+/* {{{ _php3_sock_fgets() */
+int _php3_sock_fgets(char *buf, int maxlen, int socket)
{
- char buf[CHUNK_SIZE];
- int nr_bytes;
- size_t nr_read = 0;
+ struct php3i_sockbuf *sockbuf;
+ int bytesread, toread, len, buflen, count = 0;
+ char *nl;
- if(sock->eof || max > CHUNK_SIZE) return nr_read;
+ sockbuf = _php3_sock_findsock(socket);
- 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);
+ if (sockbuf) {
+ toread = sockbuf->writepos - sockbuf->readpos;
+ if (toread > maxlen) {
+ toread = maxlen;
+ }
+ if ((nl = memchr(sockbuf->readbuf + sockbuf->readpos, '\n', toread)) != NULL) {
+ toread = (nl - (sockbuf->readbuf + sockbuf->readpos)) + 1;
+ }
+ memcpy(buf, sockbuf->readbuf + sockbuf->readpos, toread);
+ sockbuf->readpos += toread;
+ count += toread;
+ buf += toread;
+ if (sockbuf->readpos >= sockbuf->writepos) {
+ sockbuf->readpos = sockbuf->writepos = 0;
+ }
+ if (nl != NULL) {
+ /* if a newline was found, skip the recv() loop */
+ goto sock_fgets_exit;
}
- 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;
- }
-
- return nr_read;
-}
-
-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 \
- SOCK_FIND(sock,socket); \
- _php3_sock_read(sock)
-
-#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)
-
-/*
- * 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;
- SOCK_FIND_AND_READ_MAX(1);
-
- if(maxlen < 0) return ret;
-
- if(sock->is_blocked) {
- for(nr_read = 1; !sock->eof && nr_read < maxlen; ) {
- nr_read += _php3_sock_read_limited(sock, 1);
- if((p = memchr(READPTR(sock), '\n', TOREAD(sock))) != NULL) break;
+ nl = NULL;
+ buflen = 0;
+ while (count < maxlen && nl == NULL) {
+ toread = maxlen - count;
+ bytesread = recv(socket, buf, toread, 0);
+ if (bytesread <= 0) {
+ break;
+ }
+ if ((nl = memchr(buf, '\n', bytesread)) != NULL) {
+ len = (nl - buf) + 1;
+ count += len;
+ buf += len;
+ if (len < bytesread) {
+ buflen = bytesread - len;
+ break;
+ }
+ } else {
+ count += bytesread;
+ buf += bytesread;
}
- } else {
- p = memchr(READPTR(sock), '\n', MIN(TOREAD(sock), maxlen - 1));
- }
-
- 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;
+ 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;
}
- 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;
- return ret;
+ sock_fgets_exit:
+ *buf = '\0';
+ return count;
}
-/*
- * 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
- */
+/* }}} */
+/* {{{ _php3_sock_fread() */
-int _php3_sock_fgetc(int socket)
+int _php3_sock_fread(char *buf, int maxlen, int socket)
{
- int ret = EOF;
- SOCK_FIND_AND_READ_MAX(1);
-
- if(TOREAD(sock) > 0) {
- ret = *READPTR(sock);
- sock->readpos++;
+ 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;
}
- 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;
-}
-
-size_t _php3_sock_fread(char *ptr, size_t size, int socket)
-{
- size_t ret = 0;
- SOCK_FIND_AND_READ_MAX(size);
-
- if(size < 0) return ret;
-
- ret = MIN(TOREAD(sock), size);
- if(ret) {
- memcpy(ptr, READPTR(sock), ret);
- sock->readpos += ret;
+ while (count < maxlen) {
+ toread = maxlen - count;
+ bytesread = recv(socket, buf, toread, 0);
+ if (bytesread <= 0) {
+ break;
+ }
+ count += bytesread;
+ buf += bytesread;
}
- return ret;
+ *buf = '\0';
+ return count;
}
-
+/* }}} */
/* {{{ module start/shutdown functions */
/* {{{ _php3_sock_destroy */
-static void _php3_msock_destroy(int *data)
+#ifndef THREAD_SAFE
+static void _php3_sock_destroy(void *data)
{
- close(*data);
+ int *sock = (int *) data;
+ close(*sock);
}
+#endif
/* }}} */
/* {{{ php3_minit_fsock */
@@ -639,7 +453,7 @@ static int php3_minit_fsock(INIT_FUNC_ARGS)
{
#ifndef THREAD_SAFE
_php3_hash_init(&ht_keys, 0, NULL, NULL, 1);
- _php3_hash_init(&ht_socks, 0, NULL, (void (*)(void *))_php3_msock_destroy, 1);
+ _php3_hash_init(&ht_socks, 0, NULL, _php3_sock_destroy, 1);
#endif
return SUCCESS;
}
@@ -652,7 +466,6 @@ static int php3_mshutdown_fsock(SHUTDOWN_FUNC_ARGS)
_php3_hash_destroy(&ht_socks);
_php3_hash_destroy(&ht_keys);
#endif
- php_cleanup_sockbuf(1);
return SUCCESS;
}
/* }}} */
@@ -660,7 +473,15 @@ static int php3_mshutdown_fsock(SHUTDOWN_FUNC_ARGS)
static int php3_rshutdown_fsock(SHUTDOWN_FUNC_ARGS)
{
- php_cleanup_sockbuf(0);
+ struct php3i_sockbuf *sockbuf = phpsockbuf, *this;
+
+ while (sockbuf) {
+ this = sockbuf;
+ sockbuf = this->next;
+ efree(this->readbuf);
+ efree(this);
+ }
+ phpsockbuf = NULL;
return SUCCESS;
}