Add bind option to net/connect

This will allow us to set the address we use for outgoing connections.

Builds, haven't checked it passes current tests, haven't checked it
actually works either.
This commit is contained in:
llmII 2021-09-06 08:54:24 -05:00
parent 7037532943
commit 6e8584e8e0
No known key found for this signature in database
GPG Key ID: E3AD2E259F58A9A0
1 changed files with 59 additions and 12 deletions

View File

@ -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