From 91e38abd714d5dda2764022ea370d7dde73a55cc Mon Sep 17 00:00:00 2001 From: multiplexd Date: Mon, 17 Feb 2020 19:46:02 +0000 Subject: Implement source address selection This commit introduces a '-S' command line option and a "bind" configuration file option for selecting the source address when making outbound TCP connections (similar to the corresponding option in catgirl(1)). --- bounce.c | 7 +++++-- bounce.h | 2 +- pounce.1 | 8 +++++++- server.c | 35 ++++++++++++++++++++++++++++++----- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/bounce.c b/bounce.c index eef6c12..19e2dd4 100644 --- a/bounce.c +++ b/bounce.c @@ -258,6 +258,7 @@ int main(int argc, char *argv[]) { bool insecure = false; const char *clientCert = NULL; const char *clientPriv = NULL; + const char *serverBindHost = NULL; const char *host = NULL; const char *port = "6697"; @@ -271,7 +272,7 @@ int main(int argc, char *argv[]) { const char *join = NULL; const char *quit = "connection reset by purr"; - const char *Opts = "!A:C:H:K:NP:U:W:a:c:ef:g:h:j:k:n:p:q:r:s:u:vw:xy:"; + const char *Opts = "!A:C:H:K:NP:S:U:W:a:c:ef:g:h:j:k:n:p:q:r:s:u:vw:xy:"; const struct option LongOpts[] = { { "insecure", no_argument, NULL, '!' }, { "local-ca", required_argument, NULL, 'A' }, @@ -280,6 +281,7 @@ int main(int argc, char *argv[]) { { "local-priv", required_argument, NULL, 'K' }, { "no-names", no_argument, NULL, 'N' }, { "local-port", required_argument, NULL, 'P' }, + { "bind", required_argument, NULL, 'S' }, { "local-path", required_argument, NULL, 'U' }, { "local-pass", required_argument, NULL, 'W' }, { "sasl-plain", required_argument, NULL, 'a' }, @@ -321,6 +323,7 @@ int main(int argc, char *argv[]) { break; case 'K': strlcpy(privPath, optarg, sizeof(privPath)); break; case 'N': stateNoNames = true; break; case 'P': bindPort = optarg; + break; case 'S': serverBindHost = optarg; break; case 'U': strlcpy(bindPath, optarg, sizeof(bindPath)); break; case 'W': clientPass = optarg; break; case 'a': sasl = true; plain = optarg; @@ -403,7 +406,7 @@ int main(int argc, char *argv[]) { : localBind(bind, ARRAY_LEN(bind), bindHost, bindPort); serverConfig(insecure, clientCert, clientPriv); - int server = serverConnect(host, port); + int server = serverConnect(serverBindHost, host, port); #ifdef __FreeBSD__ int error = cap_enter(); diff --git a/bounce.h b/bounce.h index a0f9160..20d7d40 100644 --- a/bounce.h +++ b/bounce.h @@ -133,7 +133,7 @@ size_t localUnix(int fds[], size_t cap, const char *path); struct tls *localAccept(int *fd, int bind); void serverConfig(bool insecure, const char *cert, const char *priv); -int serverConnect(const char *host, const char *port); +int serverConnect(const char *bindHost, const char *host, const char *port); void serverRecv(void); void serverSend(const char *ptr, size_t len); void serverFormat(const char *format, ...) diff --git a/pounce.1 b/pounce.1 index 275b5a6..c55310b 100644 --- a/pounce.1 +++ b/pounce.1 @@ -1,4 +1,4 @@ -.Dd January 17, 2020 +.Dd February 18, 2020 .Dt POUNCE 1 .Os . @@ -14,6 +14,7 @@ .Op Fl H Ar host .Op Fl K Ar priv .Op Fl P Ar port +.Op Fl S Ar bind .Op Fl U Ar unix .Op Fl W Ar pass .Op Fl a Ar auth @@ -121,6 +122,11 @@ Bind to .Ar port . The default port is 6697. . +.It Fl S Ar host , Cm bind = Ar host +Bind to source address +.Ar host +when connecting to the server. +. .It Fl U Ar path , Cm local-path = Ar path Bind to a UNIX-domain socket at .Ar path . diff --git a/server.c b/server.c index ce5ae87..2bb897d 100644 --- a/server.c +++ b/server.c @@ -63,22 +63,47 @@ void serverConfig(bool insecure, const char *cert, const char *priv) { tls_config_free(config); } -int serverConnect(const char *host, const char *port) { +int serverConnect(const char *bindHost, const char *host, const char *port) { assert(client); + int error; + int sock = -1; struct addrinfo *head; struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, }; - int error = getaddrinfo(host, port, &hints, &head); + + if (bindHost) { + error = getaddrinfo(bindHost, NULL, &hints, &head); + if (error) errx(EX_NOHOST, "%s: %s", bindHost, gai_strerror(error)); + + for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) err(EX_OSERR, "socket"); + + error = bind(sock, ai->ai_addr, ai->ai_addrlen); + if (!error) { + hints.ai_family = ai->ai_family; + break; + } + + close(sock); + sock = -1; + } + if (sock < 0) err(EX_UNAVAILABLE, "%s", bindHost); + freeaddrinfo(head); + } + + error = getaddrinfo(host, port, &hints, &head); if (error) errx(EX_NOHOST, "%s:%s: %s", host, port, gai_strerror(error)); - int sock = -1; for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) err(EX_OSERR, "socket"); + if (sock < 0) { + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) err(EX_OSERR, "socket"); + } error = connect(sock, ai->ai_addr, ai->ai_addrlen); if (!error) break; -- cgit 1.4.1