diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/connect.h | 4 | ||||
-rw-r--r-- | lib/errcodes.cc | 1 | ||||
-rw-r--r-- | lib/errcodes.h | 1 | ||||
-rw-r--r-- | lib/tcpconnect.cc | 84 |
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); |