diff options
Diffstat (limited to 'common')
| -rw-r--r-- | common/async-resolver.c | 322 | ||||
| -rw-r--r-- | common/async-resolver.h | 16 | ||||
| -rw-r--r-- | common/server-mainloop.h | 2 | ||||
| -rw-r--r-- | common/sock-any.c | 35 | ||||
| -rw-r--r-- | common/sock-any.h | 17 | 
5 files changed, 381 insertions, 11 deletions
| diff --git a/common/async-resolver.c b/common/async-resolver.c new file mode 100644 index 0000000..c734bff --- /dev/null +++ b/common/async-resolver.c @@ -0,0 +1,322 @@ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <netdb.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> + +#include "async-resolver.h" +#include "server-mainloop.h" + +/* ----------------------------------------------------------------------------- + * THREAD COMMUNICATION + */ + +#define TSIGNAL_UNITITIALIZED  { -1 -1 } + +static int +tsignal_init(int* sig) +{ +    if(pipe(sig) == -1) +        return -1; +    fcntl(sig[0], F_SETFL, fcntl(sig[0], F_GETFL, 0) | O_NONBLOCK); +    return 0; +} + +static int +tsignal_get_fd(int* sig) +{ +    return sig[0]; +} + +static void +tsignal_wake(int* sig) +{ +    write(sig[1], "1", 1); +} + +static void +tsignal_clear(int* sig) +{ +    char buf[16]; +    while(read(sig[0], buf, sizeof(buf)) > 0); +} + +static void +tsignal_wait(int* sig, struct timeval* tv) +{ +    fd_set watch; +    FD_ZERO(&watch); +    FD_SET(sig[0], &watch); +    select(sig[0], &watch, NULL, NULL, tv); +} + +static void +tsignal_uninit(int* sig) +{ +    if(sig[1] != -1) +        close(sig[1]); +    sig[1] = -1; +    if(sig[0] != -1) +        close(sig[0]); +    sig[0] = -1; +} + +/* ----------------------------------------------------------------------------- + * RESOLVER + */ + +typedef struct _resolve_request +{ +    char hostname[256]; +    char servname[256]; +    async_resolve_callback cb; +    void *arg; + +    int gaierr; +    int errn; +    struct addrinfo *ai; + +    struct _resolve_request *next; +} +resolve_request; + +/* The queues */ +static int res_quit = 0; +static resolve_request* res_requests = NULL; +static resolve_request* res_done = NULL; + +/* Thread communication */ +static pthread_t res_thread = 0; +static pthread_mutex_t res_mutex = PTHREAD_MUTEX_INITIALIZER; +static int res_request_signal[2] = TSIGNAL_UNITITIALIZED; +static int res_done_signal[2] = TSIGNAL_UNITITIALIZED; + +static void* +resolver_thread(void* arg) +{ +    resolve_request* req; +    resolve_request* r; +    struct timeval tv; + +    while(!res_quit) +    { +        pthread_mutex_lock(&res_mutex); + +            /* Dig out any requests */ +            req = res_requests; +            if(req) +            { +                res_requests = req->next; +                req->next = NULL; +            } + +        pthread_mutex_unlock(&res_mutex); + +        /* No requests, wait for a request */ +        if(!req) +        { +            tv.tv_sec = 0; +            tv.tv_usec = 500000; +            tsignal_wait(res_request_signal, &tv); +            tsignal_clear(res_request_signal); +            continue; +        } + +        /* The actual resolve */ +        req->gaierr = getaddrinfo(req->hostname, req->servname[0] ? req->servname : NULL, +                                 NULL, &req->ai); +        req->errn = errno; + +        /* A timeout */ +        if(!req->gaierr && !req->ai) +        { +            req->gaierr = EAI_SYSTEM; +            req->errn = ETIMEDOUT; +        } + +        /* Append the result to done */ +        pthread_mutex_lock(&res_mutex); + +            if(!res_done) +            { +                res_done = req; +            } +            else +            { +                r = res_done; +                while(r->next) +                    r = r->next; +                r->next = req; +            } + +        pthread_mutex_unlock(&res_mutex); + +        /* Tell the main thread to check outbound */ +        tsignal_wake(res_done_signal); +    } + +    return NULL; +} + +static void +resolver_done(int fd, int type, void* arg) +{ +    resolve_request* req; +    resolve_request* r; + +    tsignal_clear(res_done_signal); + +    pthread_mutex_lock(&res_mutex); + +        req = res_done; +        res_done = NULL; + +    pthread_mutex_unlock(&res_mutex); + +    while(req) +    { +        /* Send off the result */ +        errno = req->errn; +        (req->cb)(req->gaierr, req->ai, req->arg); + +        /* And free it all */ +        r = req->next; +        if(req->ai) +            freeaddrinfo(req->ai); +        free(req); + +        req = r; +    } +} + +int +async_resolver_init() +{ +    int r; + +    /* The signal pipes */ +    if(tsignal_init(res_request_signal) < 0) +        return -1; +    if(tsignal_init(res_done_signal) < 0) +        return -1; + +    if(server_watch(tsignal_get_fd(res_done_signal), SERVER_READ, resolver_done, NULL) == -1) +        return -1; + +    r = pthread_create(&res_thread, NULL, resolver_thread, NULL); +    if(r != 0) +    { +        res_thread = 0; +        return -1; +    } + +    return 0; +} + +void +async_resolver_queue(const char* hostname, const char* servname, +                     async_resolve_callback cb, void* arg) +{ +    resolve_request* req; +    resolve_request* r; +    char* t; + +    req = calloc(1, sizeof(resolve_request)); +    if(!req) +    { +        /* All errors go to callback */ +        (cb)(EAI_MEMORY, NULL, arg); +        return; +    } + +    req->cb = cb; +    req->arg = arg; + +    strncpy(req->hostname, hostname, sizeof(req->hostname)); +    req->hostname[sizeof(req->hostname) - 1] = 0; + +    /* A colon and we try to split */ +    t = strchr(req->hostname, ':'); +    if(t) +    { +        *t = 0; +        strncpy(req->servname, t + 1, sizeof(req->servname)); +    } + +    if(servname && !req->servname[0]) +        strncpy(req->servname, servname, sizeof(req->servname)); +    req->servname[sizeof(req->servname) - 1] = 0; + +    /* Append the result to requests */ +    pthread_mutex_lock(&res_mutex); + +        if(!res_requests) +        { +            res_requests = req; +        } +        else +        { +            r = res_requests; +            while(r->next) +                r = r->next; +            r->next = req; +        } + +    pthread_mutex_unlock(&res_mutex); + +    tsignal_wake(res_request_signal); +} + +void +async_resolver_uninit() +{ +    resolve_request* req; + +    /* No more responses from this point on */ +    if(tsignal_get_fd(res_done_signal) != -1) +        server_unwatch(tsignal_get_fd(res_done_signal)); + +    pthread_mutex_lock(&res_mutex); + +        while(res_requests) +        { +            req = res_requests->next; +            if(res_requests->ai) +                freeaddrinfo(res_requests->ai); +            free(res_requests); +            res_requests = req; +        } + +        while(res_done) +        { +            req = res_done->next; +            if(res_done->ai) +                freeaddrinfo(res_done->ai); +            free(res_done); +            res_done = req; +        } + +    pthread_mutex_unlock(&res_mutex); + +    /* Wake up the resolver thread */ +    res_quit = 1; +    tsignal_uninit(res_request_signal); + +    /* Wait for it to finish */ +    if(res_thread) +    { +        pthread_join(res_thread, NULL); +        res_thread = 0; +    } + +    /* And close up the signals in the other direction */ +    tsignal_uninit(res_done_signal); +} diff --git a/common/async-resolver.h b/common/async-resolver.h new file mode 100644 index 0000000..6b31ff4 --- /dev/null +++ b/common/async-resolver.h @@ -0,0 +1,16 @@ + +#ifndef __ASYNC_RESOLVER_H__ +#define __ASYNC_RESOLVER_H__ + +#include <netdb.h> + +typedef void (*async_resolve_callback)(int ecode, struct addrinfo* ai, void* arg); + +int  async_resolver_init(); +void async_resolver_uninit(); + +void async_resolver_queue(const char* hostname, const char* servname, +                          async_resolve_callback cb, void* arg); + + +#endif /* __ASYNC_RESOLVER_H__ */ diff --git a/common/server-mainloop.h b/common/server-mainloop.h index 098ef44..788f3ae 100644 --- a/common/server-mainloop.h +++ b/common/server-mainloop.h @@ -4,8 +4,6 @@  #include <stdint.h> -/* TODO: Prefix functions with svr */ -  #define SERVER_READ       0x01  #define SERVER_WRITE      0x02 diff --git a/common/sock-any.c b/common/sock-any.c index 1938d5d..bb207ea 100644 --- a/common/sock-any.c +++ b/common/sock-any.c @@ -246,6 +246,7 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts)    {      struct addrinfo* res;      int port = 0; +    int family = 0;      t = NULL;      l = strlen(addr); @@ -273,13 +274,33 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts)          break;      } -    /* Try and resolve the domain name */ -    if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res) -      break; +    if(!(opts & SANY_OPT_NORESOLV)) +    { +      /* Try and resolve the domain name */ +      if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res) +        break; -    memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr)); -    any->namelen = res->ai_addrlen; -    freeaddrinfo(res); +      memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr)); +      any->namelen = res->ai_addrlen; +      family = any->s.a.sa_family; +      freeaddrinfo(res); +    } +    else +    { +       family = SANY_AF_DNS; +#ifdef HAVE_INET6 +       if(opt & SANY_OPT_DEFINET6) +       { +         any->s.a.sa_family = AF_INET6; +         any->namelen = sizeof(any->s.in6); +       } +       else +#endif +       { +         any->s.a.sa_family = AF_INET; +         any->namelen = sizeof(any->s.in); +       } +    }      port = htons((unsigned short)(port <= 0 ? defport : port)); @@ -295,7 +316,7 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts)  #endif      }; -    return any->s.a.sa_family; +    return family;    }    while(0); diff --git a/common/sock-any.h b/common/sock-any.h index 31cb13b..7df54e1 100644 --- a/common/sock-any.h +++ b/common/sock-any.h @@ -64,6 +64,9 @@ struct sockaddr_any  #define SANY_LEN(any)   ((any).namelen)  #define SANY_TYPE(any)  ((any).s.a.sa_family) +/* -------------------------------------------------------------------------- */ + +/* Returns AF_XXX family type or -1 */  int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts);  /* The default port to fill in when no IP/IPv6 port specified */ @@ -76,15 +79,25 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts);  #define SANY_OPT_DEFLOCAL       0x00100000  /* When only port specified default to IPv6 */ -#ifdef HAVE_INET6  #define SANY_OPT_DEFINET6       0x00200000 -#endif +/* Don't resolve host name */ +#define SANY_OPT_NORESOLV       0x01000000 + +/* The family type returned when resolving is needed */ +#define SANY_AF_DNS             0x01000000 + +/* -------------------------------------------------------------------------- */ + +/* Returns -1 when failed */  int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts);  /* Don't print or compare the port */  #define SANY_OPT_NOPORT         0x01000000 +/* -------------------------------------------------------------------------- */ + +/* Returns 0 for equal */  int sock_any_cmp(const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts);  #endif /* __SOCK_ANY_H__ */ | 
