enum error proto_open_connection(Var arglist, int *read_fd, int *write_fd, const char **local_name, const char **remote_name) { /* These are `static' rather than `volatile' because I can't cope with * getting all those nasty little parameter-passing rules right. This * function isn't recursive anyway, so it doesn't matter. */ struct sockaddr_in rec_addr; struct t_bind received; static const char *host_name; static int port; static Timer_ID id; int fd, result; int timeout = server_int_option("name_lookup_timeout", 5); static struct sockaddr_in addr; static Stream *st1 = 0, *st2 = 0; if (!st1) { st1 = new_stream(20); st2 = new_stream(50); } if (arglist.v.list[0].v.num != 2) return E_ARGS; else if (arglist.v.list[1].type != TYPE_STR || arglist.v.list[2].type != TYPE_INT) return E_TYPE; host_name = arglist.v.list[1].v.str; port = arglist.v.list[2].v.num; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = lookup_addr_from_name(host_name, timeout); if (addr.sin_addr.s_addr == 0) return E_INVARG; /* Cast to (void *) here to workaround const-less decls on some systems. */ fd = t_open((void *) "/dev/tcp", O_RDWR, 0); if (fd < 0) { if (t_errno != TSYSERR || errno != EMFILE) log_ti_error("Making endpoint in proto_open_connection"); return E_QUOTA; } received.addr.maxlen = sizeof(rec_addr); received.addr.len = sizeof(rec_addr); received.addr.buf = (void *) &rec_addr; if (t_bind(fd, 0, &received) < 0) { log_ti_error("Binding outbound endpoint"); t_close(fd); return E_QUOTA; } call->addr.maxlen = sizeof(addr); call->addr.len = sizeof(addr); call->addr.buf = (void *) &addr; TRY id = set_timer(server_int_option("outbound_connect_timeout", 5), timeout_proc, 0); result = t_connect(fd, call, 0); cancel_timer(id); EXCEPT(timeout_exception) result = -1; errno = ETIMEDOUT; t_errno = TSYSERR; reenable_timers(); ENDTRY if (result < 0) { t_close(fd); log_ti_error("Connecting in proto_open_connection"); return E_QUOTA; } if (!set_rw_able(fd)) { t_close(fd); return E_QUOTA; } *read_fd = *write_fd = fd; stream_printf(st1, "port %d", (int) ntohs(rec_addr.sin_port)); *local_name = reset_stream(st1); stream_printf(st2, "%s, port %d", host_name, port); *remote_name = reset_stream(st2); return E_NONE; }
enum error proto_open_connection(Var arglist, int *read_fd, int *write_fd, const char **local_name, const char **remote_name) { /* These are `static' rather than `volatile' because I can't cope with * getting all those nasty little parameter-passing rules right. This * function isn't recursive anyway, so it doesn't matter. */ static const char *host_name; static int port; static Timer_ID id; size_t length; int s, result; int timeout = server_int_option("name_lookup_timeout", 5); static struct sockaddr_in addr; static Stream *st1 = 0, *st2 = 0; if (!outbound_network_enabled) return E_PERM; if (!st1) { st1 = new_stream(20); st2 = new_stream(50); } if (arglist.v.list[0].v.num != 2) return E_ARGS; else if (arglist.v.list[1].type != TYPE_STR || arglist.v.list[2].type != TYPE_INT) return E_TYPE; host_name = arglist.v.list[1].v.str; port = arglist.v.list[2].v.num; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = lookup_addr_from_name(host_name, timeout); if (addr.sin_addr.s_addr == 0) return E_INVARG; s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { if (errno != EMFILE) log_perror("Making socket in proto_open_connection"); return E_QUOTA; } if (bind_local_ip != INADDR_ANY) { static struct sockaddr_in local_addr; local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = bind_local_ip; local_addr.sin_port = 0; /* In theory, if the original listen() succeeded, * then this should too, but who knows, really? */ if (bind(s, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { enum error e = E_QUOTA; log_perror("Binding local address in proto_open_connection"); if (errno == EACCES) e = E_PERM; close(s); return e; } } TRY { id = set_timer(server_int_option("outbound_connect_timeout", 5), timeout_proc, 0); result = connect(s, (struct sockaddr *) &addr, sizeof(addr)); cancel_timer(id); } EXCEPT(timeout_exception) { result = -1; errno = ETIMEDOUT; reenable_timers(); } ENDTRY; if (result < 0) { close(s); if (errno == EADDRNOTAVAIL || errno == ECONNREFUSED || errno == ENETUNREACH || errno == ETIMEDOUT) return E_INVARG; log_perror("Connecting in proto_open_connection"); return E_QUOTA; } length = sizeof(addr); if (getsockname(s, (struct sockaddr *) &addr, &length) < 0) { close(s); log_perror("Getting local name in proto_open_connection"); return E_QUOTA; } *read_fd = *write_fd = s; stream_printf(st1, "port %d", (int) ntohs(addr.sin_port)); *local_name = reset_stream(st1); stream_printf(st2, "%s, port %d", host_name, port); *remote_name = reset_stream(st2); return E_NONE; }