summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2009-12-03 14:41:08 +0000
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2009-12-03 14:41:08 +0000
commit4156debe5cdb617e25f6c974c95270dc1ce345de (patch)
treedd794abdead6be0b2e18baaf496e61af05d18d5b /src
parent854660005dc8394ee9df5eba143b3e8d95140688 (diff)
2009-12-03 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Added --interface option. This feature binds sockets to given interface. You can specify interface name, IP address and hostname. * configure.ac * src/OptionHandlerFactory.cc * src/SocketCore.cc * src/SocketCore.h * src/main.cc * src/message.h * src/prefs.cc * src/prefs.h * src/usage_text.h
Diffstat (limited to 'src')
-rw-r--r--src/OptionHandlerFactory.cc10
-rw-r--r--src/SocketCore.cc193
-rw-r--r--src/SocketCore.h13
-rw-r--r--src/main.cc6
-rw-r--r--src/message.h2
-rw-r--r--src/prefs.cc2
-rw-r--r--src/prefs.h2
-rw-r--r--src/usage_text.h3
8 files changed, 205 insertions, 26 deletions
diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc
index 60ff3387..6341b1f6 100644
--- a/src/OptionHandlerFactory.cc
+++ b/src/OptionHandlerFactory.cc
@@ -242,6 +242,16 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
}
{
SharedHandle<OptionHandler> op(new DefaultOptionHandler
+ (PREF_INTERFACE,
+ TEXT_INTERFACE,
+ NO_DEFAULT_VALUE,
+ "interface, IP address, hostname",
+ OptionHandler::REQ_ARG));
+ op->addTag(TAG_ADVANCED);
+ handlers.push_back(op);
+ }
+ {
+ SharedHandle<OptionHandler> op(new DefaultOptionHandler
(PREF_LOG,
TEXT_LOG,
NO_DEFAULT_VALUE,
diff --git a/src/SocketCore.cc b/src/SocketCore.cc
index 423d5efd..bdd5f02b 100644
--- a/src/SocketCore.cc
+++ b/src/SocketCore.cc
@@ -35,6 +35,9 @@
#include "SocketCore.h"
#include <unistd.h>
+#ifdef HAVE_IFADDRS_H
+# include <ifaddrs.h>
+#endif // HAVE_IFADDRS_H
#include <cerrno>
#include <cstring>
@@ -51,6 +54,7 @@
#include "util.h"
#include "TimeA2.h"
#include "a2functional.h"
+#include "LogFactory.h"
#ifdef ENABLE_SSL
# include "TLSContext.h"
#endif // ENABLE_SSL
@@ -83,6 +87,10 @@ SocketCore::PollMethod SocketCore::_pollMethod = SocketCore::POLL_METHOD_SELECT;
int SocketCore::_protocolFamily = AF_UNSPEC;
+SharedHandle<struct sockaddr_storage> SocketCore::_bindAddr;
+
+socklen_t SocketCore::_bindAddrLen = 0;
+
#ifdef ENABLE_SSL
SharedHandle<TLSContext> SocketCore::_tlsContext;
@@ -159,46 +167,81 @@ std::string uitos(T value)
return str;
}
-void SocketCore::bind(uint16_t port, int flags)
+static sock_t bindInternal(int family, int socktype, int protocol,
+ const struct sockaddr* addr, socklen_t addrlen)
{
- closeConnection();
-
- struct addrinfo hints;
- struct addrinfo* res;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = _protocolFamily;
- hints.ai_socktype = _sockType;
- hints.ai_flags = flags;
- hints.ai_protocol = 0;
- int s;
- s = getaddrinfo(0, uitos(port).c_str(), &hints, &res);
- if(s) {
- throw DL_ABORT_EX(StringFormat(EX_SOCKET_BIND, gai_strerror(s)).str());
- }
- struct addrinfo* rp;
- for(rp = res; rp; rp = rp->ai_next) {
- sock_t fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ sock_t fd = socket(family, socktype, protocol);
if(fd == (sock_t) -1) {
- continue;
+ return -1;
}
int sockopt = 1;
- if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (a2_sockopt_t) &sockopt, sizeof(sockopt)) < 0) {
+ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (a2_sockopt_t) &sockopt,
+ sizeof(sockopt)) < 0) {
CLOSE(fd);
- continue;
+ return -1;
}
- if(::bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
+ if(::bind(fd, addr, addrlen) == -1) {
+ LogFactory::getInstance()->debug("%s", strerror(errno));
CLOSE(fd);
- continue;
+ return -1;
+ }
+ return fd;
+}
+
+void SocketCore::bind(uint16_t port, int flags)
+{
+ closeConnection();
+
+ if(flags == 0 || _bindAddr.isNull()) {
+ struct addrinfo hints;
+ struct addrinfo* res;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = _protocolFamily;
+ hints.ai_socktype = _sockType;
+ hints.ai_flags = flags;
+ hints.ai_protocol = 0;
+ int s;
+ s = getaddrinfo(0, uitos(port).c_str(), &hints, &res);
+ if(s) {
+ throw DL_ABORT_EX(StringFormat(EX_SOCKET_BIND, gai_strerror(s)).str());
+ }
+ struct addrinfo* rp;
+ for(rp = res; rp; rp = rp->ai_next) {
+ sock_t fd = bindInternal(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
+ rp->ai_addr, rp->ai_addrlen);
+ if(fd == (sock_t) -1) {
+ continue;
+ }
+ sockfd = fd;
+ break;
+ }
+ freeaddrinfo(res);
+ } else {
+ sock_t fd = bindInternal
+ (_bindAddr->ss_family, _sockType, 0,
+ reinterpret_cast<const struct sockaddr*>(_bindAddr.get()), _bindAddrLen);
+ if(fd != (sock_t)-1) {
+ sockfd = fd;
}
- sockfd = fd;
- break;
}
- freeaddrinfo(res);
if(sockfd == (sock_t) -1) {
throw DL_ABORT_EX(StringFormat(EX_SOCKET_BIND, "all addresses failed").str());
}
}
+void SocketCore::bind(const struct sockaddr* addr, socklen_t addrlen)
+{
+ closeConnection();
+
+ sock_t fd = bindInternal(addr->sa_family, _sockType, 0, addr, addrlen);
+ if(fd != (sock_t)-1) {
+ sockfd = fd;
+ }
+ if(sockfd == (sock_t) -1) {
+ throw DL_ABORT_EX(StringFormat(EX_SOCKET_BIND, strerror(errno)).str());
+ }
+}
+
void SocketCore::beginListen()
{
if(listen(sockfd, 1) == -1) {
@@ -268,6 +311,15 @@ void SocketCore::establishConnection(const std::string& host, uint16_t port)
CLOSE(fd);
continue;
}
+
+ if(!_bindAddr.isNull()) {
+ if(::bind(fd, reinterpret_cast<const struct sockaddr*>(_bindAddr.get()),
+ _bindAddrLen) == -1) {
+ CLOSE(fd);
+ continue;
+ }
+ }
+
sockfd = fd;
// make socket non-blocking mode
setNonBlockingMode();
@@ -1073,4 +1125,93 @@ void SocketCore::useSelect()
_pollMethod = SocketCore::POLL_METHOD_SELECT;
}
+void SocketCore::bindAddress(const std::string& interface)
+{
+ SharedHandle<struct sockaddr_storage> bindAddr(new struct sockaddr_storage());
+ socklen_t bindAddrLen = 0;
+ memset(bindAddr.get(), 0, sizeof(struct sockaddr_storage));
+ bool found = false;
+#ifdef HAVE_GETIFADDRS
+ // First find interface in interface addresses
+ struct ifaddrs* ifaddr = 0;
+ if(getifaddrs(&ifaddr) == -1) {
+ throw DL_ABORT_EX
+ (StringFormat(MSG_INTERFACE_NOT_FOUND,
+ interface.c_str(), strerror(errno)).str());
+ } else {
+ auto_delete<struct ifaddrs*> ifaddrDeleter(ifaddr, freeifaddrs);
+ for(struct ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ if(!ifa->ifa_addr) {
+ continue;
+ }
+ int family = ifa->ifa_addr->sa_family;
+ if(_protocolFamily == AF_UNSPEC) {
+ if(family != AF_INET && family != AF_INET6) {
+ continue;
+ }
+ } else if(_protocolFamily == AF_INET) {
+ if(family != AF_INET) {
+ continue;
+ }
+ } else if(_protocolFamily == AF_INET6) {
+ if(family != AF_INET6) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ if(std::string(ifa->ifa_name) == interface) {
+ bindAddrLen =
+ family == AF_INET?sizeof(struct sockaddr_in):
+ sizeof(struct sockaddr_in6);
+ memcpy(bindAddr.get(), ifa->ifa_addr, bindAddrLen);
+ found = true;
+ break;
+ }
+ }
+ }
+#endif // HAVE_GETIFADDRS
+ if(!found) {
+ struct addrinfo hints;
+ struct addrinfo* res = 0;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = _protocolFamily;
+ hints.ai_socktype = 0;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0;
+ int s;
+ s = getaddrinfo(interface.c_str(), 0, &hints, &res);
+ if(s) {
+ throw DL_ABORT_EX
+ (StringFormat(MSG_INTERFACE_NOT_FOUND,
+ interface.c_str(), gai_strerror(s)).str());
+ } else {
+ auto_delete<struct addrinfo*> resDeleter(res, freeaddrinfo);
+ struct addrinfo* rp;
+ for(rp = res; rp; rp = rp->ai_next) {
+ bindAddrLen = rp->ai_addrlen;
+ memcpy(bindAddr.get(), rp->ai_addr, rp->ai_addrlen);
+ // Try to bind socket with this address. If it fails, the
+ // address is not for this machine.
+ try {
+ SocketCore socket;
+ socket.bind
+ (reinterpret_cast<const struct sockaddr*>(bindAddr.get()),
+ bindAddrLen);
+ } catch(RecoverableException& e) {
+ throw DL_ABORT_EX2
+ (StringFormat(MSG_INTERFACE_NOT_FOUND,
+ interface.c_str()).str(), e);
+ }
+ found = true;
+ break;
+ }
+ }
+ }
+ if(found) {
+ _bindAddr = bindAddr;
+ _bindAddrLen = bindAddrLen;
+ }
+}
+
} // namespace aria2
diff --git a/src/SocketCore.h b/src/SocketCore.h
index 896c1f63..9d06fbeb 100644
--- a/src/SocketCore.h
+++ b/src/SocketCore.h
@@ -92,6 +92,10 @@ private:
static int _protocolFamily;
+ static SharedHandle<struct sockaddr_storage> _bindAddr;
+
+ static socklen_t _bindAddrLen;
+
bool blocking;
int secure;
@@ -124,6 +128,8 @@ private:
void init();
+ void bind(const struct sockaddr* addr, socklen_t addrlen);
+
#ifdef HAVE_EPOLL
void initEPOLL();
@@ -349,6 +355,13 @@ public:
{
_protocolFamily = protocolFamily;
}
+
+ // Bind socket to interface. interface may be specified as a
+ // hostname, IP address or interface name like eth0. If the given
+ // interface is not found or binding socket is failed, exception
+ // will be thrown. Set _protocolFamily before calling this function
+ // if you limit protocol family.
+ static void bindAddress(const std::string& interface);
};
} // namespace aria2
diff --git a/src/main.cc b/src/main.cc
index 99dba3e1..79d3a5e1 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -208,6 +208,12 @@ downloadresultcode::RESULT main(int argc, char* argv[])
MessageDigestHelper::staticSHA1DigestInit();
#endif // ENABLE_MESSAGE_DIGEST
+ // Bind interface
+ if(!op->get(PREF_INTERFACE).empty()) {
+ std::string interface = op->get(PREF_INTERFACE);
+ SocketCore::bindAddress(interface);
+ }
+
#ifdef SIGPIPE
util::setGlobalSignalHandler(SIGPIPE, SIG_IGN, 0);
#endif
diff --git a/src/message.h b/src/message.h
index 5500e496..69eba714 100644
--- a/src/message.h
+++ b/src/message.h
@@ -175,6 +175,8 @@
#define MSG_CANNOT_PARSE_XML_RPC_REQUEST "Failed to parse xml-rpc request."
#define MSG_GOOD_BYE_SEEDER "Client is in seed state: Good Bye Seeder;)"
#define MSG_NOT_FILE _("Is '%s' a file?")
+#define MSG_INTERFACE_NOT_FOUND _("Failed to find given interface %s,"\
+ " cause: %s")
#define EX_TIME_OUT _("Timeout.")
#define EX_INVALID_CHUNK_SIZE _("Invalid chunk size.")
diff --git a/src/prefs.cc b/src/prefs.cc
index cb618e29..17614012 100644
--- a/src/prefs.cc
+++ b/src/prefs.cc
@@ -172,6 +172,8 @@ const std::string PREF_ON_DOWNLOAD_COMPLETE("on-download-complete");
const std::string PREF_ON_DOWNLOAD_ERROR("on-download-error");
// value: true | false
const std::string PREF_XML_RPC_LISTEN_ALL("xml-rpc-listen-all");
+// value: string
+const std::string PREF_INTERFACE("interface");
/**
* FTP related preferences
diff --git a/src/prefs.h b/src/prefs.h
index 27adf95d..f536f1f2 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -176,6 +176,8 @@ extern const std::string PREF_ON_DOWNLOAD_COMPLETE;
extern const std::string PREF_ON_DOWNLOAD_ERROR;
// value: true | false
extern const std::string PREF_XML_RPC_LISTEN_ALL;
+// value: string
+extern const std::string PREF_INTERFACE;
/**
* FTP related preferences
diff --git a/src/usage_text.h b/src/usage_text.h
index 339f6d35..2446a21d 100644
--- a/src/usage_text.h
+++ b/src/usage_text.h
@@ -575,3 +575,6 @@ _(" --bt-prioritize-piece=head[=SIZE],tail[=SIZE] Try to download first and last
" priority. tail=SIZE means the range of last SIZE\n"\
" bytes of each file. SIZE can include K or M(1K =\n"\
" 1024, 1M = 1024K).")
+#define TEXT_INTERFACE \
+_(" --interface=INTERFACE Bind sockets to given interface. You can specify\n"\
+ " interface name, IP address and hostname.")