+static int do_connect(int fd, struct io_plan *plan)
+{
+ int err, ret;
+ socklen_t len = sizeof(err);
+
+ /* Has async connect finished? */
+ ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
+ if (ret < 0)
+ return -1;
+
+ if (err == 0) {
+ /* Restore blocking if it was initially. */
+ fcntl(fd, F_SETFL, plan->u1.s);
+ return 1;
+ } else if (err == EINPROGRESS)
+ return 0;
+
+ errno = err;
+ return -1;
+}
+
+struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr,
+ struct io_plan *(*next)(struct io_conn *, void *),
+ void *arg)
+{
+ struct io_plan *plan = io_get_plan(conn, IO_IN);
+ int fd = io_conn_fd(conn);
+
+ assert(next);
+
+ /* Save old flags, set nonblock if not already. */
+ plan->u1.s = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, plan->u1.s | O_NONBLOCK);
+
+ /* Immediate connect can happen. */
+ if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0)
+ return set_always(conn, plan, next, arg);
+
+ if (errno != EINPROGRESS)
+ return io_close(conn);
+
+ plan->next = next;
+ plan->next_arg = arg;
+ plan->io = do_connect;
+
+ return plan;
+}
+
+struct io_plan *io_wait_(struct io_conn *conn,
+ const void *wait,
+ struct io_plan *(*next)(struct io_conn *, void *),
+ void *arg)