/* * Callback for the async resolver on ns_connect_opt() call. * Main task of this function is to trigger NS_CONNECT event with * either failure (and dealloc the connection) * or success (and proceed with connect() */ static void resolve_cb(struct ns_dns_message *msg, void *data) { struct ns_connection *nc = (struct ns_connection *) data; int i; int failure = -1; if (msg != NULL) { /* * Take the first DNS A answer and run... */ for (i = 0; i < msg->num_answers; i++) { if (msg->answers[i].rtype == NS_DNS_A_RECORD) { static struct ns_add_sock_opts opts; /* * Async resolver guarantees that there is at least one answer. * TODO(lsm): handle IPv6 answers too */ ns_dns_parse_record_data(msg, &msg->answers[i], &nc->sa.sin.sin_addr, 4); /* Make ns_finish_connect() trigger NS_CONNECT on failure */ nc->flags |= NSF_CONNECTING; ns_finish_connect(nc, nc->flags & NSF_UDP ? SOCK_DGRAM : SOCK_STREAM, &nc->sa, opts); return; } } } /* * If we get there was no NS_DNS_A_RECORD in the answer */ ns_call(nc, NS_CONNECT, &failure); ns_destroy_conn(nc); }
static void ns_close_conn(struct ns_connection *conn) { DBG(("%p %lu", conn, conn->flags)); if (!(conn->flags & NSF_CONNECTING)) { ns_call(conn, NS_CLOSE, NULL); } ns_remove_conn(conn); ns_destroy_conn(conn); }
struct ns_connection *ns_connect_opt(struct ns_mgr *mgr, const char *address, ns_event_handler_t callback, struct ns_connect_opts opts) { struct ns_connection *nc = NULL; int proto, rc; struct ns_add_sock_opts add_sock_opts; char host[NS_MAX_HOST_LEN]; NS_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); if ((nc = ns_create_connection(mgr, callback, add_sock_opts)) == NULL) { return NULL; } else if ((rc = ns_parse_address(address, &nc->sa, &proto, host, sizeof(host))) < 0) { /* Address is malformed */ NS_SET_PTRPTR(opts.error_string, "cannot parse address"); ns_destroy_conn(nc); return NULL; } nc->flags |= opts.flags & _NS_ALLOWED_CONNECT_FLAGS_MASK; nc->flags |= (proto == SOCK_DGRAM) ? NSF_UDP : 0; nc->user_data = opts.user_data; if (rc == 0) { #ifndef NS_DISABLE_RESOLVER /* * DNS resolution is required for host. * ns_parse_address() fills port in nc->sa, which we pass to resolve_cb() */ if (ns_resolve_async(nc->mgr, host, NS_DNS_A_RECORD, resolve_cb, nc) != 0) { NS_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup"); ns_destroy_conn(nc); return NULL; } return nc; #else NS_SET_PTRPTR(opts.error_string, "Resolver is disabled"); ns_destroy_conn(nc); return NULL; #endif } else { /* Address is parsed and resolved to IP. proceed with connect() */ return ns_finish_connect(nc, proto, &nc->sa, add_sock_opts); } }
/* * Schedules an async connect for a resolved address and proto. * Called from two places: `ns_connect_opt()` and from async resolver. * When called from the async resolver, it must trigger `NS_CONNECT` event * with a failure flag to indicate connection failure. */ NS_INTERNAL struct ns_connection *ns_finish_connect(struct ns_connection *nc, int proto, union socket_address *sa, struct ns_add_sock_opts o) { sock_t sock = INVALID_SOCKET; int rc; DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { int failure = errno; NS_SET_PTRPTR(o.error_string, "cannot create socket"); if (nc->flags & NSF_CONNECTING) { ns_call(nc, NS_CONNECT, &failure); } ns_destroy_conn(nc); return NULL; } ns_set_non_blocking_mode(sock); rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa->sa, sizeof(sa->sin)); if (rc != 0 && ns_is_error(rc)) { NS_SET_PTRPTR(o.error_string, "cannot connect to socket"); if (nc->flags & NSF_CONNECTING) { ns_call(nc, NS_CONNECT, &rc); } ns_destroy_conn(nc); close(sock); return NULL; } /* Fire NS_CONNECT on next poll. */ nc->flags |= NSF_CONNECTING; /* No ns_destroy_conn() call after this! */ ns_set_sock(nc, sock); return nc; }
static void ns_close_conn(struct ns_connection *conn) { DBG(("%p %lu", conn, conn->flags)); ns_call(conn, NS_CLOSE, NULL); ns_remove_conn(conn); ns_destroy_conn(conn); }