commit cc4953ef55207880f2363213450274581b44b0db from: Omar Polo date: Tue Mar 16 18:14:36 2021 UTC first draft of asynchronous dns resolution Use asr on OpenBSD to do DNS query lookups asynchronously. At the moment it seems a bit hard to provide the whole asr + asr_event stuff in the compat layer, so hide those bits behind an #ifdef; not the prettiest solution, but a working one. Future works may either: - provide a stub implementation for the asr stuff - bundle the libasr from OpenSMTD-portable commit - 58c75fabb676904383f4b1bc176518cbc563d1bf commit + cc4953ef55207880f2363213450274581b44b0db blob - 5c8d2187a97b6002a08ac45a907bc7f907fff67b blob + cb4bb1355f7f51704a0b2af48c7bad9a498b709b --- configure.ac +++ configure.ac @@ -40,6 +40,8 @@ AC_CHECK_LIB(util, imsg_init, [], [ AC_LIBOBJ(imsg-buffer) ]) +AC_CHECK_FUNCS([asr_run]) + # check compiler flags AC_DEFUN([CC_ADD_CHECK_FLAGS], [ AC_MSG_CHECKING([if $CC supports $1 flag]) blob - 48a1aa827cfed24d77aab48a2f07a126eb4b9543 blob + fe9fdd24ee551c9f088d78a9f8353545bab5ba39 --- gemini.c +++ gemini.c @@ -40,6 +40,10 @@ #include #include +#if HAVE_ASR_RUN +# include +#endif + static struct event imsgev; static struct tls_config *tlsconf; static struct imsgbuf *ibuf; @@ -47,14 +51,22 @@ static struct imsgbuf *ibuf; struct req; static void die(void) __attribute__((__noreturn__)); + +#if HAVE_ASR_RUN +static void try_to_connect(int, short, void*); +static void query_done(struct asr_result*, void*); +static void async_conn_towards(struct req*); +#else static char *xasprintf(const char*, ...); -static int conn_towards(struct url*, char**); +static int blocking_conn_towards(struct url*, char**); +#endif static void close_with_err(struct req*, const char*); static void close_with_errf(struct req*, const char*, ...) __attribute__((format(printf, 2, 3))); static struct req *req_by_id(uint32_t); static struct req *req_by_id_try(uint32_t); +static void setup_tls(struct req*); static void do_handshake(int, short, void*); static void write_request(int, short, void*); static void read_reply(int, short, void*); @@ -91,6 +103,12 @@ struct req { struct tls *ctx; char buf[1024]; size_t off; + +#if HAVE_ASR_RUN + struct addrinfo hints, *servinfo, *p; + struct event_asr *asrev; +#endif + TAILQ_ENTRY(req) reqs; }; @@ -119,8 +137,84 @@ static void __attribute__((__noreturn__)) die(void) { abort(); /* TODO */ +} + +#if HAVE_ASR_RUN +static void +try_to_connect(int fd, short ev, void *d) +{ + struct req *req = d; + int error = 0; + socklen_t len = sizeof(error); + + if (req->p == NULL) + goto err; + + if (req->fd != -1) { + if (getsockopt(req->fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) + goto err; + if (error != 0) { + errno = error; + goto err; + } + goto done; + } + + req->fd = socket(req->p->ai_family, req->p->ai_socktype, req->p->ai_protocol); + if (req->fd == -1) + req->p = req->p->ai_next; + else { + mark_nonblock(req->fd); + if (connect(req->fd, req->p->ai_addr, req->p->ai_addrlen) == 0) + goto done; + } + try_to_connect(fd, ev, req); + return; + +err: + freeaddrinfo(req->servinfo); + close_with_errf(req, "failed to connect to %s", + req->url.host); + return; + +done: + freeaddrinfo(req->servinfo); + setup_tls(req); +} + +static void +query_done(struct asr_result *res, void *d) +{ + struct req *req = d; + + req->asrev = NULL; + if (res->ar_gai_errno != 0) { + close_with_errf(req, "failed to resolve %s: %s", + req->url.host, gai_strerror(res->ar_gai_errno)); + return; + } + + req->fd = -1; + req->servinfo = res->ar_addrinfo; + req->p = res->ar_addrinfo; + try_to_connect(0, 0, req); } +static void +async_conn_towards(struct req *req) +{ + struct asr_query *q; + const char *proto = "1965"; + + if (*req->url.port != '\0') + proto = req->url.port; + + req->hints.ai_family = AF_UNSPEC; + req->hints.ai_socktype = SOCK_STREAM; + q = getaddrinfo_async(req->url.host, proto, &req->hints, NULL); + req->asrev = event_asr_run(q, query_done, req); +} +#else static char * xasprintf(const char *fmt, ...) { @@ -136,7 +230,7 @@ xasprintf(const char *fmt, ...) } static int -conn_towards(struct url *url, char **err) +blocking_conn_towards(struct url *url, char **err) { struct addrinfo hints, *servinfo, *p; int status, sock; @@ -174,6 +268,7 @@ conn_towards(struct url *url, char **err) freeaddrinfo(servinfo); return sock; } +#endif static struct req * req_by_id(uint32_t id) @@ -203,6 +298,11 @@ close_conn(int fd, short ev, void *d) { struct req *req = d; +#if HAVE_ASR_RUN + if (req->asrev != NULL) + event_asr_abort(req->asrev); +#endif + if (req->ctx != NULL) { switch (tls_close(req->ctx)) { case TLS_WANT_POLLIN: @@ -246,6 +346,24 @@ close_with_errf(struct req *req, const char *fmt, ...) } static void +setup_tls(struct req *req) +{ + if ((req->ctx = tls_client()) == NULL) { + close_with_errf(req, "tls_client: %s", strerror(errno)); + return; + } + if (tls_configure(req->ctx, tlsconf) == -1) { + close_with_errf(req, "tls_configure: %s", tls_error(req->ctx)); + return; + } + if (tls_connect_socket(req->ctx, req->fd, req->url.host) == -1) { + close_with_errf(req, "tls_connect_socket: %s", tls_error(req->ctx)); + return; + } + yield_w(req, do_handshake, &timeout_for_handshake); +} + +static void do_handshake(int fd, short ev, void *d) { struct req *req = d; @@ -426,7 +544,7 @@ handle_get(struct imsg *imsg, size_t datalen) { struct req *req; const char *e; - char *data, *err = NULL; + char *data; data = imsg->data; @@ -444,25 +562,19 @@ handle_get(struct imsg *imsg, size_t datalen) return; } - if ((req->fd = conn_towards(&req->url, &err)) == -1) - goto err; - if ((req->ctx = tls_client()) == NULL) - goto err; - if (tls_configure(req->ctx, tlsconf) == -1) { - err = xasprintf("tls_configure: %s", tls_error(req->ctx)); - goto err; - } - if (tls_connect_socket(req->ctx, req->fd, req->url.host) == -1) { - err = xasprintf("tls_connect_socket: %s", tls_error(req->ctx)); - goto err; - } +#if HAVE_ASR_RUN + async_conn_towards(req); +#else + { + char *e = NULL; - yield_w(req, do_handshake, &timeout_for_handshake); - return; - -err: - close_with_err(req, err); - free(err); + if ((req->fd = blocking_conn_towards(&req->url, &err)) == -1) { + close_with_err(req, err); + free(err); + } + setup_tls(req); + } +#endif } static void