summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/connect.h4
-rw-r--r--lib/errcodes.cc1
-rw-r--r--lib/errcodes.h1
-rw-r--r--lib/tcpconnect.cc84
4 files changed, 70 insertions, 20 deletions
diff --git a/lib/connect.h b/lib/connect.h
index 0f2af85..d3354b0 100644
--- a/lib/connect.h
+++ b/lib/connect.h
@@ -1,8 +1,6 @@
#ifndef NULLMAILER_CONNECT__H__
#define NULLMAILER_CONNECT__H__
-#include "mystring/mystring.h"
-
-extern int tcpconnect(const mystring& 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 1d5f42f..83b9d4c 100644
--- a/lib/tcpconnect.cc
+++ b/lib/tcpconnect.cc
@@ -50,59 +50,109 @@ static int err_return(int errn, int dflt)
#ifdef HAVE_GETADDRINFO
-int tcpconnect(const mystring& hostname, int port)
+static int getaddr(const char* hostname, int port, struct addrinfo** result)
{
- struct addrinfo req, *res, *orig_res;
const char *service = itoa(port, 6);
-
+ struct addrinfo req;
memset(&req, 0, sizeof(req));
req.ai_flags = AI_NUMERICSERV;
req.ai_socktype = SOCK_STREAM;
- int e = getaddrinfo(hostname.c_str(), service, &req, &res);
- if(e)
- return err_return(e, ERR_GHBN_TEMP);
+ int e = getaddrinfo(hostname, service, &req, result);
+ return e ? err_return(e, ERR_GHBN_TEMP) : 0;
+}
+
+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;
- orig_res = res;
+ 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;
}
#else
-static int sethostbyname(const mystring& hostname, struct sockaddr_in& sa)
+static int sethostbyname(const char* hostname, struct sockaddr_in& sa)
{
- struct hostent *he = gethostbyname(hostname.c_str());
+ struct hostent *he = gethostbyname(hostname);
if(!he)
return err_return(h_errno, ERR_GHBN_TEMP);
memcpy(&sa.sin_addr, he->h_addr, he->h_length);
return 0;
}
-int tcpconnect(const mystring& 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);