summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBruce Guenter <bruce@untroubled.org>2016-01-15 16:36:44 -0600
committerBruce Guenter <bruce@untroubled.org>2016-01-15 17:12:06 -0600
commitd3afb601c93e36cfd0cbf5b0962baacc1e88e956 (patch)
tree3bedf0bd1544df3e22fa8919c18ad8d01a5b0b20 /lib
parent5e91ef61f7ec0638d8d7aa42da328161fa232aa8 (diff)
protocols: Add support for binding the source address
Diffstat (limited to 'lib')
-rw-r--r--lib/connect.h2
-rw-r--r--lib/errcodes.cc1
-rw-r--r--lib/errcodes.h1
-rw-r--r--lib/tcpconnect.cc62
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);