#include "php.h"
#include <stddef.h>
#include <errno.h>
#ifdef PHP_WIN32
# include <Ws2tcpip.h>
# include "win32/inet.h"
# define O_RDONLY _O_RDONLY
# include "win32/param.h"
#else
#include <sys/param.h>
#endif
#include <sys/types.h>
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifndef _FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if HAVE_POLL_H
#include <poll.h>
#elif HAVE_SYS_POLL_H
#include <sys/poll.h>
#endif
#ifndef PHP_WIN32
#include <netinet/in.h>
#include <netdb.h>
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#endif
#ifndef HAVE_INET_ATON
int inet_aton(const char *, struct in_addr *);
#endif
#include "php_network.h"
#if defined(PHP_WIN32) || defined(__riscos__)
#undef AF_UNIX
#endif
#if defined(AF_UNIX)
#include <sys/un.h>
#endif
#include "ext/standard/file.h"
#ifdef PHP_WIN32
# include "win32/time.h"
# define SOCK_ERR INVALID_SOCKET
# define SOCK_CONN_ERR SOCKET_ERROR
# define PHP_TIMEOUT_ERROR_VALUE WSAETIMEDOUT
#if HAVE_IPV6
const struct in6_addr in6addr_any = {0};
#endif
#else
# define SOCK_ERR -1
# define SOCK_CONN_ERR -1
# define PHP_TIMEOUT_ERROR_VALUE ETIMEDOUT
#endif
#if HAVE_GETADDRINFO
#ifdef HAVE_GAI_STRERROR
# define PHP_GAI_STRERROR(x) (gai_strerror(x))
#else
# define PHP_GAI_STRERROR(x) (php_gai_strerror(x))
static const char *php_gai_strerror(int code)
{
static struct {
int code;
const char *msg;
} values[] = {
# ifdef EAI_ADDRFAMILY
{EAI_ADDRFAMILY, "Address family for hostname not supported"},
# endif
{EAI_AGAIN, "Temporary failure in name resolution"},
{EAI_BADFLAGS, "Bad value for ai_flags"},
{EAI_FAIL, "Non-recoverable failure in name resolution"},
{EAI_FAMILY, "ai_family not supported"},
{EAI_MEMORY, "Memory allocation failure"},
# ifdef EAI_NODATA
{EAI_NODATA, "No address associated with hostname"},
# endif
{EAI_NONAME, "Name or service not known"},
{EAI_SERVICE, "Servname not supported for ai_socktype"},
{EAI_SOCKTYPE, "ai_socktype not supported"},
{EAI_SYSTEM, "System error"},
{0, NULL}
};
int i;
for (i = 0; values[i].msg != NULL; i++) {
if (values[i].code == code) {
return (char *)values[i].msg;
}
}
return "Unknown error";
}
#endif
#endif
PHPAPI void php_network_freeaddresses(struct sockaddr **sal)
{
struct sockaddr **sap;
if (sal == NULL)
return;
for (sap = sal; *sap != NULL; sap++)
efree(*sap);
efree(sal);
}
PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string)
{
struct sockaddr **sap;
int n;
#if HAVE_GETADDRINFO
# if HAVE_IPV6
static int ipv6_borked = -1;
# endif
struct addrinfo hints, *res, *sai;
#else
struct hostent *host_info;
struct in_addr in;
#endif
if (host == NULL) {
return 0;
}
#if HAVE_GETADDRINFO
memset(&hints, '\0', sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = socktype;
# if HAVE_IPV6
if (ipv6_borked == -1) {
int s;
s = socket(PF_INET6, SOCK_DGRAM, 0);
if (s == SOCK_ERR) {
ipv6_borked = 1;
} else {
ipv6_borked = 0;
closesocket(s);
}
}
hints.ai_family = ipv6_borked ? AF_INET : AF_UNSPEC;
# endif
if ((n = getaddrinfo(host, NULL, &hints, &res))) {
if (error_string) {
*error_string = strpprintf(0, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string));
} else {
php_error_docref(NULL, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
}
return 0;
} else if (res == NULL) {
if (error_string) {
*error_string = strpprintf(0, "php_network_getaddresses: getaddrinfo failed (null result pointer) errno=%d", errno);
php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string));
} else {
php_error_docref(NULL, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)");
}
return 0;
}
sai = res;
for (n = 1; (sai = sai->ai_next) != NULL; n++)
;
*sal = safe_emalloc((n + 1), sizeof(*sal), 0);
sai = res;
sap = *sal;
do {
*sap = emalloc(sai->ai_addrlen);
memcpy(*sap, sai->ai_addr, sai->ai_addrlen);
sap++;
} while ((sai = sai->ai_next) != NULL);
freeaddrinfo(res);
#else
if (!inet_aton(host, &in)) {
if(strlen(host) > MAXFQDNLEN) {
host_info = NULL;
errno = E2BIG;
} else {
host_info = php_network_gethostbyname(host);
}
if (host_info == NULL) {
if (error_string) {
*error_string = strpprintf(0, "php_network_getaddresses: gethostbyname failed. errno=%d", errno);
php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(*error_string));
} else {
php_error_docref(NULL, E_WARNING, "php_network_getaddresses: gethostbyname failed");
}
return 0;
}
in = *((struct in_addr *) host_info->h_addr);
}
*sal = safe_emalloc(2, sizeof(*sal), 0);
sap = *sal;
*sap = emalloc(sizeof(struct sockaddr_in));
(*sap)->sa_family = AF_INET;
((struct sockaddr_in *)*sap)->sin_addr = in;
sap++;
n = 1;
#endif
*sap = NULL;
return n;
}
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
#ifdef PHP_WIN32
typedef u_long php_non_blocking_flags_t;
# define SET_SOCKET_BLOCKING_MODE(sock, save) \
save = TRUE; ioctlsocket(sock, FIONBIO, &save)
# define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
ioctlsocket(sock, FIONBIO, &save)
#else
typedef int php_non_blocking_flags_t;
# define SET_SOCKET_BLOCKING_MODE(sock, save) \
save = fcntl(sock, F_GETFL, 0); \
fcntl(sock, F_SETFL, save | O_NONBLOCK)
# define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \
fcntl(sock, F_SETFL, save)
#endif
PHPAPI int php_network_connect_socket(php_socket_t sockfd,
const struct sockaddr *addr,
socklen_t addrlen,
int asynchronous,
struct timeval *timeout,
zend_string **error_string,
int *error_code)
{
php_non_blocking_flags_t orig_flags;
int n;
int error = 0;
socklen_t len;
int ret = 0;
SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
if ((n = connect(sockfd, addr, addrlen)) != 0) {
error = php_socket_errno();
if (error_code) {
*error_code = error;
}
if (error != EINPROGRESS) {
if (error_string) {
*error_string = php_socket_error_str(error);
}
return -1;
}
if (asynchronous && error == EINPROGRESS) {
return 0;
}
}
if (n == 0) {
goto ok;
}
# ifdef PHP_WIN32
if ((n = php_pollfd_for(sockfd, POLLOUT|POLLPRI, timeout)) == 0) {
#else
if ((n = php_pollfd_for(sockfd, PHP_POLLREADABLE|POLLOUT, timeout)) == 0) {
#endif
error = PHP_TIMEOUT_ERROR_VALUE;
}
if (n > 0) {
len = sizeof(error);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) {
ret = -1;
}
} else {
ret = -1;
}
ok:
if (!asynchronous) {
RESTORE_SOCKET_BLOCKING_MODE(sockfd, orig_flags);
}
if (error_code) {
*error_code = error;
}
if (error) {
ret = -1;
if (error_string) {
*error_string = php_socket_error_str(error);
}
}
return ret;
}
static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result)
{
result->tv_usec = a.tv_usec - b.tv_usec;
if (result->tv_usec < 0L) {
a.tv_sec--;
result->tv_usec += 1000000L;
}
result->tv_sec = a.tv_sec - b.tv_sec;
if (result->tv_sec < 0L) {
result->tv_sec++;
result->tv_usec -= 1000000L;
}
}
php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port,
int socktype, long sockopts, zend_string **error_string, int *error_code
)
{
int num_addrs, n, err = 0;
php_socket_t sock;
struct sockaddr **sal, **psal, *sa;
socklen_t socklen;
int sockoptval = 1;
num_addrs = php_network_getaddresses(host, socktype, &psal, error_string);
if (num_addrs == 0) {
return -1;
}
for (sal = psal; *sal != NULL; sal++) {
sa = *sal;
sock = socket(sa->sa_family, socktype, 0);
if (sock == SOCK_ERR) {
continue;
}
switch (sa->sa_family) {
#if HAVE_GETADDRINFO && HAVE_IPV6
case AF_INET6:
((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
socklen = sizeof(struct sockaddr_in6);
break;
#endif
case AF_INET:
((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
((struct sockaddr_in *)sa)->sin_port = htons(port);
socklen = sizeof(struct sockaddr_in);
break;
default:
socklen = 0;
sa = NULL;
}
if (sa) {
#ifdef SO_REUSEADDR
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&sockoptval, sizeof(sockoptval));
#endif
#ifdef IPV6_V6ONLY
if (sockopts & STREAM_SOCKOP_IPV6_V6ONLY) {
int ipv6_val = !!(sockopts & STREAM_SOCKOP_IPV6_V6ONLY_ENABLED);
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6_val, sizeof(sockoptval));
}
#endif
#ifdef SO_REUSEPORT
if (sockopts & STREAM_SOCKOP_SO_REUSEPORT) {
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&sockoptval, sizeof(sockoptval));
}
#endif
#ifdef SO_BROADCAST
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
}
#endif
#ifdef TCP_NODELAY
if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
}
#endif
n = bind(sock, sa, socklen);
if (n != SOCK_CONN_ERR) {
goto bound;
}
err = php_socket_errno();
}
closesocket(sock);
}
sock = -1;
if (error_code) {
*error_code = err;
}
if (error_string) {
*error_string = php_socket_error_str(err);
}
bound:
php_network_freeaddresses(psal);
return sock;
}
PHPAPI int php_network_parse_network_address_with_port(const char *addr, zend_long addrlen, struct sockaddr *sa, socklen_t *sl)
{
char *colon;
char *tmp;
int ret = FAILURE;
short port;
struct sockaddr_in *in4 = (struct sockaddr_in*)sa;
struct sockaddr **psal;
int n;
zend_string *errstr = NULL;
#if HAVE_IPV6
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
#endif
memset(sa, 0, sizeof(struct sockaddr));
if (*addr == '[') {
colon = memchr(addr + 1, ']', addrlen-1);
if (!colon || colon[1] != ':') {
return FAILURE;
}
port = atoi(colon + 2);
addr++;
} else {
colon = memchr(addr, ':', addrlen);
if (!colon) {
return FAILURE;
}
port = atoi(colon + 1);
}
tmp = estrndup(addr, colon - addr);
#if HAVE_IPV6 && HAVE_INET_PTON
if (inet_pton(AF_INET6, tmp, &in6->sin6_addr) > 0) {
in6->sin6_port = htons(port);
in6->sin6_family = AF_INET6;
*sl = sizeof(struct sockaddr_in6);
ret = SUCCESS;
goto out;
}
#endif
if (inet_aton(tmp, &in4->sin_addr) > 0) {
in4->sin_port = htons(port);
in4->sin_family = AF_INET;
*sl = sizeof(struct sockaddr_in);
ret = SUCCESS;
goto out;
}
n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr);
if (n == 0) {
if (errstr) {
php_error_docref(NULL, E_WARNING, "Failed to resolve `%s': %s", tmp, ZSTR_VAL(errstr));
zend_string_release_ex(errstr, 0);
}
goto out;
}
switch ((*psal)->sa_family) {
#if HAVE_GETADDRINFO && HAVE_IPV6
case AF_INET6:
*in6 = **(struct sockaddr_in6**)psal;
in6->sin6_port = htons(port);
*sl = sizeof(struct sockaddr_in6);
ret = SUCCESS;
break;
#endif
case AF_INET:
*in4 = **(struct sockaddr_in**)psal;
in4->sin_port = htons(port);
*sl = sizeof(struct sockaddr_in);
ret = SUCCESS;
break;
}
php_network_freeaddresses(psal);
out:
efree(tmp);
return ret;
}
PHPAPI void php_network_populate_name_from_sockaddr(
struct sockaddr *sa, socklen_t sl,
zend_string **textaddr,
struct sockaddr **addr,
socklen_t *addrlen
)
{
if (addr) {
*addr = emalloc(sl);
memcpy(*addr, sa, sl);
*addrlen = sl;
}
if (textaddr) {
#if HAVE_IPV6 && HAVE_INET_NTOP
char abuf[256];
#endif
char *buf = NULL;
switch (sa->sa_family) {
case AF_INET:
buf = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr);
if (buf) {
*textaddr = strpprintf(0, "%s:%d",
buf, ntohs(((struct sockaddr_in*)sa)->sin_port));
}
break;
#if HAVE_IPV6 && HAVE_INET_NTOP
case AF_INET6:
buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf));
if (buf) {
*textaddr = strpprintf(0, "[%s]:%d",
buf, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
}
break;
#endif
#ifdef AF_UNIX
case AF_UNIX:
{
struct sockaddr_un *ua = (struct sockaddr_un*)sa;
if (ua->sun_path[0] == '\0') {
int len = sl - sizeof(sa_family_t);
*textaddr = zend_string_init((char*)ua->sun_path, len, 0);
} else {
int len = strlen(ua->sun_path);
*textaddr = zend_string_init((char*)ua->sun_path, len, 0);
}
}
break;
#endif
}
}
}
PHPAPI int php_network_get_peer_name(php_socket_t sock,
zend_string **textaddr,
struct sockaddr **addr,
socklen_t *addrlen
)
{
php_sockaddr_storage sa;
socklen_t sl = sizeof(sa);
memset(&sa, 0, sizeof(sa));
if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) {
php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
textaddr,
addr, addrlen
);
return 0;
}
return -1;
}
PHPAPI int php_network_get_sock_name(php_socket_t sock,
zend_string **textaddr,
struct sockaddr **addr,
socklen_t *addrlen
)
{
php_sockaddr_storage sa;
socklen_t sl = sizeof(sa);
memset(&sa, 0, sizeof(sa));
if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) {
php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
textaddr,
addr, addrlen
);
return 0;
}
return -1;
}
PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
zend_string **textaddr,
struct sockaddr **addr,
socklen_t *addrlen,
struct timeval *timeout,
zend_string **error_string,
int *error_code,
int tcp_nodelay
)
{
php_socket_t clisock = -1;
int error = 0, n;
php_sockaddr_storage sa;
socklen_t sl;
n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout);
if (n == 0) {
error = PHP_TIMEOUT_ERROR_VALUE;
} else if (n == -1) {
error = php_socket_errno();
} else {
sl = sizeof(sa);
clisock = accept(srvsock, (struct sockaddr*)&sa, &sl);
if (clisock != SOCK_ERR) {
php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
textaddr,
addr, addrlen
);
if (tcp_nodelay) {
#ifdef TCP_NODELAY
setsockopt(clisock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcp_nodelay, sizeof(tcp_nodelay));
#endif
}
} else {
error = php_socket_errno();
}
}
if (error_code) {
*error_code = error;
}
if (error_string) {
*error_string = php_socket_error_str(error);
}
return clisock;
}
php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
int socktype, int asynchronous, struct timeval *timeout, zend_string **error_string,
int *error_code, char *bindto, unsigned short bindport, long sockopts
)
{
int num_addrs, n, fatal = 0;
php_socket_t sock;
struct sockaddr **sal, **psal, *sa;
struct timeval working_timeout;
socklen_t socklen;
#if HAVE_GETTIMEOFDAY
struct timeval limit_time, time_now;
#endif
num_addrs = php_network_getaddresses(host, socktype, &psal, error_string);
if (num_addrs == 0) {
return -1;
}
if (timeout) {
memcpy(&working_timeout, timeout, sizeof(working_timeout));
#if HAVE_GETTIMEOFDAY
gettimeofday(&limit_time, NULL);
limit_time.tv_sec += working_timeout.tv_sec;
limit_time.tv_usec += working_timeout.tv_usec;
if (limit_time.tv_usec >= 1000000) {
limit_time.tv_usec -= 1000000;
limit_time.tv_sec++;
}
#endif
}
for (sal = psal; !fatal && *sal != NULL; sal++) {
sa = *sal;
sock = socket(sa->sa_family, socktype, 0);
if (sock == SOCK_ERR) {
continue;
}
switch (sa->sa_family) {
#if HAVE_GETADDRINFO && HAVE_IPV6
case AF_INET6:
if (!bindto || strchr(bindto, ':')) {
((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family;
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
socklen = sizeof(struct sockaddr_in6);
} else {
socklen = 0;
sa = NULL;
}
break;
#endif
case AF_INET:
((struct sockaddr_in *)sa)->sin_family = sa->sa_family;
((struct sockaddr_in *)sa)->sin_port = htons(port);
socklen = sizeof(struct sockaddr_in);
break;
default:
socklen = 0;
sa = NULL;
}
if (sa) {
if (bindto) {
struct sockaddr *local_address = NULL;
int local_address_len = 0;
if (sa->sa_family == AF_INET) {
if (strchr(bindto,':')) {
goto skip_bind;
}
struct sockaddr_in *in4 = emalloc(sizeof(struct sockaddr_in));
local_address = (struct sockaddr*)in4;
local_address_len = sizeof(struct sockaddr_in);
in4->sin_family = sa->sa_family;
in4->sin_port = htons(bindport);
if (!inet_aton(bindto, &in4->sin_addr)) {
php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
goto skip_bind;
}
memset(&(in4->sin_zero), 0, sizeof(in4->sin_zero));
}
#if HAVE_IPV6 && HAVE_INET_PTON
else {
struct sockaddr_in6 *in6 = emalloc(sizeof(struct sockaddr_in6));
local_address = (struct sockaddr*)in6;
local_address_len = sizeof(struct sockaddr_in6);
in6->sin6_family = sa->sa_family;
in6->sin6_port = htons(bindport);
if (inet_pton(AF_INET6, bindto, &in6->sin6_addr) < 1) {
php_error_docref(NULL, E_WARNING, "Invalid IP Address: %s", bindto);
goto skip_bind;
}
}
#endif
if (!local_address || bind(sock, local_address, local_address_len)) {
php_error_docref(NULL, E_WARNING, "failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
}
skip_bind:
if (local_address) {
efree(local_address);
}
}
if (error_string && *error_string) {
zend_string_release_ex(*error_string, 0);
*error_string = NULL;
}
#ifdef SO_BROADCAST
{
int val = 1;
if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&val, sizeof(val));
}
}
#endif
#ifdef TCP_NODELAY
{
int val = 1;
if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
}
}
#endif
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
timeout ? &working_timeout : NULL,
error_string, error_code);
if (n != -1) {
goto connected;
}
#if HAVE_GETTIMEOFDAY
if (timeout) {
gettimeofday(&time_now, NULL);
if (timercmp(&time_now, &limit_time, >=)) {
fatal = 1;
} else {
sub_times(limit_time, time_now, &working_timeout);
}
}
#else
if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) {
fatal = 1;
} else {
if (timeout) {
memcpy(&working_timeout, timeout, sizeof(working_timeout));
}
}
#endif
}
closesocket(sock);
}
sock = -1;
connected:
php_network_freeaddresses(psal);
return sock;
}
PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port)
{
memset(addr, 0, sizeof(php_sockaddr_storage));
switch (family) {
#if HAVE_IPV6
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
sin6->sin6_addr = in6addr_any;
break;
}
#endif
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *) addr;
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
sin->sin_addr.s_addr = htonl(INADDR_ANY);
break;
}
}
}
PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr)
{
switch (((struct sockaddr *)addr)->sa_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
#if HAVE_IPV6
case AF_INET6:
return sizeof(struct sockaddr_in6);
#endif
#ifdef AF_UNIX
case AF_UNIX:
return sizeof(struct sockaddr_un);
#endif
default:
return 0;
}
}
PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize)
{
#ifndef PHP_WIN32
char *errstr;
errstr = strerror(err);
if (buf == NULL) {
buf = estrdup(errstr);
} else {
strncpy(buf, errstr, bufsize);
buf[bufsize?(bufsize-1):0] = 0;
}
return buf;
#else
char *sysbuf;
int free_it = 1;
if (!FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&sysbuf,
0,
NULL)) {
free_it = 0;
sysbuf = "Unknown Error";
}
if (buf == NULL) {
buf = estrdup(sysbuf);
} else {
strncpy(buf, sysbuf, bufsize);
buf[bufsize?(bufsize-1):0] = 0;
}
if (free_it) {
LocalFree(sysbuf);
}
return buf;
#endif
}
PHPAPI zend_string *php_socket_error_str(long err)
{
#ifndef PHP_WIN32
char *errstr;
errstr = strerror(err);
return zend_string_init(errstr, strlen(errstr), 0);
#else
zend_string *ret;
char *sysbuf;
int free_it = 1;
if (!FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&sysbuf,
0,
NULL)) {
free_it = 0;
sysbuf = "Unknown Error";
}
ret = zend_string_init(sysbuf, strlen(sysbuf), 0);
if (free_it) {
LocalFree(sysbuf);
}
return ret;
#endif
}
PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC)
{
php_stream *stream;
php_netstream_data_t *sock;
sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
memset(sock, 0, sizeof(php_netstream_data_t));
sock->is_blocked = 1;
sock->timeout.tv_sec = FG(default_socket_timeout);
sock->timeout.tv_usec = 0;
sock->socket = socket;
stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+");
if (stream == NULL) {
pefree(sock, persistent_id ? 1 : 0);
} else {
stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;
}
return stream;
}
PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port,
int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC)
{
char *res;
zend_long reslen;
php_stream *stream;
reslen = spprintf(&res, 0, "tcp://%s:%d", host, port);
stream = php_stream_xport_create(res, reslen, REPORT_ERRORS,
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL);
efree(res);
return stream;
}
PHPAPI int php_set_sock_blocking(php_socket_t socketd, int block)
{
int ret = SUCCESS;
#ifdef PHP_WIN32
u_long flags;
flags = !block;
if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) {
ret = FAILURE;
}
#else
int myflag = 0;
int flags = fcntl(socketd, F_GETFL);
#ifdef O_NONBLOCK
myflag = O_NONBLOCK;
#elif defined(O_NDELAY)
myflag = O_NDELAY;
#endif
if (!block) {
flags |= myflag;
} else {
flags &= ~myflag;
}
if (fcntl(socketd, F_SETFL, flags) == -1) {
ret = FAILURE;
}
#endif
return ret;
}
PHPAPI void _php_emit_fd_setsize_warning(int max_fd)
{
#ifdef PHP_WIN32
php_error_docref(NULL, E_WARNING,
"PHP needs to be recompiled with a larger value of FD_SETSIZE.\n"
"If this binary is from an official www.php.net package, file a bug report\n"
"at http://bugs.php.net, including the following information:\n"
"FD_SETSIZE=%d, but you are using %d.\n"
" --enable-fd-setsize=%d is recommended, but you may want to set it\n"
"to match to maximum number of sockets each script will work with at\n"
"one time, in order to avoid seeing this error again at a later date.",
FD_SETSIZE, max_fd, (max_fd + 128) & ~127);
#else
php_error_docref(NULL, E_WARNING,
"You MUST recompile PHP with a larger value of FD_SETSIZE.\n"
"It is set to %d, but you have descriptors numbered at least as high as %d.\n"
" --enable-fd-setsize=%d is recommended, but you may want to set it\n"
"to equal the maximum number of open files supported by your system,\n"
"in order to avoid seeing this error again at a later date.",
FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023);
#endif
}
#if defined(PHP_USE_POLL_2_EMULATION)
PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout)
{
fd_set rset, wset, eset;
php_socket_t max_fd = SOCK_ERR;
unsigned int i;
int n;
struct timeval tv;
for (i = 0; i < nfds; i++) {
if (ufds[i].fd > max_fd)
max_fd = ufds[i].fd;
}
PHP_SAFE_MAX_FD(max_fd, nfds + 1);
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
for (i = 0; i < nfds; i++) {
if (ufds[i].events & PHP_POLLREADABLE) {
PHP_SAFE_FD_SET(ufds[i].fd, &rset);
}
if (ufds[i].events & POLLOUT) {
PHP_SAFE_FD_SET(ufds[i].fd, &wset);
}
if (ufds[i].events & POLLPRI) {
PHP_SAFE_FD_SET(ufds[i].fd, &eset);
}
}
if (timeout >= 0) {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
}
#ifdef PHP_WIN32
WSASetLastError(0);
#else
errno = 0;
#endif
n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL);
if (n >= 0) {
for (i = 0; i < nfds; i++) {
ufds[i].revents = 0;
if (PHP_SAFE_FD_ISSET(ufds[i].fd, &rset)) {
ufds[i].revents |= POLLIN;
}
if (PHP_SAFE_FD_ISSET(ufds[i].fd, &wset)) {
ufds[i].revents |= POLLOUT;
}
if (PHP_SAFE_FD_ISSET(ufds[i].fd, &eset)) {
ufds[i].revents |= POLLPRI;
}
}
}
return n;
}
#endif
#if defined(HAVE_GETHOSTBYNAME_R)
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_6
struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
{
struct hostent *hp;
int herr,res;
if (*hstbuflen == 0) {
*hstbuflen = 1024;
*tmphstbuf = (char *)malloc (*hstbuflen);
}
while (( res =
gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr))
&& (errno == ERANGE)) {
*hstbuflen *= 2;
*tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen);
}
if (res != SUCCESS) {
return NULL;
}
return hp;
}
#endif
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_5
struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
{
struct hostent *hp;
int herr;
if (*hstbuflen == 0) {
*hstbuflen = 1024;
*tmphstbuf = (char *)malloc (*hstbuflen);
}
while ((NULL == ( hp =
gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&herr)))
&& (errno == ERANGE)) {
*hstbuflen *= 2;
*tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen);
}
return hp;
}
#endif
#ifdef HAVE_FUNC_GETHOSTBYNAME_R_3
struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen)
{
if (*hstbuflen == 0) {
*hstbuflen = sizeof(struct hostent_data);
*tmphstbuf = (char *)malloc (*hstbuflen);
} else {
if (*hstbuflen < sizeof(struct hostent_data)) {
*hstbuflen = sizeof(struct hostent_data);
*tmphstbuf = (char *)realloc(*tmphstbuf, *hstbuflen);
}
}
memset((void *)(*tmphstbuf),0,*hstbuflen);
if (SUCCESS != gethostbyname_r(host,hostbuf,(struct hostent_data *)*tmphstbuf)) {
return NULL;
}
return hostbuf;
}
#endif
#endif
PHPAPI struct hostent* php_network_gethostbyname(char *name) {
#if !defined(HAVE_GETHOSTBYNAME_R)
return gethostbyname(name);
#else
if (FG(tmp_host_buf)) {
free(FG(tmp_host_buf));
}
FG(tmp_host_buf) = NULL;
FG(tmp_host_buf_len) = 0;
memset(&FG(tmp_host_info), 0, sizeof(struct hostent));
return gethostname_re(name, &FG(tmp_host_info), &FG(tmp_host_buf), &FG(tmp_host_buf_len));
#endif
}