non-blocking gethostbyname

SVN revision: 16203
This commit is contained in:
sebastid 2005-08-16 09:25:02 +00:00 committed by sebastid
parent f69ffbf261
commit 8db857be0d
5 changed files with 522 additions and 33 deletions

View File

@ -18,6 +18,8 @@
# endif
#endif
#include <netdb.h>
/**
* @file Ecore_Con.h
* @brief Sockets functions.
@ -161,6 +163,11 @@ extern "C" {
EAPI int ecore_con_url_url_set(Ecore_Con_Url *url_con, const char *url);
EAPI int ecore_con_url_send(Ecore_Con_Url *url_con, void *data, size_t length, char *content_type);
EAPI int ecore_con_dns_gethostbyname(const char *name,
void (*done_cb)(struct hostent *hostent, void *data),
void *data);
#ifdef __cplusplus
}
#endif

View File

@ -18,6 +18,7 @@ Ecore_Con.h
libecore_con_la_SOURCES = \
ecore_con.c \
ecore_con_dns.c \
ecore_con_url.c \
ecore_con_private.h
@ -32,5 +33,6 @@ endif
EXTRA_DIST = \
ecore_con.c \
ecore_con_dns.c \
ecore_con_url.c \
ecore_con_private.h

View File

@ -29,6 +29,7 @@
#include <time.h>
#endif
static void _ecore_con_cb_gethostbyname(struct hostent *he, void *data);
static void _ecore_con_server_free(Ecore_Con_Server *svr);
static void _ecore_con_client_free(Ecore_Con_Client *cl);
static int _ecore_con_svr_handler(void *data, Ecore_Fd_Handler *fd_handler);
@ -80,7 +81,10 @@ ecore_con_init(void)
#if USE_OPENSSL
SSL_library_init();
SSL_load_error_strings();
#endif
#endif
/* TODO Remember return value, if it fails, use gethostbyname() */
ecore_con_dns_init();
}
if (!servers)
servers = ecore_list_new();
@ -104,6 +108,8 @@ ecore_con_shutdown(void)
_ecore_con_server_free(ecore_list_remove_first(servers));
ecore_list_destroy(servers);
servers = NULL;
ecore_con_dns_shutdown();
}
return 0;
}
@ -371,7 +377,6 @@ ecore_con_server_connect(Ecore_Con_Type compl_type,
Ecore_Con_Server *svr;
Ecore_Con_Type type;
struct sockaddr_un socket_unix;
struct sockaddr_in socket_addr;
int curstate = 0;
char buf[4096];
@ -437,36 +442,7 @@ ecore_con_server_connect(Ecore_Con_Type compl_type,
}
else if (type == ECORE_CON_REMOTE_SYSTEM)
{
struct hostent *he;
/* FIXME: gethostbyname is blocking... */
if (!(he = gethostbyname(name))) goto error;
svr->fd = socket(AF_INET, SOCK_STREAM, 0);
if (svr->fd < 0) goto error;
if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate)) < 0) goto error;
socket_addr.sin_family = AF_INET;
socket_addr.sin_port = htons(port);
memcpy((struct in_addr *)&socket_addr.sin_addr,
he->h_addr, sizeof(struct in_addr));
if (connect(svr->fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in)) < 0)
{
if (errno != EINPROGRESS)
goto error;
svr->connecting = 1;
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ | ECORE_FD_WRITE,
_ecore_con_cl_handler, svr,
NULL, NULL);
}
else
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ,
_ecore_con_cl_handler, svr,
NULL, NULL);
if (!svr->fd_handler) goto error;
ecore_con_dns_gethostbyname(name, _ecore_con_cb_gethostbyname, svr);
}
#if USE_OPENSSL
@ -922,10 +898,52 @@ kill_server(Ecore_Con_Server *svr)
svr->fd_handler = NULL;
}
static void
_ecore_con_cb_gethostbyname(struct hostent *he, void *data)
{
Ecore_Con_Server *svr;
struct sockaddr_in socket_addr;
int curstate = 0;
svr = data;
svr->fd = socket(AF_INET, SOCK_STREAM, 0);
if (svr->fd < 0) goto error;
if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate)) < 0) goto error;
socket_addr.sin_family = AF_INET;
socket_addr.sin_port = htons(svr->port);
memcpy((struct in_addr *)&socket_addr.sin_addr,
he->h_addr, sizeof(struct in_addr));
if (connect(svr->fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in)) < 0)
{
if (errno != EINPROGRESS)
goto error;
svr->connecting = 1;
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ | ECORE_FD_WRITE,
_ecore_con_cl_handler, svr,
NULL, NULL);
}
else
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ,
_ecore_con_cl_handler, svr,
NULL, NULL);
if (!svr->fd_handler) goto error;
return;
error:
kill_server(svr);
}
static int
svr_try_connect_plain(Ecore_Con_Server *svr)
{
int so_err = 0, size = sizeof(int);
int so_err = 0;
unsigned int size = sizeof(int);
if (getsockopt(svr->fd, SOL_SOCKET, SO_ERROR, &so_err, &size) < 0)
so_err = -1;

View File

@ -0,0 +1,458 @@
/*
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
*/
/*
* Simple dns lookup
*
* http://www.faqs.org/rfcs/rfc1035.html
* man resolv.conf
*
* And a sneakpeek at ares, ftp://athena-dist.mit.edu/pub/ATHENA/ares/
*/
/*
* TODO
* * Check env LOCALDOMAIN to override search
* * Check env RES_OPTIONS to override options
* * Lookup names using domain or search
*
* * Make search and domain mutually exclusive
*
* * Read /etc/host.conf
* * Check /etc/hosts
*
* * Caching
* * Remember all querys and delete on shutdown
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <Ecore.h>
#include "ecore_private.h"
#define SERVERS 3
typedef struct _Ecore_Con_Dns_Query Ecore_Con_Dns_Query;
struct _Ecore_Con_Dns_Query {
Ecore_Oldlist list;
/* Can ask three servers */
unsigned int id[SERVERS];
int socket[SERVERS];
Ecore_Fd_Handler *fd_handlers[SERVERS];
int length;
Ecore_Timer *timeout;
char *hostname;
struct {
void (*cb)(struct hostent *hostent, void *data);
void *data;
} done;
};
static int _ecore_con_dns_timeout(void *data);
static int _ecore_con_cb_fd_handler(void *data, Ecore_Fd_Handler *fd_handler);
static void _ecore_con_dns_query_free(Ecore_Con_Dns_Query *query);
static int _init = 0;
static struct in_addr _servers[SERVERS];
static int _server_count;
static char *_search[6];
static int _search_count = 0;
static char *_domain = NULL;
static uint16_t _id = 0;
#define SET_16BIT(p, v) \
(((p)[0]) = ((v) >> 8) & 0xff), \
(((p)[1]) = v & 0xff)
#define GET_16BIT(p) (((p)[0]) << 8 | ((p)[1]))
int
ecore_con_dns_init(void)
{
FILE *file;
char buf[1024];
char *p, *p2;
int ret;
_init++;
if (_init > 1) return 1;
memset(_servers, 0, sizeof(_servers));
_server_count = 0;
file = fopen("/etc/resolv.conf", "rb");
if (!file) return 0;
while (fgets(buf, sizeof(buf), file))
{
if (strlen(buf) >= 1023)
printf("WARNING: Very long line in resolv.conf\n");
/* remove whitespace */
p = strchr(buf, ' ');
while (isspace(*p))
p++;
if (!strncmp(buf, "nameserver", 10))
{
if (_server_count >= SERVERS) continue;
_servers[_server_count].s_addr = inet_addr(p);
_server_count++;
}
else if (!strncmp(buf, "domain", 6))
{
_domain = strdup(p);
}
else if (!strncmp(buf, "search", 6))
{
while ((p) && (_search_count < 6))
{
/* Remove whitespace */
while (isspace(*p))
p++;
/* Find next element */
p2 = strchr(p, ' ');
if (!p2)
p2 = strchr(p, '\t');
if (p2)
*p2 = 0;
/* Get this element */
_search[_search_count] = strdup(p);
_search_count++;
if (p2) p = p2 + 1;
else p = NULL;
}
}
else if (!strncmp(buf, "sortlist", 8))
{
/* TODO */
}
else if (!strncmp(buf, "options", 8))
{
/* TODO */
}
else
printf("WARNING: Weird line in resolv.conf: %s\n", buf);
}
fclose(file);
if (!_server_count)
{
/* We should try localhost */
_servers[_server_count].s_addr = inet_addr("127.0.0.1");
_server_count++;
}
if ((!_search_count) && (!_domain))
{
/* Get domain from hostname */
ret = gethostname(buf, sizeof(buf));
if ((ret > 0) && (ret < 1024))
{
p = strchr(buf, '.');
if (p)
{
p++;
_domain = strdup(p);
}
}
}
return 1;
}
void
ecore_con_dns_shutdown(void)
{
int i;
_init--;
if (_init > 0) return;
if (_domain)
{
free(_domain);
_domain = NULL;
}
for (i = 0; i < _search_count; i++)
free(_search[i]);
_search_count = 0;
}
int
ecore_con_dns_gethostbyname(const char *name,
void (*done_cb)(struct hostent *hostent, void *data),
void *data)
{
Ecore_Con_Dns_Query *query;
int i;
if (!_server_count) return 0;
if ((!name) || (!*name)) return 0;
query = calloc(1, sizeof(Ecore_Con_Dns_Query));
if (!query) return 0;
query->done.cb = done_cb;
query->done.data = data;
query->timeout = ecore_timer_add(20.0, _ecore_con_dns_timeout, query);
query->hostname = strdup(name);
/* We're crazy, just ask all servers! */
for (i = 0; i < _server_count; i++)
{
struct sockaddr_in sin;
unsigned char buf[256];
unsigned char *p, *q, *pl;
int len;
query->socket[i] = socket(AF_INET, SOCK_DGRAM, 0);
if (query->socket[i] == -1)
{
printf("ERROR: Couldn't create socket\n");
continue;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr = _servers[i];
sin.sin_port = htons(NAMESERVER_PORT);
if (connect(query->socket[i], (struct sockaddr *) &sin, sizeof(sin)) == -1)
{
/* TODO: EINPROGRESS isn't a fatal error */
printf("ERROR: Couldn't connect to nameserver\n");
close(query->socket[i]);
query->socket[i] = 0;
continue;
}
query->id[i] = ++_id;
/* Create buf */
memset(buf, 0, sizeof(buf));
p = buf;
/* qid */
SET_16BIT(p, query->id[i]);
p += 2;
/* opcode */
*p |= (QUERY & 0xf) << 3;
/* TODO: rd, do we always want recursive? */
*p |= 1 & 0x1;
p += 2;
/* qdcount, only asking for one name */
SET_16BIT(p, 1);
query->length += HFIXEDSZ;
p = &buf[HFIXEDSZ];
/* remember where the length shall be placed */
pl = p;
p++;
query->length++;
/* name */
q = name;
len = 0;
while ((*q) && (query->length < 1024))
{
if (*q == '.')
{
if (len)
{
*pl = len;
pl = p;
p++;
len = 0;
query->length++;
}
q++;
}
else if ((*q == '\\') && (*(q + 1) == 0))
{
q++;
*p++ = *q++;
len++;
query->length++;
}
else
{
*p++ = *q++;
len++;
query->length++;
}
}
/* Null at the end of the query */
if (len)
{
*pl = len;
*p = 0;
p++;
query->length++;
}
/* type */
SET_16BIT(p, T_A);
p += 2;
/* class */
SET_16BIT(p, C_IN);
p += 2;
query->length += QFIXEDSZ;
if (send(query->socket[i], buf, query->length, 0) == -1)
{
printf("ERROR: Send failed\n");
close(query->socket[i]);
query->socket[i] = 0;
continue;
}
query->fd_handlers[i] = ecore_main_fd_handler_add(query->socket[i],
ECORE_FD_READ|ECORE_FD_ERROR,
_ecore_con_cb_fd_handler, query,
NULL, NULL);
}
return 1;
}
static int
_ecore_con_dns_timeout(void *data)
{
Ecore_Con_Dns_Query *query;
query = data;
if (query->done.cb)
query->done.cb(NULL, query->done.data);
_ecore_con_dns_query_free(query);
return 0;
}
static int
_ecore_con_cb_fd_handler(void *data, Ecore_Fd_Handler *fd_handler)
{
Ecore_Con_Dns_Query *query;
int i, n, fd, found = 0;
unsigned int id;
unsigned char buf[256];
query = data;
fd = ecore_main_fd_handler_fd_get(fd_handler);
n = recv(fd, buf, sizeof(buf), 0);
if (n == -1) goto error;
/* Check if this message is for us */
id = GET_16BIT(buf);
for (i = 0; i < _server_count; i++)
{
if (query->id[i] == id)
{
found = 1;
id = i;
break;
}
}
if ((found) && (n > query->length))
{
/* This should be it! */
unsigned char *p;
int size;
p = buf;
/* Skip the query */
p += query->length;
/* Skip the header */
p += RRFIXEDSZ;
size = GET_16BIT(p);
if (size == 4)
{
struct hostent he;
p += 2;
/* Get the IP address */
he.h_addr_list = malloc(2 * sizeof(char *));
if (he.h_addr_list)
{
/* Fill in the hostent and return successfully. */
/* TODO: Maybe get the hostname from the reply */
he.h_name = strdup(query->hostname);
/* he.h_aliases = aliases; */
he.h_addrtype = AF_INET;
he.h_length = sizeof(struct in_addr);
he.h_addr_list[0] = malloc(4 * sizeof(char));
memcpy(he.h_addr_list[0], p, he.h_length);
he.h_addr_list[1] = NULL;
if (query->done.cb)
query->done.cb(&he, query->done.data);
free(he.h_addr_list);
_ecore_con_dns_query_free(query);
return 0;
}
}
}
error:
found = 0;
for (i = 0; i < _server_count; i++)
{
if (query->fd_handlers[i] == fd_handler)
{
/* This server didn't do it */
if (query->socket[i]) close(query->socket[i]);
query->socket[i] = 0;
query->fd_handlers[i] = NULL;
break;
}
else if (query->socket[i])
{
/* We're still looking */
found = 1;
}
}
if (!found)
{
/* Shutdown */
if (query->done.cb)
query->done.cb(NULL, query->done.data);
_ecore_con_dns_query_free(query);
}
return 0;
}
static void
_ecore_con_dns_query_free(Ecore_Con_Dns_Query *query)
{
int i;
for (i = 0; i < _server_count; i++)
{
if (query->socket[i]) close(query->socket[i]);
query->socket[i] = 0;
if (query->fd_handlers[i]) ecore_main_fd_handler_del(query->fd_handlers[i]);
query->fd_handlers[i] = NULL;
}
if (query->timeout) ecore_timer_del(query->timeout);
query->timeout = NULL;
free(query->hostname);
free(query);
}

View File

@ -83,4 +83,8 @@ struct _Ecore_Con_Url
char active : 1;
};
#endif
/* from ecore_con_dns.c */
int ecore_con_dns_init(void);
void ecore_con_dns_shutdown(void);
#endif