From 8db857be0daa0168a7f47535e5dc0b088117e587 Mon Sep 17 00:00:00 2001 From: sebastid Date: Tue, 16 Aug 2005 09:25:02 +0000 Subject: [PATCH] non-blocking gethostbyname SVN revision: 16203 --- legacy/ecore/src/lib/ecore_con/Ecore_Con.h | 7 + legacy/ecore/src/lib/ecore_con/Makefile.am | 2 + legacy/ecore/src/lib/ecore_con/ecore_con.c | 84 ++-- .../ecore/src/lib/ecore_con/ecore_con_dns.c | 458 ++++++++++++++++++ .../src/lib/ecore_con/ecore_con_private.h | 4 + 5 files changed, 522 insertions(+), 33 deletions(-) create mode 100644 legacy/ecore/src/lib/ecore_con/ecore_con_dns.c diff --git a/legacy/ecore/src/lib/ecore_con/Ecore_Con.h b/legacy/ecore/src/lib/ecore_con/Ecore_Con.h index fd022b3f54..68df1b7817 100644 --- a/legacy/ecore/src/lib/ecore_con/Ecore_Con.h +++ b/legacy/ecore/src/lib/ecore_con/Ecore_Con.h @@ -18,6 +18,8 @@ # endif #endif +#include + /** * @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 diff --git a/legacy/ecore/src/lib/ecore_con/Makefile.am b/legacy/ecore/src/lib/ecore_con/Makefile.am index e40be6eb27..59ed8edbd5 100644 --- a/legacy/ecore/src/lib/ecore_con/Makefile.am +++ b/legacy/ecore/src/lib/ecore_con/Makefile.am @@ -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 diff --git a/legacy/ecore/src/lib/ecore_con/ecore_con.c b/legacy/ecore/src/lib/ecore_con/ecore_con.c index 43639258f1..1272943168 100644 --- a/legacy/ecore/src/lib/ecore_con/ecore_con.c +++ b/legacy/ecore/src/lib/ecore_con/ecore_con.c @@ -29,6 +29,7 @@ #include #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; diff --git a/legacy/ecore/src/lib/ecore_con/ecore_con_dns.c b/legacy/ecore/src/lib/ecore_con/ecore_con_dns.c new file mode 100644 index 0000000000..fbdf42bba2 --- /dev/null +++ b/legacy/ecore/src/lib/ecore_con/ecore_con_dns.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#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); +} diff --git a/legacy/ecore/src/lib/ecore_con/ecore_con_private.h b/legacy/ecore/src/lib/ecore_con/ecore_con_private.h index d94793ec85..6081a01eb2 100644 --- a/legacy/ecore/src/lib/ecore_con/ecore_con_private.h +++ b/legacy/ecore/src/lib/ecore_con/ecore_con_private.h @@ -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