diff options
Diffstat (limited to 'eperd/tcputil.c')
-rw-r--r-- | eperd/tcputil.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/eperd/tcputil.c b/eperd/tcputil.c new file mode 100644 index 0000000..0e5d221 --- /dev/null +++ b/eperd/tcputil.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2013 RIPE NCC <atlas@ripe.net> + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * tcputil.c + */ + +#include "libbb.h" +#include "eperd.h" +#include <event2/bufferevent.h> +#include <event2/dns.h> +#include <event2/event.h> + +#include "tcputil.h" + +static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx); +static void create_bev(struct tu_env *env); +static void eventcb(struct bufferevent *bev, short events, void *ptr); + +void tu_connect_to_name(struct tu_env *env, char *host, char *port, + struct timeval *interval, + struct evutil_addrinfo *hints, + void (*timeout_callback)(int unused, const short event, void *s), + void (*reporterr)(struct tu_env *env, enum tu_err cause, + const char *err), + void (*reportcount)(struct tu_env *env, int count), + void (*beforeconnect)(struct tu_env *env, + struct sockaddr *addr, socklen_t addrlen), + void (*connected)(struct tu_env *env, struct bufferevent *bev), + void (*readcb)(struct bufferevent *bev, void *ptr), + void (*writecb)(struct bufferevent *bev, void *ptr)) +{ + struct evdns_getaddrinfo_request *evdns_req; + + env->interval= *interval; + env->reporterr= reporterr; + env->reportcount= reportcount; + env->beforeconnect= beforeconnect; + env->connected= connected; + env->readcb= readcb; + env->writecb= writecb; + env->dns_res= NULL; + env->bev= NULL; + + evtimer_assign(&env->timer, EventBase, + timeout_callback, env); + + env->dnsip= 1; + env->connecting= 0; + evdns_req= evdns_getaddrinfo(DnsBase, host, port, + hints, dns_cb, env); +} + +void tu_restart_connect(struct tu_env *env) +{ + struct bufferevent *bev; + + /* Delete old bev */ + if (env->bev) + { + bufferevent_free(env->bev); + env->bev= NULL; + } + + /* And create a new one */ + create_bev(env); + bev= env->bev; + + /* Connect failed, try next address */ + if (env->dns_curr) + /* Just to be on the safe side */ + { + env->dns_curr= env->dns_curr->ai_next; + } + while (env->dns_curr) + { + evtimer_add(&env->timer, &env->interval); + + env->beforeconnect(env, + env->dns_curr->ai_addr, env->dns_curr->ai_addrlen); + if (bufferevent_socket_connect(bev, + env->dns_curr->ai_addr, + env->dns_curr->ai_addrlen) == 0) + { + /* Connecting, wait for callback */ + return; + } + + /* Immediate error? */ + printf("connect error\n"); + env->dns_curr= env->dns_curr->ai_next; + } + + /* Something went wrong */ + bufferevent_free(env->bev); + env->bev= NULL; + if (env->dns_res) + { + evutil_freeaddrinfo(env->dns_res); + env->dns_res= NULL; + env->dns_curr= NULL; + } + env->reporterr(env, TU_OUT_OF_ADDRS, ""); +} + +void tu_cleanup(struct tu_env *env) +{ + if (env->dns_res) + { + evutil_freeaddrinfo(env->dns_res); + env->dns_res= NULL; + env->dns_curr= NULL; + } + if (env->bev) + { + bufferevent_free(env->bev); + env->bev= NULL; + } + + event_del(&env->timer); +} + +static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx) +{ + int count; + struct tu_env *env; + struct bufferevent *bev; + struct evutil_addrinfo *cur; + + env= ctx; + + if (!env->dnsip) + { + crondlog(LVL7 + "dns_cb: in dns_cb but not doing dns at this time"); + if (res) + evutil_freeaddrinfo(res); + return; + } + + env->dnsip= 0; + + if (result != 0) + { + env->reporterr(env, TU_DNS_ERR, evutil_gai_strerror(result)); + return; + } + + env->dns_res= res; + env->dns_curr= res; + + count= 0; + for (cur= res; cur; cur= cur->ai_next) + count++; + + env->reportcount(env, count); + + create_bev(env); + + while (env->dns_curr) + { + evtimer_add(&env->timer, &env->interval); + + env->beforeconnect(env, + env->dns_curr->ai_addr, env->dns_curr->ai_addrlen); + bev= env->bev; + if (bufferevent_socket_connect(bev, + env->dns_curr->ai_addr, + env->dns_curr->ai_addrlen) == 0) + { + /* Connecting, wait for callback */ + return; + } + + /* Immediate error? */ + printf("dns_cb: connect error\n"); + + /* It is possible that the callback already freed dns_curr. */ + if (!env->dns_curr) + { + printf("dns_cb: callback ate dns_curr\n"); + if (env->dns_res) + crondlog(DIE9 "dns_cb: dns_res not null"); + return; + } + env->dns_curr= env->dns_curr->ai_next; + } + + /* Something went wrong */ + printf("dns_cb: Connect failed\n"); + bufferevent_free(env->bev); + env->bev= NULL; + evutil_freeaddrinfo(env->dns_res); + env->dns_res= NULL; + env->dns_curr= NULL; + env->reporterr(env, TU_OUT_OF_ADDRS, ""); +} + +static void create_bev(struct tu_env *env) +{ + struct bufferevent *bev; + + bev= bufferevent_socket_new(EventBase, -1, + BEV_OPT_CLOSE_ON_FREE); + if (!bev) + { + crondlog(DIE9 "bufferevent_socket_new failed"); + } + bufferevent_setcb(bev, env->readcb, env->writecb, eventcb, env); + bufferevent_enable(bev, EV_WRITE); + env->bev= bev; + env->connecting= 1; + +} + +static void eventcb(struct bufferevent *bev, short events, void *ptr) +{ + struct tu_env *env; + + env= ptr; + + if (events & BEV_EVENT_READING) + { + env->reporterr(env, TU_READ_ERR, ""); + events &= ~BEV_EVENT_READING; + return; + } + if (events & BEV_EVENT_ERROR) + { + if (env->connecting) + { + env->reporterr(env, TU_CONNECT_ERR, + strerror(errno)); + return; + } + events &= ~BEV_EVENT_ERROR; + } + if (events & BEV_EVENT_CONNECTED) + { + events &= ~BEV_EVENT_CONNECTED; + env->connecting= 0; + bufferevent_enable(bev, EV_READ); + + env->connected(env, bev); + env->writecb(bev, ptr); + } + if (events) + printf("events = 0x%x\n", events); +} + |