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
pull/1/head
Tatsuhiro Tsujikawa 2009-12-03 14:41:08 +00:00
parent 854660005d
commit 4156debe5c
12 changed files with 236 additions and 32 deletions

View File

@ -1,3 +1,18 @@
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
2009-11-29 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Updated man page for bt-prioritize-piece option in -i list.

View File

@ -162,6 +162,9 @@
/* Define to 1 if you have the `gethostbyname' function. */
#undef HAVE_GETHOSTBYNAME
/* Define to 1 if you have the `getifaddrs' function. */
#undef HAVE_GETIFADDRS
/* Define to 1 if you have the `getpagesize' function. */
#undef HAVE_GETPAGESIZE
@ -177,6 +180,9 @@
/* Define if you have the iconv() function and it works. */
#undef HAVE_ICONV
/* Define to 1 if you have the <ifaddrs.h> header file. */
#undef HAVE_IFADDRS_H
/* Define to 1 if you have the `inet_aton' function. */
#undef HAVE_INET_ATON

4
configure vendored
View File

@ -7682,7 +7682,8 @@ for ac_header in argz.h \
termios.h \
unistd.h \
utime.h \
wchar.h
wchar.h \
ifaddrs.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@ -14402,6 +14403,7 @@ for ac_func in __argz_count \
getcwd \
gethostbyaddr \
gethostbyname \
getifaddrs \
getpagesize \
inet_ntoa \
memchr \

View File

@ -214,7 +214,8 @@ AC_CHECK_HEADERS([argz.h \
termios.h \
unistd.h \
utime.h \
wchar.h])
wchar.h \
ifaddrs.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
@ -263,6 +264,7 @@ AC_CHECK_FUNCS([__argz_count \
getcwd \
gethostbyaddr \
gethostbyname \
getifaddrs \
getpagesize \
inet_ntoa \
memchr \

View File

@ -240,6 +240,16 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
op->addTag(TAG_BASIC);
handlers.push_back(op);
}
{
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,

View File

@ -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;
}
static sock_t bindInternal(int family, int socktype, int protocol,
const struct sockaddr* addr, socklen_t addrlen)
{
sock_t fd = socket(family, socktype, protocol);
if(fd == (sock_t) -1) {
return -1;
}
int sockopt = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (a2_sockopt_t) &sockopt,
sizeof(sockopt)) < 0) {
CLOSE(fd);
return -1;
}
if(::bind(fd, addr, addrlen) == -1) {
LogFactory::getInstance()->debug("%s", strerror(errno));
CLOSE(fd);
return -1;
}
return fd;
}
void SocketCore::bind(uint16_t port, int flags)
{
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());
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;
}
}
struct addrinfo* rp;
for(rp = res; rp; rp = rp->ai_next) {
sock_t fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if(fd == (sock_t) -1) {
continue;
}
int sockopt = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (a2_sockopt_t) &sockopt, sizeof(sockopt)) < 0) {
CLOSE(fd);
continue;
}
if(::bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
CLOSE(fd);
continue;
}
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

View File

@ -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

View File

@ -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

View File

@ -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.")

View File

@ -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

View File

@ -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

View File

@ -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.")