From b4be0683e64a0778a00acb28da136b9f65bdb6c3 Mon Sep 17 00:00:00 2001 From: Richard Nyberg Date: Thu, 25 Dec 2008 12:59:28 +0100 Subject: [PATCH] Add code to connect to host given by its hostname. --- btpd/Makefile.am | 2 +- btpd/btpd.h | 6 +++ btpd/nameconn.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 btpd/nameconn.c diff --git a/btpd/Makefile.am b/btpd/Makefile.am index 197ee3b..b4def23 100644 --- a/btpd/Makefile.am +++ b/btpd/Makefile.am @@ -6,7 +6,7 @@ btpd_SOURCES=\ download.c download_subr.c download.h\ http_tr_if.c\ main.c\ - net.c net.h net_types.h\ + nameconn.c net.c net.h net_types.h\ net_buf.c net_buf.h\ opts.c opts.h\ peer.c peer.h\ diff --git a/btpd/btpd.h b/btpd/btpd.h index f03df65..790518e 100644 --- a/btpd/btpd.h +++ b/btpd/btpd.h @@ -90,4 +90,10 @@ aictx_t btpd_addrinfo(const char *node, short port, struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *), void *arg); void btpd_addrinfo_cancel(aictx_t ctx); + +typedef struct nameconn *nameconn_t; +nameconn_t btpd_name_connect(const char *name, short port, + void (*cb)(void *, int, int), void *arg); +void btpd_name_connect_cancel(nameconn_t nc); + #endif diff --git a/btpd/nameconn.c b/btpd/nameconn.c new file mode 100644 index 0000000..ba2f952 --- /dev/null +++ b/btpd/nameconn.c @@ -0,0 +1,116 @@ +#include "btpd.h" + +struct nameconn { + struct event write_ev; + void (*cb)(void *, int, int); + void *arg; + aictx_t ai_handle; + struct addrinfo *ai_res, *ai_cur; + int sd; + enum { NC_AI, NC_CONN } state; +}; + +static void nc_connect(struct nameconn *nc); + +static void +nc_free(struct nameconn *nc) +{ + if (nc->ai_res != NULL) + freeaddrinfo(nc->ai_res); + free(nc); +} + +static void +nc_done(struct nameconn *nc, int error) +{ + nc->cb(nc->arg, error, nc->sd); + nc_free(nc); +} + +static void +nc_write_cb(int sd, short type, void *arg) +{ + struct nameconn *nc = arg; + int error; + socklen_t errsiz = sizeof(int); + if (getsockopt(nc->sd, SOL_SOCKET, SO_ERROR, &error, &errsiz) < 0) + btpd_err("getsockopt error (%s).\n", strerror(errno)); + if (error == 0) + nc_done(nc, 0); + else { + close(nc->sd); + nc->ai_cur = nc->ai_cur->ai_next; + nc_connect(nc); + } +} + +static void +nc_connect(struct nameconn *nc) +{ + struct addrinfo *ai; + int err; + +again: + if ((ai = nc->ai_cur) == NULL) { + nc_done(nc, ENOTCONN); + return; + } + + if ((nc->sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) + btpd_err("Failed to create socket (%s).\n", strerror(errno)); + + set_nonblocking(nc->sd); + + err = connect(nc->sd, ai->ai_addr, ai->ai_addrlen); + if (err == 0) + nc_done(nc, 0); + else if (err < 0 && errno == EINPROGRESS) { + event_set(&nc->write_ev, nc->sd, EV_WRITE, nc_write_cb, nc); + btpd_ev_add(&nc->write_ev, NULL); + } else { + close(nc->sd); + nc->ai_cur = ai->ai_next; + goto again; + } +} + +static void +nc_ai_cb(void *arg, int error, struct addrinfo *ai) +{ + struct nameconn *nc = arg; + if (error == 0) { + nc->ai_cur = nc->ai_res = ai; + nc->state = NC_CONN; + nc_connect(nc); + } else + nc_done(nc, error); +} + +nameconn_t +btpd_name_connect(const char *name, short port, void (*cb)(void *, int, int), + void *arg) +{ + struct addrinfo hints; + struct nameconn *nc = btpd_calloc(1, sizeof(*nc)); + nc->cb = cb; + nc->arg = arg; + nc->sd = -1; + bzero(&hints, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + nc->ai_handle = btpd_addrinfo(name, port, &hints, nc_ai_cb, nc); + return nc; +} + +void +btpd_name_connect_cancel(struct nameconn *nc) +{ + if (nc->state == NC_AI) + btpd_addrinfo_cancel(nc->ai_handle); + else { + btpd_ev_del(&nc->write_ev); + close(nc->sd); + } + nc_free(nc); +}