From b4be0683e64a0778a00acb28da136b9f65bdb6c3 Mon Sep 17 00:00:00 2001
From: Richard Nyberg <rnyberg@murmeldjur.se>
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);
+}