php-src/ext/standard/fsock.c
Wez Furlong 0a18a9d744 A add much more useful select(2) implementation than is provided by
windows sockets.  The winsock implementation will only work with sockets;
our implementation works with sockets and file descriptors.
By association, stream_select() will now operate correctly with files, pipes and sockets.

This change required linking against the winsock2 library.  In terms of
compatibility, only older versions of windows 95 do not have winsock2
installed by default.  It is available as a redistributable file, and is most likely installed by any OS patches (eg: Internet Explorer) applied by the user.

Also, add a win32 compatible pipe test when opening a stream from a pipe.  This test will only work on NT, win2k and XP platforms.  Without this test, interleaved fread() and select() calls would cause the read buffer to be clobbered.  I will be working on a fix for this issue for win9x.
2003-02-16 03:48:49 +00:00

304 lines
7.5 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 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: Paul Panotzki - Bunyip Information Systems |
| Jim Winstead <jimw@php.net> |
| Sascha Schumann <sascha@schumann.cx> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* converted to PHP Streams and moved much code to main/network.c [wez] */
/* Synced with php 3.0 revision 1.121 1999-06-18 [ssb] */
/* Synced with php 3.0 revision 1.133 1999-07-21 [sas] */
#include "php.h"
#include "php_globals.h"
#include <stdlib.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>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef PHP_WIN32
#include <winsock2.h>
#elif defined(NETWARE)
#ifdef NEW_LIBC
#ifdef USE_WINSOCK
#include <novsock2.h>
#else
#include <netinet/in.h>
#include <netdb.h>
/*#include <sys/socket.h>*/
#include <sys/select.h>
/*#else
#include <sys/socket.h>*/
#endif
#endif
#else
#include <netinet/in.h>
#include <netdb.h>
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#endif
#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
#undef AF_UNIX
#endif
#if defined(AF_UNIX)
#include <sys/un.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef PF_INET
#define PF_INET AF_INET
#endif
#ifndef PF_UNIX
#define PF_UNIX AF_UNIX
#endif
#include <string.h>
#include <errno.h>
#include "base64.h"
#include "file.h"
#include "url.h"
#include "fsock.h"
#include "php_network.h"
#ifdef ZTS
static int fsock_globals_id;
#endif
#ifdef PHP_WIN32
#define EWOULDBLOCK WSAEWOULDBLOCK
#elif defined(NETWARE)
#ifdef USE_WINSOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#endif
/* {{{ php_lookup_hostname */
/*
* Converts a host name to an IP address.
* TODO: This looks like unused code suitable for nuking.
*/
PHPAPI int php_lookup_hostname(const char *addr, struct in_addr *in)
{
struct hostent *host_info;
if (!inet_aton(addr, in)) {
/* XXX NOT THREAD SAFE */
host_info = gethostbyname(addr);
if (host_info == 0) {
/* Error: unknown host */
return -1;
}
*in = *((struct in_addr *) host_info->h_addr);
}
return 0;
}
/* }}} */
/* {{{ php_fsockopen() */
static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
{
char *host;
int host_len;
int port = -1;
zval *zerrno = NULL, *zerrstr = NULL;
double timeout = FG(default_socket_timeout);
unsigned long conv;
struct timeval tv;
char *hashkey = NULL;
php_stream *stream = NULL;
int err;
RETVAL_FALSE;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lzzd", &host, &host_len, &port, &zerrno, &zerrstr, &timeout) == FAILURE) {
RETURN_FALSE;
}
if (persistent) {
spprintf(&hashkey, 0, "pfsockopen__%s:%d", host, port);
switch(php_stream_from_persistent_id(hashkey, &stream TSRMLS_CC)) {
case PHP_STREAM_PERSISTENT_SUCCESS:
/* TODO: could check if the socket is still alive here */
php_stream_to_zval(stream, return_value);
/* fall through */
case PHP_STREAM_PERSISTENT_FAILURE:
efree(hashkey);
return;
}
}
/* prepare the timeout value for use */
conv = (unsigned long) (timeout * 1000000.0);
tv.tv_sec = conv / 1000000;
tv.tv_usec = conv % 1000000;
if (zerrno) {
zval_dtor(zerrno);
ZVAL_LONG(zerrno, 0);
}
if (zerrstr) {
zval_dtor(zerrstr);
ZVAL_STRING(zerrstr, "", 1);
}
if (port > 0) { /* connect to a host */
enum php_sslflags_t { php_ssl_none, php_ssl_v23, php_ssl_tls };
enum php_sslflags_t ssl_flags = php_ssl_none;
struct {
char *proto;
int protolen;
int socktype;
enum php_sslflags_t ssl_flags;
/* more flags to be added here */
} sockmodes[] = {
{ "udp://", 6, SOCK_DGRAM, php_ssl_none },
{ "tcp://", 6, SOCK_STREAM, php_ssl_none },
{ "ssl://", 6, SOCK_STREAM, php_ssl_v23 },
{ "tls://", 6, SOCK_STREAM, php_ssl_tls },
/* more modes to be added here */
{ NULL, 0, 0 }
};
int socktype = SOCK_STREAM;
int i;
for (i = 0; sockmodes[i].proto != NULL; i++) {
if (strncmp(host, sockmodes[i].proto, sockmodes[i].protolen) == 0) {
ssl_flags = sockmodes[i].ssl_flags;
socktype = sockmodes[i].socktype;
host += sockmodes[i].protolen;
break;
}
}
#if !HAVE_OPENSSL_EXT
if (ssl_flags != php_ssl_none) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "no SSL support in this build");
}
else
#endif
stream = php_stream_sock_open_host(host, (unsigned short)port, socktype, &tv, hashkey);
/* Preserve error */
err = php_socket_errno();
if (stream == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%d", host, port);
}
#if HAVE_OPENSSL_EXT
if (stream && ssl_flags != php_ssl_none) {
int ssl_ret = FAILURE;
switch(ssl_flags) {
case php_ssl_v23:
ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, SSLv23_client_method(), NULL TSRMLS_CC);
break;
case php_ssl_tls:
ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, TLSv1_client_method(), NULL TSRMLS_CC);
break;
default:
/* unknown ?? */
break;
}
if (ssl_ret == FAILURE)
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to activate SSL mode %d", ssl_flags);
}
#endif
} else {
/* FIXME: Win32 - this probably does not return sensible errno and errstr */
stream = php_stream_sock_open_unix(host, host_len, hashkey, &tv);
err = php_socket_errno();
}
if (hashkey)
efree(hashkey);
if (stream == NULL) {
if (zerrno) {
zval_dtor(zerrno);
ZVAL_LONG(zerrno, err);
}
if (zerrstr) {
char *buf = php_socket_strerror(err, NULL, 0);
/* no need to dup; we would only need to efree buf anyway */
zval_dtor(zerrstr);
ZVAL_STRING(zerrstr, buf, 0);
}
RETURN_FALSE;
}
php_stream_to_zval(stream, return_value);
}
/* }}} */
/* {{{ proto int fsockopen(string hostname, int port [, int errno [, string errstr [, float timeout]]])
Open Internet or Unix domain socket connection */
PHP_FUNCTION(fsockopen)
{
php_fsockopen_stream(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto int pfsockopen(string hostname, int port [, int errno [, string errstr [, float timeout]]])
Open persistent Internet or Unix domain socket connection */
PHP_FUNCTION(pfsockopen)
{
php_fsockopen_stream(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ RSHUTDOWN_FUNCTION(fsock) */
PHP_RSHUTDOWN_FUNCTION(fsock)
{
return SUCCESS;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 fdm=marker
* vim<600: sw=4 ts=4
*/