php-src/main/network.c
2001-02-26 06:11:02 +00:00

249 lines
6.2 KiB
C

/*
+----------------------------------------------------------------------+
| PHP version 4.0 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2001 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: Stig Venaas <venaas@uninett.no> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#include "php.h"
#ifdef PHP_WIN32
#include <windows.h>
#include <winsock.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 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"
#ifdef PHP_WIN32
#undef AF_UNIX
#endif
#if defined(AF_UNIX)
#include <sys/un.h>
#endif
#ifdef PHP_WIN32
# define SOCK_ERR INVALID_SOCKET
# define SOCK_CONN_ERR SOCKET_ERROR
#else
# define SOCK_ERR -1
# define SOCK_CONN_ERR -1
#endif
#ifdef 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 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
static void php_network_freeaddresses(struct sockaddr **sal)
{
struct sockaddr **sap;
if (sal == NULL)
return;
for (sap = sal; *sap != NULL; sap++)
efree(*sap);
efree(sal);
}
static int php_network_getaddresses(const char *host, struct sockaddr ***sal)
{
struct sockaddr **sap;
if (host == NULL) {
return -1;
}
{
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *sai;
int n;
memset(&hints, '\0', sizeof(hints));
hints.ai_family = AF_UNSPEC;
if ((n = getaddrinfo(host, NULL, &hints, &res))) {
php_error(E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
return -1;
}
sai = res;
for (n=2; (sai = sai->ai_next) != NULL; n++);
*sal = emalloc(n * sizeof(*sal));
sai = res;
sap = *sal;
do {
switch (sai->ai_family) {
# ifdef HAVE_IPV6
case AF_INET6:
*sap = emalloc(sizeof(struct sockaddr_in6));
*(struct sockaddr_in6 *)*sap =
*((struct sockaddr_in6 *)sai->ai_addr);
sap++;
break;
# endif
case AF_INET:
*sap = emalloc(sizeof(struct sockaddr_in));
*(struct sockaddr_in *)*sap =
*((struct sockaddr_in *)sai->ai_addr);
sap++;
break;
}
} while ((sai = sai->ai_next) != NULL);
freeaddrinfo(res);
#else
struct hostent *host_info;
struct in_addr in;
if (!inet_aton(host, &in)) {
/* XXX NOT THREAD SAFE */
host_info = gethostbyname(host);
if (host_info == NULL) {
php_error(E_WARNING, "php_network_getaddresses: gethostbyname failed");
return -1;
}
in = *((struct in_addr *) host_info->h_addr);
}
*sal = emalloc(2 * sizeof(*sal));
sap = *sal;
*sap = emalloc(sizeof(struct sockaddr_in));
(*sap)->sa_family = AF_INET;
((struct sockaddr_in *)*sap)->sin_addr = in;
sap++;
#endif
}
*sap = NULL;
return 0;
}
/*
* Creates a socket of type socktype and connects to the given host and
* port, returns the created socket on success, else returns -1.
* timeout gives timeout in seconds, 0 means blocking mode.
*/
int php_hostconnect(char *host, unsigned short port, int socktype, int timeout)
{
int s;
struct sockaddr **sal, **psal;
if (php_network_getaddresses(host, &sal))
return -1;
psal = sal;
while (*sal != NULL) {
s = socket((*sal)->sa_family, socktype, 0);
if (s != SOCK_ERR) {
switch ((*sal)->sa_family) {
#if defined( HAVE_GETADDRINFO ) && defined( HAVE_IPV6 )
case AF_INET6: {
struct sockaddr_in6 *sa =
(struct sockaddr_in6 *)*sal;
sa->sin6_family = (*sal)->sa_family;
sa->sin6_port = htons(port);
if (connect(s, (struct sockaddr *) sa,
sizeof(*sa)) != SOCK_CONN_ERR)
goto ok;
} break;
#endif
case AF_INET: {
struct sockaddr_in *sa =
(struct sockaddr_in *)*sal;
sa->sin_family = (*sal)->sa_family;
sa->sin_port = htons(port);
if (connect(s, (struct sockaddr *) sa,
sizeof(*sa)) != SOCK_CONN_ERR)
goto ok;
} break;
}
close (s);
}
sal++;
}
php_network_freeaddresses(psal);
php_error(E_WARNING, "php_hostconnect: connect failed");
return -1;
ok:
php_network_freeaddresses(psal);
return s;
}
/*
* Local variables:
* tab-width: 8
* c-basic-offset: 8
* End:
*/