diff options
| author | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2009-12-03 14:41:08 +0000 |
|---|---|---|
| committer | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2009-12-03 14:41:08 +0000 |
| commit | 4156debe5cdb617e25f6c974c95270dc1ce345de (patch) | |
| tree | dd794abdead6be0b2e18baaf496e61af05d18d5b /src | |
| parent | 854660005dc8394ee9df5eba143b3e8d95140688 (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.cc | 10 | ||||
| -rw-r--r-- | src/SocketCore.cc | 193 | ||||
| -rw-r--r-- | src/SocketCore.h | 13 | ||||
| -rw-r--r-- | src/main.cc | 6 | ||||
| -rw-r--r-- | src/message.h | 2 | ||||
| -rw-r--r-- | src/prefs.cc | 2 | ||||
| -rw-r--r-- | src/prefs.h | 2 | ||||
| -rw-r--r-- | src/usage_text.h | 3 |
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.") |
