diff --git a/src/core/net.c b/src/core/net.c index 8f522a27..9b7d6b39 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -261,7 +261,7 @@ 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. 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) { +static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) { /* Unix socket support - not yet supported on windows. */ #ifndef JANET_WINDOWS if (janet_keyeq(argv[offset], "unix")) { @@ -286,24 +286,12 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock } #endif /* Get host and port */ - 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"; + char *host = (char *)janet_getcstring(argv, offset); + char *port = NULL; + if (janet_checkint(argv[offset + 1])) { + port = (char *)janet_to_string(argv[offset + 1]); } else { - /* 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 */ - host = (char *)janet_getcstring(argv, 3); - port = NULL; - err = "could not get address info for connect bind: %s"; + port = (char *)janet_optcstring(argv, offset + 2, offset + 1, NULL); } /* getaddrinfo */ struct addrinfo *ai = NULL; @@ -314,7 +302,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(err, gai_strerror(status)); + janet_panicf("could not get address info: %s", gai_strerror(status)); } *is_unix = 0; return ai; @@ -335,7 +323,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, 0); + struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix); #ifndef JANET_WINDOWS /* no unix domain socket support on windows yet */ if (is_unix) { @@ -370,24 +358,47 @@ JANET_CORE_FN(cfun_net_sockaddr, } JANET_CORE_FN(cfun_net_connect, - "(net/connect host port &opt type bindhost)", + "(net/connect host port &opt type bindhost bindport)", "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. " "Bindhost is an optional string to select from what address to make the outgoing " "connection, with the default being the same as using the OS's preferred address. ") { - janet_arity(argc, 2, 4); + janet_arity(argc, 2, 5); + /* Check arguments */ int socktype = janet_get_sockettype(argv, argc, 2); int is_unix = 0; + const char *bindhost = janet_optcstring(argv, argc, 3, NULL); + int bindport = 0; + if (janet_checkint(argv[4])) { + bindport = janet_unwrap_integer(argv[4]); + } else { + const char *portstring = (char *)janet_optcstring(argv, argc, 4, "0"); + bindport = atoi(portstring); + } + /* Where we're connecting to */ - struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, 0); + struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix); /* Check if we're binding address */ struct addrinfo *binding = NULL; - if (argc > 3 && is_unix == 0 && !janet_checktype(argv[3], JANET_NIL)) { - int is_bindhost_unix = 0; /* discarded value */ - binding = janet_get_addrinfo(argv, 0, socktype, 0, &is_bindhost_unix, 1); + if (bindhost != NULL) { + if (is_unix) { + freeaddrinfo(ai); + janet_panic("bindhost not supported for unix domain sockets"); + } + /* getaddrinfo */ + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = socktype; + hints.ai_flags = 0; + int status = getaddrinfo(bindhost, NULL, &hints, &binding); + if (status) { + freeaddrinfo(ai); + janet_panicf("could not get address info for bindhost: %s", gai_strerror(status)); + } } /* Create socket */ @@ -398,7 +409,9 @@ JANET_CORE_FN(cfun_net_connect, if (is_unix) { sock = socket(AF_UNIX, socktype | JSOCKFLAGS, 0); if (!JSOCKVALID(sock)) { - janet_panicf("could not create socket: %V", janet_ev_lasterr()); + Janet v = janet_ev_lasterr(); + janet_free(ai); + janet_panicf("could not create socket: %V", v); } addr = (void *) ai; addrlen = sizeof(struct sockaddr_un); @@ -419,39 +432,36 @@ JANET_CORE_FN(cfun_net_connect, } } if (NULL == addr) { + Janet v = janet_ev_lasterr(); freeaddrinfo(ai); - janet_panicf("could not create socket: %V", janet_ev_lasterr()); + janet_panicf("could not create socket: %V", v); } } - /* Perform bind if binding */ - if (binding != NULL) { - /* Check all addrinfos in a loop for the first that we can bind to. */ - struct addrinfo *rp = NULL; - for (rp = binding; 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; + /* Bind to bindhost and bindport if given */ + if (bindhost != NULL) { + /* Bind to a specific network interface (and optionally a specific local port) */ + struct sockaddr_in localaddr; + memset(&localaddr, 0, sizeof(localaddr)); + localaddr.sin_family = AF_INET; + localaddr.sin_addr.s_addr = inet_addr(bindhost); + localaddr.sin_port = bindport; + if (0 == bind(sock, (struct sockaddr *)&localaddr, sizeof(localaddr))) { + Janet lasterr = janet_ev_lasterr(); + freeaddrinfo(ai); JSOCKCLOSE(sock); - } - freeaddrinfo(binding); - if (NULL == rp) { - janet_panicf("could not bind outgoing address: %V", janet_ev_lasterr()); + janet_panicf("could not bind outgoing address: %V", lasterr); } } /* Connect to socket */ #ifdef JANET_WINDOWS int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL); + Janet lasterr = janet_ev_lasterr(); freeaddrinfo(ai); #else int status = connect(sock, addr, addrlen); + Janet lasterr = janet_ev_lasterr(); if (is_unix) { janet_free(ai); } else { @@ -461,7 +471,7 @@ JANET_CORE_FN(cfun_net_connect, if (status == -1) { JSOCKCLOSE(sock); - janet_panicf("could not connect to socket: %V", janet_ev_lasterr()); + janet_panicf("could not connect socket: %V", lasterr); } /* Set up the socket for non-blocking IO after connect - TODO - non-blocking connect? */ @@ -547,7 +557,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, 0); + struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix); JSock sfd = JSOCKDEFAULT; #ifndef JANET_WINDOWS