diff --git a/src/core/net.c b/src/core/net.c index 7fde0711..e85f2991 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -259,8 +259,9 @@ static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) { } /* Needs argc >= offset + 2 */ -/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1, otherwise 0 */ -static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) { +/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1, + * otherwise 0. Also, ignores is_bind when is a unix socket. */ +static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix, int is_bind) { /* Unix socket support - not yet supported on windows. */ #ifndef JANET_WINDOWS if (janet_keyeq(argv[offset], "unix")) { @@ -285,12 +286,29 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock } #endif /* Get host and port */ - const char *host = janet_getcstring(argv, offset); - const char *port; - if (janet_checkint(argv[offset + 1])) { - port = (const char *)janet_to_string(argv[offset + 1]); + char *host = NULL, *port = NULL; + char *err = NULL; + /* if is_bind is set, skip offsets and ports! */ + if (!is_bind) { + host = (char *)janet_getcstring(argv, offset); + if (janet_checkint(argv[offset + 1])) { + port = (char *)janet_to_string(argv[offset + 1]); + } else { + port = (char *)janet_optcstring(argv, offset + 2, offset + 1, NULL); + } + err = "could not get address info: %s"; } else { - port = janet_optcstring(argv, offset + 2, offset + 1, NULL); + /* when is_bind is set, we're performing a connect, but wanting to + * specify from where we connect, and in general don't care about a + * port */ + int32_t current_offset = 3; + if (janet_keyeq(argv[current_offset], "stream") || + janet_keyeq(argv[current_offset], "datagram")) { + current_offset = 4; + } + host = (char *)janet_getcstring(argv, current_offset); + port = NULL; + err = "could not get address info for connect bind: %s"; } /* getaddrinfo */ struct addrinfo *ai = NULL; @@ -301,7 +319,7 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock hints.ai_flags = passive ? AI_PASSIVE : 0; int status = getaddrinfo(host, port, &hints, &ai); if (status) { - janet_panicf("could not get address info: %s", gai_strerror(status)); + janet_panicf(err, gai_strerror(status)); } *is_unix = 0; return ai; @@ -322,7 +340,7 @@ JANET_CORE_FN(cfun_net_sockaddr, int socktype = janet_get_sockettype(argv, argc, 2); int is_unix = 0; int make_arr = (argc >= 3 && janet_truthy(argv[3])); - struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix); + struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, 0); #ifndef JANET_WINDOWS /* no unix domain socket support on windows yet */ if (is_unix) { @@ -361,11 +379,11 @@ JANET_CORE_FN(cfun_net_connect, "Open a connection to communicate with a server. Returns a duplex stream " "that can be used to communicate with the server. Type is an optional keyword " "to specify a connection type, either :stream or :datagram. The default is :stream. ") { - janet_arity(argc, 2, 3); + janet_arity(argc, 2, 4); int socktype = janet_get_sockettype(argv, argc, 2); int is_unix = 0; - struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix); + struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, 0); /* Create socket */ JSock sock = JSOCKDEFAULT; @@ -401,6 +419,35 @@ JANET_CORE_FN(cfun_net_connect, } } + /* TODO: check if need bind and bind! */ + ai = NULL; + if (argc >= 3 && is_unix == 0) { + if (argc == 4 || + (!janet_keyeq(argv[3], "stream") && + !janet_keyeq(argv[3], "datagram"))) { + ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, 1); + } + } + + /* Check all addrinfos in a loop for the first that we can bind to. */ + struct addrinfo *rp = NULL; + for (rp = ai; rp != NULL; rp = rp->ai_next) { +#ifdef JANET_WINDOWS + sock = WSASocketW(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED); +#else + sock = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol); +#endif + if (!JSOCKVALID(sock)) continue; + + /* Bind */ + if (bind(sock, rp->ai_addr, (int) rp->ai_addrlen) == 0) break; + JSOCKCLOSE(sock); + } + freeaddrinfo(ai); + if (NULL == rp) { + janet_panic("could not bind to any sockets"); + } + /* Connect to socket */ #ifdef JANET_WINDOWS int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL); @@ -502,7 +549,7 @@ JANET_CORE_FN(cfun_net_listen, /* Get host, port, and handler*/ int socktype = janet_get_sockettype(argv, argc, 2); int is_unix = 0; - struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix); + struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix, 0); JSock sfd = JSOCKDEFAULT; #ifndef JANET_WINDOWS