diff options
author | Bruce Guenter <bruce@untroubled.org> | 2016-01-15 16:36:44 -0600 |
---|---|---|
committer | Bruce Guenter <bruce@untroubled.org> | 2016-01-15 17:12:06 -0600 |
commit | d3afb601c93e36cfd0cbf5b0962baacc1e88e956 (patch) | |
tree | 3bedf0bd1544df3e22fa8919c18ad8d01a5b0b20 /lib | |
parent | 5e91ef61f7ec0638d8d7aa42da328161fa232aa8 (diff) |
protocols: Add support for binding the source address
Diffstat (limited to 'lib')
-rw-r--r-- | lib/connect.h | 2 | ||||
-rw-r--r-- | lib/errcodes.cc | 1 | ||||
-rw-r--r-- | lib/errcodes.h | 1 | ||||
-rw-r--r-- | lib/tcpconnect.cc | 62 |
4 files changed, 56 insertions, 10 deletions
diff --git a/lib/connect.h b/lib/connect.h index 7b0fbf5..d3354b0 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -1,6 +1,6 @@ #ifndef NULLMAILER_CONNECT__H__ #define NULLMAILER_CONNECT__H__ -extern int tcpconnect(const char* hostname, int port); +extern int tcpconnect(const char* hostname, int port, const char* source); #endif // NULLMAILER_CONNECT__H__ diff --git a/lib/errcodes.cc b/lib/errcodes.cc index 7aaef64..2cf2a31 100644 --- a/lib/errcodes.cc +++ b/lib/errcodes.cc @@ -43,6 +43,7 @@ const char* errorstr(int code) case ERR_CONFIG: return "Could not read config files"; case ERR_MSG_REFUSED: return "Server refused the message"; case ERR_MSG_PERMFAIL: return "Permanent error in sending the message"; + case ERR_BIND_FAILED: return "Failed to bind source address"; } return (code & ERR_PERMANENT_FLAG) ? "Unspecified permanent error" diff --git a/lib/errcodes.h b/lib/errcodes.h index 4302678..94564ee 100644 --- a/lib/errcodes.h +++ b/lib/errcodes.h @@ -19,6 +19,7 @@ #define ERR_MSG_TEMPFAIL 16 // server temporarily failed to receive #define ERR_UNKNOWN 17 // Arbitrary error code #define ERR_CONFIG 18 // Error reading a config file +#define ERR_BIND_FAILED 19 // Failed to bind source address // Permanent errors #define ERR_GHBN_FATAL 33 // gethostbyname failed with NO_RECOVERY diff --git a/lib/tcpconnect.cc b/lib/tcpconnect.cc index ba7796d..83b9d4c 100644 --- a/lib/tcpconnect.cc +++ b/lib/tcpconnect.cc @@ -61,29 +61,63 @@ static int getaddr(const char* hostname, int port, struct addrinfo** result) return e ? err_return(e, ERR_GHBN_TEMP) : 0; } -int tcpconnect(const char* hostname, int port) +static bool canbind(int family, const struct addrinfo* ai) +{ + for (; ai; ai = ai->ai_next) + if (ai->ai_family == family) + return true; + return false; +} + +static bool bindit(int fd, int family, const struct addrinfo* ai) +{ + for (; ai; ai = ai->ai_next) + if (ai->ai_family == family) + if (bind(fd, ai->ai_addr, ai->ai_addrlen) == 0) + return true; + return false; +} + +int tcpconnect(const char* hostname, int port, const char* source) { struct addrinfo* res; int err = getaddr(hostname, port, &res); if (err) return err; + struct addrinfo* source_addr = NULL; + if (source) { + err = getaddr(source, 0, &source_addr); + if (err) + return err; + } int s = -1; + err = ERR_CONN_FAILED; struct addrinfo* orig_res = res; for (; res; res = res->ai_next ) { - s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if(s > 0) { - if(connect(s, res->ai_addr, res->ai_addrlen) == 0) - break; - close(s); - s = -1; + if (!source_addr || canbind(res->ai_family, source_addr)) { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if(s > 0) { + if(source_addr && !bindit(s, res->ai_family, source_addr)) { + close(s); + err = ERR_BIND_FAILED; + s = -1; + break; + } + if(connect(s, res->ai_addr, res->ai_addrlen) == 0) + break; + close(s); + s = -1; + } } } freeaddrinfo(orig_res); + if (source_addr) + freeaddrinfo(source_addr); if(s < 0) - return err_return(errno, ERR_CONN_FAILED); + return err_return(errno, err); return s; } @@ -98,17 +132,27 @@ static int sethostbyname(const char* hostname, struct sockaddr_in& sa) return 0; } -int tcpconnect(const char* hostname, int port) +int tcpconnect(const char* hostname, int port, const char* source) { struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); int e = sethostbyname(hostname, sa); if(e) return e; + struct sockaddr_in source_sa; + memset(&source_sa, 0, sizeof source_sa); + if(source) { + e = sethostbyname(source, source_sa); + if(e) return e; + } sa.sin_family = AF_INET; sa.sin_port = htons(port); int s = socket(PF_INET, SOCK_STREAM, 0); if(s == -1) return -ERR_SOCKET; + if(source && bind(s, (sockaddr*)&source_sa, sizeof source_sa) != 0) { + close(s); + return err_return(errno, ERR_BIND_FAILED); + } if(connect(s, (sockaddr*)&sa, sizeof(sa)) != 0) { close(s); return err_return(errno, ERR_CONN_FAILED); |