diff options
Diffstat (limited to 'net-p2p/transmission/files/transmission-2.73-bind-to-interface.patch')
| -rw-r--r-- | net-p2p/transmission/files/transmission-2.73-bind-to-interface.patch | 2216 |
1 files changed, 0 insertions, 2216 deletions
diff --git a/net-p2p/transmission/files/transmission-2.73-bind-to-interface.patch b/net-p2p/transmission/files/transmission-2.73-bind-to-interface.patch deleted file mode 100644 index 5a1fea7c..00000000 --- a/net-p2p/transmission/files/transmission-2.73-bind-to-interface.patch +++ /dev/null @@ -1,2216 +0,0 @@ -bind to interface - -From: eroen <eroen@occam.eroen.eu> - -by: ThornsArcana -source: https://trac.transmissionbt.com/ticket/2313 - -ported from bind-to-interface-r12779-trunk.patch by eroen ---- - Transmission.xcodeproj/project.pbxproj | 9 + - configure.ac | 69 ++++++++ - gtk/tr-prefs.c | 10 + - libtransmission/Makefile.am | 9 + - libtransmission/fdlimit.c | 241 +++++++++++++++++++++++++-- - libtransmission/fdlimit.h | 5 + - libtransmission/net-interfaces-test.c | 101 +++++++++++ - libtransmission/net-interfaces.c | 183 ++++++++++++++++++++ - libtransmission/net-interfaces.h | 49 +++++ - libtransmission/net.c | 175 ++++++++++++++++++- - libtransmission/net.h | 16 ++ - libtransmission/peer-io.c | 30 +++ - libtransmission/peer-mgr.c | 2 - libtransmission/resolver.c | 157 +++++++++++++++++ - libtransmission/resolver.h | 71 ++++++++ - libtransmission/resume.c | 4 - libtransmission/session.c | 289 +++++++++++++++++++++++++++++++- - libtransmission/session.h | 7 + - libtransmission/tr-dht.h | 5 + - libtransmission/tr-lpd.c | 3 - libtransmission/tr-udp.c | 4 - libtransmission/tr-utp.c | 8 + - libtransmission/transmission.h | 1 - macosx/Controller.m | 2 - third-party/libutp/utp_config.h | 4 - 25 files changed, 1413 insertions(+), 41 deletions(-) - create mode 100644 libtransmission/net-interfaces-test.c - create mode 100644 libtransmission/net-interfaces.c - create mode 100644 libtransmission/net-interfaces.h - create mode 100644 libtransmission/resolver.c - create mode 100644 libtransmission/resolver.h - -diff --git a/Transmission.xcodeproj/project.pbxproj b/Transmission.xcodeproj/project.pbxproj -index 898278b..ed26f53 100644 ---- a/Transmission.xcodeproj/project.pbxproj -+++ b/Transmission.xcodeproj/project.pbxproj -@@ -59,6 +59,8 @@ - 4DF7500D08A103AD007B0D70 /* ToolbarInfoTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* ToolbarInfoTemplate.png */; }; - 4DF7500E08A103AD007B0D70 /* ToolbarRemoveTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* ToolbarRemoveTemplate.png */; }; - 4DFBC2DF09C0970D00D5C571 /* Torrent.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFBC2DE09C0970D00D5C571 /* Torrent.m */; }; -+ 68FE979E11C77CE800FE4F58 /* net-interfaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FE979D11C77CE800FE4F58 /* net-interfaces.h */; }; -+ 68FE97A011C77D1200FE4F58 /* net-interfaces.c in Sources */ = {isa = PBXBuildFile; fileRef = 68FE979F11C77D1200FE4F58 /* net-interfaces.c */; }; - 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; - 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; - 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; -@@ -628,6 +630,8 @@ - 4DF7500908A103AD007B0D70 /* ToolbarRemoveTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ToolbarRemoveTemplate.png; path = macosx/Images/ToolbarRemoveTemplate.png; sourceTree = "<group>"; }; - 4DFBC2DD09C0970D00D5C571 /* Torrent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Torrent.h; path = macosx/Torrent.h; sourceTree = "<group>"; }; - 4DFBC2DE09C0970D00D5C571 /* Torrent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Torrent.m; path = macosx/Torrent.m; sourceTree = "<group>"; }; -+ 68FE979D11C77CE800FE4F58 /* net-interfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "net-interfaces.h"; path = "libtransmission/net-interfaces.h"; sourceTree = "<group>"; }; -+ 68FE979F11C77D1200FE4F58 /* net-interfaces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "net-interfaces.c"; path = "libtransmission/net-interfaces.c"; sourceTree = "<group>"; }; - 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; name = Info.plist; path = macosx/Info.plist; sourceTree = "<group>"; }; - 8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; }; - A200B8390A2263BA007BBB1E /* InfoWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoWindowController.h; path = macosx/InfoWindowController.h; sourceTree = "<group>"; }; -@@ -1723,6 +1727,8 @@ - A2A4EA0A0DE106E8000CE197 /* ConvertUTF.c */, - A2A4EA0B0DE106E8000CE197 /* ConvertUTF.h */, - 4DB74F070E8CD75100AEB1A8 /* wildmat.c */, -+ 68FE979D11C77CE800FE4F58 /* net-interfaces.h */, -+ 68FE979F11C77D1200FE4F58 /* net-interfaces.c */, - ); - name = libtransmission; - sourceTree = "<group>"; -@@ -3169,6 +3175,7 @@ - "-DSYS_DARWIN", - "-DWITH_UTP", - "-DHAVE_OPENSSL", -+ "-DHAVE_SYSCTLBYNAME", - "-D__TRANSMISSION__", - "-DHAVE_STRLCPY", - "-DHAVE_STRLCAT", -@@ -3317,6 +3324,7 @@ - "-DSYS_DARWIN", - "-DWITH_UTP", - "-DHAVE_OPENSSL", -+ "-DHAVE_SYSCTLBYNAME", - "-D__TRANSMISSION__", - "-DHAVE_STRLCPY", - "-DHAVE_STRLCAT", -@@ -3498,6 +3506,7 @@ - "-DSYS_DARWIN", - "-DWITH_UTP", - "-DHAVE_OPENSSL", -+ "-DHAVE_SYSCTLBYNAME", - "-D__TRANSMISSION__", - "-DHAVE_STRLCPY", - "-DHAVE_STRLCAT", -diff --git a/configure.ac b/configure.ac -index c1931c3..1fe4a14 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -177,6 +177,75 @@ AC_CHECK_HEADERS([sys/statvfs.h \ - - dnl ---------------------------------------------------------------------------- - dnl -+dnl SO_BINDTODEVICE support -+ -+AC_CACHE_CHECK([whether setsockopt accepts SO_BINDTODEVICE], -+ [ac_cv_c_so_bindtodevice], -+ [CXXFLAGS="${save_CXXFLAGS} -Wall -Werror" -+ AC_TRY_COMPILE([#include <sys/types.h> -+ #include <sys/socket.h>], -+ [return SO_BINDTODEVICE;], -+ ac_cv_c_so_bindtodevice=yes, ac_cv_c_so_bindtodevice=no)]) -+if test x"$ac_cv_c_so_bindtodevice" != x"no"; then -+ AC_DEFINE(HAVE_SO_BINDTODEVICE, 1, Define if setsockopt accepts SO_BINDTODEVICE.) -+fi -+ -+dnl ---------------------------------------------------------------------------- -+dnl -+dnl SO_BINDTODEVICE enable/disable. -+ -+AC_ARG_ENABLE([bindtodevice], -+ AS_HELP_STRING([--enable-bindtodevice],[Enable bind to device support]), -+ [want_bindtodevice=${enableval}], -+ [want_bindtodevice=${ac_cv_c_so_bindtodevice}]) -+ -+if test x"$want_bindtodevice" = x"yes" ; then -+ if test x"$ac_cv_c_so_bindtodevice" != x"no"; then -+ AC_DEFINE(USE_SO_BINDTODEVICE, 1, Define as 1 if bind to device feature is enabled.) -+ else -+ AC_DEFINE(USE_SO_BINDTODEVICE, 0, Define as 0 if bind to device feature is disabled.) -+ fi -+fi -+ -+dnl ---------------------------------------------------------------------------- -+dnl -+dnl getifaddrs -+ -+AC_CACHE_CHECK([whether getifaddrs() exists], -+ [ac_cv_c_getifaddrs], -+ [CXXFLAGS="${save_CXXFLAGS} -Wall -Werror" -+ AC_TRY_COMPILE([#include <sys/types.h> -+ #include <sys/socket.h> -+ #include <ifaddrs.h>], -+ [{struct ifaddrs * pIfa; -+ return getifaddrs(&pIfa);}], -+ ac_cv_c_getifaddrs=yes, ac_cv_c_getifaddrs=no)]) -+if test x"$ac_cv_c_getifaddrs" != x"no"; then -+ AC_DEFINE(HAVE_GETIFADDRS, 1, Define if getifaddrs is available..) -+fi -+ -+dnl ---------------------------------------------------------------------------- -+dnl -+dnl libnetlink -+ -+AC_CACHE_CHECK([whether libnetlink is available (iproute-dev)], -+ [ac_cv_c_libnetlink], -+ [CXXFLAGS="${save_CXXFLAGS} -Wall -Werror" -+ AC_TRY_COMPILE([#include <sys/types.h> -+ #include <sys/socket.h> -+ #include <stdio.h> -+ #include <libnetlink.h>], -+ [{struct rtnl_handle rth; -+ return rtnl_open(&rth, RTNLGRP_IPV4_ROUTE);}], -+ ac_cv_c_libnetlink=yes, ac_cv_c_libnetlink=no)]) -+if test x"$ac_cv_c_libnetlink" != x"no"; then -+ AC_DEFINE(HAVE_LIBNETLINK, 1, Define if libnetlink is available..) -+ dnl THIS IS A HACK: -+ LIBS="$LIBS -lnetlink" -+fi -+ -+dnl ---------------------------------------------------------------------------- -+dnl - dnl dht - - DHT_CFLAGS="-I\$(top_srcdir)/third-party/dht" -diff --git a/gtk/tr-prefs.c b/gtk/tr-prefs.c -index cc6ac6f..0caff40 100644 ---- a/gtk/tr-prefs.c -+++ b/gtk/tr-prefs.c -@@ -19,6 +19,9 @@ - #include <libtransmission/transmission.h> - #include <libtransmission/utils.h> - #include <libtransmission/version.h> -+#include <sys/time.h> /* getrlimit */ -+#include <sys/resource.h> /* getrlimit */ -+ - #include "conf.h" - #include "hig.h" - #include "tr-core.h" -@@ -1192,6 +1195,9 @@ networkPage( GObject * core ) - const char * s; - struct network_page_data * data; - guint row = 0; -+ struct rlimit limit; -+ -+ getrlimit( RLIMIT_NOFILE, &limit ); - - /* register to stop listening to core prefs changes when the page is destroyed */ - data = g_new0( struct network_page_data, 1 ); -@@ -1227,9 +1233,9 @@ networkPage( GObject * core ) - hig_workarea_add_section_divider( t, &row ); - hig_workarea_add_section_title( t, &row, _( "Peer Limits" ) ); - -- w = new_spin_button( TR_PREFS_KEY_PEER_LIMIT_TORRENT, core, 1, FD_SETSIZE, 5 ); -+ w = new_spin_button( TR_PREFS_KEY_PEER_LIMIT_TORRENT, core, 1, limit.rlim_max, 5 ); - hig_workarea_add_row( t, &row, _( "Maximum peers per _torrent:" ), w, NULL ); -- w = new_spin_button( TR_PREFS_KEY_PEER_LIMIT_GLOBAL, core, 1, FD_SETSIZE, 5 ); -+ w = new_spin_button( TR_PREFS_KEY_PEER_LIMIT_GLOBAL, core, 1, limit.rlim_max, 5 ); - hig_workarea_add_row( t, &row, _( "Maximum peers _overall:" ), w, NULL ); - - hig_workarea_add_section_divider( t, &row ); -diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am -index aaa7412..cb9a67e 100644 ---- a/libtransmission/Makefile.am -+++ b/libtransmission/Makefile.am -@@ -41,12 +41,14 @@ libtransmission_a_SOURCES = \ - metainfo.c \ - natpmp.c \ - net.c \ -+ net-interfaces.c \ - peer-io.c \ - peer-mgr.c \ - peer-msgs.c \ - platform.c \ - port-forwarding.c \ - ptrarray.c \ -+ resolver.c \ - resume.c \ - rpcimpl.c \ - rpc-server.c \ -@@ -93,6 +95,7 @@ noinst_HEADERS = \ - metainfo.h \ - natpmp_local.h \ - net.h \ -+ net-interfaces.h \ - peer-common.h \ - peer-io.h \ - peer-mgr.h \ -@@ -100,6 +103,7 @@ noinst_HEADERS = \ - platform.h \ - port-forwarding.h \ - ptrarray.h \ -+ resolver.h \ - resume.h \ - rpcimpl.h \ - rpc-server.h \ -@@ -129,6 +133,7 @@ TESTS = \ - json-test \ - magnet-test \ - metainfo-test \ -+ net-interfaces-test \ - peer-msgs-test \ - rpc-test \ - test-peer-id \ -@@ -184,6 +189,10 @@ peer_msgs_test_SOURCES = peer-msgs-test.c - peer_msgs_test_LDADD = ${apps_ldadd} - peer_msgs_test_LDFLAGS = ${apps_ldflags} - -+net_interfaces_test_SOURCES = net-interfaces-test.c -+net_interfaces_test_LDADD = ${apps_ldadd} -+net_interfaces_test_LDFLAGS = ${apps_ldflags} -+ - rpc_test_SOURCES = rpc-test.c - rpc_test_LDADD = ${apps_ldadd} - rpc_test_LDFLAGS = ${apps_ldflags} -diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c -index 58fd60e..2e92abb 100644 ---- a/libtransmission/fdlimit.c -+++ b/libtransmission/fdlimit.c -@@ -40,6 +40,8 @@ - #include <sys/resource.h> /* getrlimit */ - #include <fcntl.h> /* O_LARGEFILE posix_fadvise */ - #include <unistd.h> /* lseek(), write(), ftruncate(), pread(), pwrite(), etc */ -+#include <stdlib.h> -+#include <stdio.h> - - #include "transmission.h" - #include "fdlimit.h" -@@ -71,6 +73,204 @@ - #define O_SEQUENTIAL 0 - #endif - -+struct tr_fileset -+{ -+ struct tr_cached_file * begin; -+ const struct tr_cached_file * end; -+}; -+ -+struct tr_fdInfo -+{ -+ int peerCount; -+ struct tr_fileset fileset; -+}; -+ -+/* track activity on open file handles */ -+typedef struct OpenHandleTracker_t -+{ -+ int fd; -+ uint64_t inBytes; -+ uint64_t outBytes; -+ clock_t lastActivity; -+} TOpenHandleTracker; -+ -+static TOpenHandleTracker * g_OpenTracker = NULL; -+size_t g_OpenTrackerSize = 0; -+ -+bool tr_fdtrack_add(tr_session * session, int fd) -+{ -+ bool added = false; -+ size_t index; -+ -+ if (NULL == g_OpenTracker) -+ { -+ size_t bytes; -+ g_OpenTrackerSize = session->peerLimit; -+ -+ bytes = g_OpenTrackerSize * sizeof(TOpenHandleTracker); -+ -+ g_OpenTracker = malloc(bytes); -+ memset(g_OpenTracker, 0, bytes); -+ for(index = 0; index < g_OpenTrackerSize; index++) -+ { -+ g_OpenTracker[index].fd = -1; -+ } -+ } -+ -+ if (g_OpenTracker) -+ { -+ if (g_OpenTrackerSize < session->peerLimit) -+ { -+ size_t bytes = session->peerLimit * sizeof(TOpenHandleTracker); -+ TOpenHandleTracker * tmp = realloc(g_OpenTracker, bytes); -+ if (tmp) -+ { -+ /* realloc successful */ -+ for (index = g_OpenTrackerSize; index < session->peerLimit; index++) -+ { -+ tmp[index].fd = -1; -+ tmp[index].lastActivity = 0; -+ tmp[index].inBytes = 0; -+ tmp[index].outBytes = 0; -+ } -+ g_OpenTracker = tmp; -+ g_OpenTrackerSize = session->peerLimit; -+ } -+ } -+ } -+ -+ if (g_OpenTracker) -+ { -+ for(index = 0; index < g_OpenTrackerSize; index++) -+ { -+ if (-1 == g_OpenTracker[index].fd) -+ { -+ g_OpenTracker[index].fd = fd; -+ g_OpenTracker[index].lastActivity = clock(); -+ g_OpenTracker[index].inBytes = 0; -+ g_OpenTracker[index].outBytes = 0; -+ added = true; -+ break; -+ } -+ } -+ } -+ return added; -+} -+ -+bool tr_fdtrack_input(int fd, uint64_t inCount) -+{ -+ bool found = false; -+ size_t index; -+ -+ for (index = 0; index < g_OpenTrackerSize; index++) -+ { -+ if (fd == g_OpenTracker[index].fd) -+ { -+ g_OpenTracker[index].fd = fd; -+ g_OpenTracker[index].lastActivity = clock(); -+ g_OpenTracker[index].inBytes += inCount; -+ found = true; -+ break; -+ } -+ } -+ return found; -+} -+ -+bool tr_fdtrack_output(int fd, uint64_t outCount) -+{ -+ bool found = false; -+ size_t index; -+ -+ for (index = 0; index < g_OpenTrackerSize; index++) -+ { -+ if (fd == g_OpenTracker[index].fd) -+ { -+ g_OpenTracker[index].fd = fd; -+ g_OpenTracker[index].lastActivity = clock(); -+ g_OpenTracker[index].outBytes += outCount; -+ found = true; -+ break; -+ } -+ } -+ return found; -+} -+ -+bool tr_fdtrack_close(int fd) -+{ -+ bool found = false; -+ size_t index; -+ -+ for (index = 0; index < g_OpenTrackerSize; index++) -+ { -+ if (fd == g_OpenTracker[index].fd) -+ { -+ g_OpenTracker[index].fd = -1; -+ g_OpenTracker[index].lastActivity = 0; -+ g_OpenTracker[index].inBytes = 0; -+ g_OpenTracker[index].outBytes = 0; -+ found = true; -+ break; -+ } -+ } -+ return found; -+} -+ -+int tr_fdtrack_cleanup(tr_session * session, int secs) -+{ -+ bool cleanup = 0; -+ clock_t inClock = CLOCKS_PER_SEC * secs; -+ clock_t now = clock(); -+ -+ if (now > inClock) -+ { -+ clock_t noActivitySince = now - inClock; -+ size_t index; -+ struct tr_fdInfo * gFd = session->fdInfo; -+ size_t idxOldest = g_OpenTrackerSize; -+ clock_t clkOldest = now; -+ -+ for (index = 0; index < g_OpenTrackerSize; index++) -+ { -+ if (-1 != g_OpenTracker[index].fd) -+ { -+ if (g_OpenTracker[index].lastActivity < clkOldest) -+ { -+ clkOldest = g_OpenTracker[index].lastActivity; -+ idxOldest = index; -+ } -+ -+ if (g_OpenTracker[index].lastActivity <= noActivitySince) -+ { -+ close(g_OpenTracker[index].fd); -+ fprintf(stderr, "%d\tclose\tcleanup\n", g_OpenTracker[index].fd ); -+ -+ g_OpenTracker[index].fd = -1; -+ g_OpenTracker[index].lastActivity = 0; -+ g_OpenTracker[index].inBytes = 0; -+ g_OpenTracker[index].outBytes = 0; -+ cleanup++; -+ gFd->peerCount--; -+ } -+ } -+ } -+ -+ if ( 0 == cleanup && idxOldest < g_OpenTrackerSize ) -+ { -+ index = idxOldest; -+ close(g_OpenTracker[index].fd); -+ fprintf(stderr, "%d\tclose\tcleanup\n", g_OpenTracker[index].fd ); -+ -+ g_OpenTracker[index].fd = -1; -+ g_OpenTracker[index].lastActivity = 0; -+ g_OpenTracker[index].inBytes = 0; -+ g_OpenTracker[index].outBytes = 0; -+ cleanup++; -+ gFd->peerCount--; -+ } -+ } -+ return cleanup; -+} -+ - - static bool - preallocate_file_sparse( int fd, uint64_t length ) -@@ -413,12 +613,6 @@ cached_file_open( struct tr_cached_file * o, - **** - ***/ - --struct tr_fileset --{ -- struct tr_cached_file * begin; -- const struct tr_cached_file * end; --}; -- - static void - fileset_construct( struct tr_fileset * set, int n ) - { -@@ -506,12 +700,6 @@ fileset_get_empty_slot( struct tr_fileset * set ) - **** - ***/ - --struct tr_fdInfo --{ -- int peerCount; -- struct tr_fileset fileset; --}; -- - static void - ensureSessionFdInfoExists( tr_session * session ) - { -@@ -531,6 +719,7 @@ ensureSessionFdInfoExists( tr_session * session ) - /* set the open-file limit to the largest safe size wrt FD_SETSIZE */ - if( !getrlimit( RLIMIT_NOFILE, &limit ) ) - { -+#if 0 - const int old_limit = (int) limit.rlim_cur; - const int new_limit = MIN( limit.rlim_max, FD_SETSIZE ); - if( new_limit != old_limit ) -@@ -540,6 +729,9 @@ ensureSessionFdInfoExists( tr_session * session ) - getrlimit( RLIMIT_NOFILE, &limit ); - tr_inf( "Changed open file limit from %d to %d", old_limit, (int)limit.rlim_cur ); - } -+#else -+ limit.rlim_cur = limit.rlim_max; -+#endif - } - } - } -@@ -676,10 +868,28 @@ tr_fdSocketCreate( tr_session * session, int domain, int type ) - ensureSessionFdInfoExists( session ); - gFd = session->fdInfo; - -- if( gFd->peerCount < session->peerLimit ) -+ if(session->peerLimit < 100) -+ { -+ fprintf(stderr, "Peer Limit minimum of ~250. Est overhead is 100 file handles\n"); -+ } -+ -+ assert(session->peerLimit >= 100); -+ -+ if (gFd->peerCount >= (session->peerLimit - 100)) -+ { -+ fprintf(stderr, "peerCount %d < peerLimit %d - 100\n", gFd->peerCount, session->peerLimit ); -+ /* cleanup connects that have been idle for 2 minutes or more */ -+ tr_fdtrack_cleanup(session, 120); -+ } -+ -+ if( gFd->peerCount < (session->peerLimit - 100) ) -+ { - if(( s = socket( domain, type, 0 )) < 0 ) -+ { - if( sockerrno != EAFNOSUPPORT ) - tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) ); -+ } -+ } - - if( s > -1 ) - ++gFd->peerCount; -@@ -699,6 +909,8 @@ tr_fdSocketCreate( tr_session * session, int domain, int type ) - getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size ); - tr_dbg( "SO_RCVBUF size is %d", i ); - } -+ tr_netBindSocketInterface(session, s); -+ tr_fdtrack_add(session, s); - } - - return s; -@@ -724,10 +936,11 @@ tr_fdSocketAccept( tr_session * s, int sockfd, tr_address * addr, tr_port * port - - if( fd >= 0 ) - { -- if( ( gFd->peerCount < s->peerLimit ) -+ if( ( gFd->peerCount < (s->peerLimit - 100) ) - && tr_address_from_sockaddr_storage( addr, port, &sock ) ) - { - ++gFd->peerCount; -+ tr_fdtrack_add(s, fd); - } - else - { -diff --git a/libtransmission/fdlimit.h b/libtransmission/fdlimit.h -index 0b01190..15b8df5 100644 ---- a/libtransmission/fdlimit.h -+++ b/libtransmission/fdlimit.h -@@ -40,6 +40,11 @@ ssize_t tr_pread(int fd, void *buf, size_t count, off_t offset); - ssize_t tr_pwrite(int fd, const void *buf, size_t count, off_t offset); - int tr_prefetch(int fd, off_t offset, size_t count); - -+bool tr_fdtrack_add(tr_session * session, int fd); -+bool tr_fdtrack_input(int fd, uint64_t inCount); -+bool tr_fdtrack_output(int fd, uint64_t outCount); -+bool tr_fdtrack_close(int fd); -+int tr_fdtrack_cleanup(tr_session * session, int secs); - - /** - * Returns an fd to the specified filename. -diff --git a/libtransmission/net-interfaces-test.c b/libtransmission/net-interfaces-test.c -new file mode 100644 -index 0000000..e719e23 ---- /dev/null -+++ b/libtransmission/net-interfaces-test.c -@@ -0,0 +1,101 @@ -+#include <stdio.h> -+#include <string.h> -+ -+#include "net-interfaces.h" -+#include "utils.h" -+ -+#define VERBOSE 1 -+// #undef VERBOSE -+ -+#ifdef VERBOSE -+ #define check( A ) \ -+ { \ -+ ++test; \ -+ if( A ){ \ -+ fprintf( stderr, "PASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \ -+ } else { \ -+ fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \ -+ return test; \ -+ } \ -+ } -+#else -+ #define check( A ) \ -+ { \ -+ ++test; \ -+ if( !( A ) ){ \ -+ fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \ -+ return test; \ -+ } \ -+ } -+#endif -+ -+#define info( ... ) \ -+ do { \ -+ tr_msg( __FILE__, __LINE__, TR_MSG_INF, NULL, __VA_ARGS__ ); \ -+ } while( 0 ) -+ -+ -+static void tr_list_interfaces( tr_interface ** interfaces ); -+static void tr_list_interface( tr_interface * interface ); -+ -+static void tr_list_interface( tr_interface * interface ) -+{ -+ char buf[INET6_ADDRSTRLEN]; -+ -+ info("%s:",interface->name); -+ info(" name = %s",interface->name); -+ -+ if (interface->af4) -+ { -+ tr_address_to_string_with_buf(&interface->ipv4, buf, sizeof(buf)); -+ info(" ipv4 = %s", buf); -+ } -+ if (interface->af6) -+ { -+ tr_address_to_string_with_buf(&interface->ipv6, buf, sizeof(buf)); -+ info(" ipv6 = %s", buf); -+ } -+ info(" "); -+} -+ -+static void tr_list_interfaces( tr_interface ** interfaces ) -+{ -+ if (interfaces) -+ { -+ int index; -+ for( index = 0; interfaces[index]; index++ ) -+ { -+ tr_interface * interface = interfaces[index]; -+ tr_list_interface( interface ); -+ } -+ } -+ return; -+} -+ -+static int -+test1( void ) -+{ -+ tr_interface ** interfaces; -+ -+ info("Network interfaces test..."); -+ info(" "); -+ interfaces = tr_net_interfaces(); -+ tr_list_interfaces(interfaces); -+ tr_interfacesFree(interfaces); -+ info("Done."); -+ return 0; -+} -+ -+int -+main( void ) -+{ -+ int i; -+ -+ if( ( i = test1( ) ) ) -+ return i; -+ -+#ifdef VERBOSE -+ fprintf( stderr, "net-interfaces-test passed\n" ); -+#endif -+ return 0; -+} -diff --git a/libtransmission/net-interfaces.c b/libtransmission/net-interfaces.c -new file mode 100644 -index 0000000..82ccb56 ---- /dev/null -+++ b/libtransmission/net-interfaces.c -@@ -0,0 +1,183 @@ -+/****************************************************************************** -+ * -+ * $Id$ -+ * -+ * Copyright (c) 2005-2011 Transmission authors and contributors -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ *****************************************************************************/ -+ -+#include "net-interfaces.h" -+#include "utils.h" -+#include "list.h" -+ -+#include <stdlib.h> -+#include <sys/types.h> -+#include <sys/socket.h> -+#include <netinet/in.h> -+#include <arpa/inet.h> -+#include <net/if.h> -+#include <strings.h> -+ -+#if defined(HAVE_GETIFADDRS) -+ #include <ifaddrs.h> -+#endif -+ -+#include <errno.h> -+ -+#if defined(HAVE_GETIFADDRS) -+ static tr_interface ** tr_net_getinterfaces(void); -+#else -+ static tr_interface ** tr_net_dummy_interfaces(void); -+#endif -+ -+tr_interface * tr_FindInterfaceByName(tr_interface **interfaces, char * device) -+{ -+ tr_interface * found = NULL; -+ if (interfaces) -+ { -+ int entry; -+ for(entry = 0; interfaces[entry]; entry++) -+ { -+ tr_interface * test = interfaces[entry]; -+ if( 0 == strcasecmp( test->name, device ) ) -+ { -+ found = test; -+ break; -+ } -+ } -+ } -+ return found; -+} -+ -+void tr_interfacesFree( tr_interface ** interfaces ) -+{ -+ if (interfaces) -+ { -+ int entry; -+ for(entry = 0; interfaces[entry]; entry++) -+ { -+ tr_free(interfaces[entry]); -+ } -+ } -+ tr_free(interfaces); -+} -+ -+tr_interface ** tr_net_interfaces() -+{ -+#if defined(HAVE_GETIFADDRS) -+ return tr_net_getinterfaces(); -+#else -+ return tr_net_dummy_interfaces(); -+#endif -+} -+ -+#if defined(HAVE_GETIFADDRS) -+static void tr_MergeOrAppendToInterfaces(tr_interface **interfaces, struct ifaddrs * ifa); -+ -+static void tr_MergeOrAppendToInterfaces(tr_interface **interfaces, struct ifaddrs * ifa) -+{ -+ if (interfaces) -+ { -+ tr_interface * merge = tr_FindInterfaceByName(interfaces, ifa->ifa_name); -+ -+ if (merge == NULL) -+ { -+ int entry; -+ for(entry = 0; interfaces[entry]; entry++) -+ { -+ } -+ interfaces[entry] = tr_new0(tr_interface, 1); -+ merge = interfaces[entry]; -+ // Name. -+ tr_strlcpy(merge->name, ifa->ifa_name, sizeof(merge->name)); -+ } -+ -+ if (merge) -+ { -+ if (ifa->ifa_addr->sa_family == AF_INET) -+ { -+ struct sockaddr_in * s4 = (struct sockaddr_in *)(ifa->ifa_addr); -+ -+ merge->af4 = ifa->ifa_addr->sa_family; -+ merge->ipv4.type = TR_AF_INET; -+ merge->ipv4.addr.addr4 = s4->sin_addr; -+ } -+ else if (ifa->ifa_addr->sa_family == AF_INET6) -+ { -+ struct sockaddr_in6 * s6 = (struct sockaddr_in6 *)(ifa->ifa_addr); -+ -+ merge->af6 = ifa->ifa_addr->sa_family; -+ merge->ipv6.type = TR_AF_INET6; -+ merge->ipv6.addr.addr6 = s6->sin6_addr; -+ } -+ } -+ } -+} -+ -+static tr_interface ** tr_net_getinterfaces(void) -+{ -+ tr_interface ** interfaces = NULL; -+ int ifcount = 0; -+ -+ struct ifaddrs *myaddrs, *ifa; -+ int status; -+ -+ status = getifaddrs(&myaddrs); -+ if (status != 0) -+ { -+ tr_err( _( "getifaddrs error: \'%s\' (%d)" ), tr_strerror(errno), errno ); -+ } -+ -+ for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) -+ { -+ if ( (ifa->ifa_addr != NULL) // has address -+ && (ifa->ifa_flags & IFF_UP)) // iface is up. -+ { -+ ifcount++; -+ } -+ } -+ -+ if (ifcount > 0) -+ { -+ // treat as a null terminated array of interfaces -+ interfaces = tr_new0( tr_interface *, ifcount + 1 ); -+ for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) -+ { -+ if ( (ifa->ifa_addr != NULL) // has address -+ && (ifa->ifa_flags & IFF_UP)) // iface is up. -+ { -+ tr_MergeOrAppendToInterfaces(interfaces, ifa); -+ } -+ } -+ } -+ freeifaddrs(myaddrs); -+ -+ return interfaces; -+} -+ -+#else // HAVE_GETIFADDRS -+ -+tr_interface ** tr_net_dummy_interfaces(void) -+{ -+ // Is there a port of getifaddrs for win32? -+ return NULL; -+} -+ -+#endif -diff --git a/libtransmission/net-interfaces.h b/libtransmission/net-interfaces.h -new file mode 100644 -index 0000000..9fdfa9c ---- /dev/null -+++ b/libtransmission/net-interfaces.h -@@ -0,0 +1,49 @@ -+/****************************************************************************** -+ * $Id$ -+ * -+ * Copyright (c) 2005-2008 Transmission authors and contributors -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ *****************************************************************************/ -+ -+#ifndef __TRANSMISSION__ -+#error only libtransmission should #include this header. -+#endif -+#include "net.h" -+ -+#ifndef _TR_NET_INTERFACES_H_ -+#define _TR_NET_INTERFACES_H_ -+ -+#include <net/if.h> -+ -+typedef struct tr_interface -+{ -+ char name[IF_NAMESIZE]; -+ unsigned short int af4; -+ unsigned short int af6; -+ tr_address ipv4; -+ tr_address ipv6; -+} tr_interface; -+ -+tr_interface ** tr_net_interfaces( void ); -+void tr_interfacesFree( tr_interface ** interfaces ); -+tr_interface * tr_FindInterfaceByName(tr_interface **interfaces, char * device); -+ -+ -+#endif // _TR_NET_INTERFACES_H_ -diff --git a/libtransmission/net.c b/libtransmission/net.c -index eb05de6..18ba7c5 100644 ---- a/libtransmission/net.c -+++ b/libtransmission/net.c -@@ -34,6 +34,9 @@ - #include <ws2tcpip.h> - #else - #include <netinet/tcp.h> /* TCP_CONGESTION */ -+ #include <sys/ioctl.h> -+ #include <net/if.h> -+ #include <unistd.h> - #endif - - #include <event2/util.h> -@@ -55,6 +58,43 @@ - const tr_address tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } }; - const tr_address tr_inaddr_any = { TR_AF_INET, { { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } }; - -+const char * -+tr_netGetAddress( const char * node, const char * service, tr_address * addr ) -+{ -+ struct addrinfo hints, * res, * p; -+ struct sockaddr_storage * ss; -+ int rv, family = AF_UNSPEC; -+ const char * err = NULL; -+ -+ if( !addr ) -+ return _( "Invalid address argument" ); -+ -+ memset( &hints, 0, sizeof( hints ) ); -+ if( addr->type == TR_AF_INET ) -+ family = AF_INET; -+ else if( addr->type == TR_AF_INET6 ) -+ family = AF_INET6; -+ hints.ai_family = family; -+ -+ if( ( rv = getaddrinfo( node, service, &hints, &res ) ) != 0 ) -+ return gai_strerror( rv ); -+ -+ for( p = res; p; p = p->ai_next ) -+ { -+ if( family != AF_UNSPEC && p->ai_family != family ) -+ continue; -+ ss = (struct sockaddr_storage *) p->ai_addr; -+ // assert(p->ai_addrlen == sizeof( struct sockaddr_in )); -+ tr_address_from_sockaddr_storage( addr, NULL, ss ); -+ break; -+ } -+ -+ if( p == NULL ) -+ err = _( "No matching addresses found" ); -+ freeaddrinfo( res ); -+ return err; -+} -+ - void - tr_netInit( void ) - { -@@ -166,6 +206,46 @@ tr_netSetCongestionControl( int s UNUSED, const char *algorithm UNUSED ) - #endif - } - -+void -+tr_netBindSocketInterface(tr_session *session, int socket) -+{ -+#ifdef USE_SO_BINDTODEVICE -+ if ( socket >= 0 && session->publicInterface != NULL ) -+ { -+ /* -+ * Using the ifreq struct with setsockopt seems reasonably common -+ * among the POSIX and POSIX like platforms. -+ * The linux manpage here: http://linux.die.net/man/7/socket says: -+ * ""The passed option is a variable-length null terminated -+ * interface name string with the maximum size of IFNAMSIZ."" -+ * -+ * The ifreq structure contains, as it's first element, ifr_name -+ * of size IFNAMSIZ. -+ * -+ * If you find that you do not have net/if.h or the ifreq structure -+ * but you do have SO_BINDTODEVICE then you may just pass null -+ * terminated string. IFNAMSIZ is 16, quite long as net devices -+ * tend to be named eth0, eth0:1, ppp0, etc. -+ * -+ * For size you can pass either IFNAMSIZ, sizeof(struct ifreq), or the -+ * number of bytes in session->publicInterface plus the '\0'. -+ */ -+ struct ifreq request; -+ -+ memset(&request, 0, sizeof(request)); -+ tr_strlcpy(request.ifr_name, session->publicInterface, IFNAMSIZ); -+ if ( setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, -+ &request, IFNAMSIZ) < 0 ) -+ { -+ int eno = sockerrno; -+ tr_err( _( "Bind socket to device \'%s\' error: \'%s\' (%d)" ), -+ session->publicInterface, tr_strerror(eno), eno ); -+ } -+ } -+#endif -+} -+ -+ - bool - tr_address_from_sockaddr_storage( tr_address * setme_addr, - tr_port * setme_port, -@@ -245,6 +325,10 @@ tr_netOpenPeerSocket( tr_session * session, - if( s < 0 ) - return -1; - -+ -+ -+ tr_netBindSocketInterface(session, s); -+ - /* seeds don't need much of a read buffer... */ - if( clientIsSeed ) { - int n = 8192; -@@ -309,7 +393,7 @@ tr_netOpenPeerUTPSocket( tr_session * session, - } - - static int --tr_netBindTCPImpl( const tr_address * addr, tr_port port, bool suppressMsgs, int * errOut ) -+tr_netBindTCPImpl( tr_session * session, const tr_address * addr, tr_port port, bool suppressMsgs, int * errOut ) - { - static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 }; - struct sockaddr_storage sock; -@@ -334,6 +418,7 @@ tr_netBindTCPImpl( const tr_address * addr, tr_port port, bool suppressMsgs, int - optval = 1; - setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) ); - setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) ); -+ tr_netBindSocketInterface(session, fd); - - #ifdef IPV6_V6ONLY - if( addr->type == TR_AF_INET6 ) -@@ -382,14 +467,14 @@ tr_netBindTCPImpl( const tr_address * addr, tr_port port, bool suppressMsgs, int - } - - int --tr_netBindTCP( const tr_address * addr, tr_port port, bool suppressMsgs ) -+tr_netBindTCP( tr_session * session, const tr_address * addr, tr_port port, bool suppressMsgs ) - { - int unused; -- return tr_netBindTCPImpl( addr, port, suppressMsgs, &unused ); -+ return tr_netBindTCPImpl( session, addr, port, suppressMsgs, &unused ); - } - - bool --tr_net_hasIPv6( tr_port port ) -+tr_net_hasIPv6( tr_session * session, tr_port port ) - { - static bool result = false; - static bool alreadyDone = false; -@@ -397,7 +482,7 @@ tr_net_hasIPv6( tr_port port ) - if( !alreadyDone ) - { - int err; -- int fd = tr_netBindTCPImpl( &tr_in6addr_any, port, true, &err ); -+ int fd = tr_netBindTCPImpl( session, &tr_in6addr_any, port, true, &err ); - if( fd >= 0 || err != EAFNOSUPPORT ) /* we support ipv6 */ - result = true; - if( fd >= 0 ) -@@ -428,6 +513,7 @@ void - tr_netCloseSocket( int fd ) - { - evutil_closesocket( fd ); -+ tr_fdtrack_close(fd); - } - - void -@@ -643,9 +729,78 @@ isMartianAddr( const struct tr_address * a ) - bool - tr_address_is_valid_for_peers( const tr_address * addr, tr_port port ) - { -- return ( port != 0 ) -- && ( tr_address_is_valid( addr ) ) -- && ( !isIPv6LinkLocalAddress( addr ) ) -- && ( !isIPv4MappedAddress( addr ) ) -- && ( !isMartianAddr( addr ) ); -+ return ( port != 0 ) && ( tr_isValidTrackerAddress( addr ) ); -+} -+ -+bool -+tr_isValidTrackerAddress( const tr_address * addr ) -+{ -+ return tr_isAddress( addr ) -+ && !isIPv6LinkLocalAddress( addr ) -+ && !isIPv4MappedAddress( addr ) -+ && !isMartianAddr( addr ); -+} -+ -+bool isAvailableBindAddress(tr_address * address, enum tr_address_type addrType) -+{ -+ bool rval = false; -+ int s; -+ int bindResult = 0; -+ static socklen_t sourcelen; -+ struct sockaddr_storage source_sock; -+ sourcelen = setup_sockaddr( address, 0, &source_sock ); -+ -+ s = socket( (int)addrType, SOCK_DGRAM, 0 ); -+ if (s >= 0) -+ { -+ bindResult = bind( s, (struct sockaddr*)&source_sock, sourcelen ); -+ if(0 == bindResult) -+ { -+ rval = true; -+ } -+ else if (EADDRNOTAVAIL == errno) -+ { -+ rval = false; -+ } -+ else -+ { -+ int bindErr = errno; -+ -+ tr_nerr("isAvailableBindAddress", "bind() probe gave an unhandled error code %i", bindErr); -+ tr_nerr("isAvailableBindAddress", "assuming that the address (may otherwise at other times) be bind()'able"); -+ rval = true; -+ } -+ close(s); -+ } -+ return rval; - } -+ -+/* -+ * Attempt to create a dummy private address that will disable traffic. -+ */ -+tr_address * unavailableBindAddress(enum tr_address_type addrType) -+{ -+ int i; -+ tr_address * testAddr = tr_new0( tr_address, 1 ); -+ -+ switch (addrType) -+ { -+ case TR_AF_INET: tr_address_from_string( testAddr, "1.2.3.4" ); break; -+ case TR_AF_INET6: tr_address_from_string( testAddr, "fd7f:54eb:9f51:be9a:1:2:3:4" ); break; -+ default: return testAddr; -+ } -+ -+ i = 100; -+ while( isAvailableBindAddress(testAddr, addrType) && i < 100 ) -+ { -+ switch (addrType) -+ { -+ case TR_AF_INET: testAddr->addr.addr4.s_addr += 1; break; -+ case TR_AF_INET6: testAddr->addr.addr6.s6_addr[0] -= 1; break; -+ default: return testAddr; -+ } -+ i++; -+ } -+ return testAddr; -+} -+ -diff --git a/libtransmission/net.h b/libtransmission/net.h -index 09e1095..faa77c1 100644 ---- a/libtransmission/net.h -+++ b/libtransmission/net.h -@@ -25,6 +25,7 @@ - #ifndef __TRANSMISSION__ - #error only libtransmission should #include this header. - #endif -+#include "transmission.h" - - #ifndef _TR_NET_H_ - #define _TR_NET_H_ -@@ -81,6 +82,8 @@ typedef struct tr_address - extern const tr_address tr_inaddr_any; - extern const tr_address tr_in6addr_any; - -+const char* tr_netGetAddress( const char * node, const char * service, tr_address * addr ); -+ - const char* tr_address_to_string( const tr_address * addr ); - - const char* tr_address_to_string_with_buf( const tr_address * addr, -@@ -89,6 +92,12 @@ const char* tr_address_to_string_with_buf( const tr_address * addr, - - bool tr_address_from_string ( tr_address * setme, - const char * string ); -+bool tr_isValidTrackerAddress( const tr_address * addr ); -+ -+bool isAvailableBindAddress(tr_address * address, enum tr_address_type addrType); -+tr_address * unavailableBindAddress(enum tr_address_type addrType); -+ -+static inline bool tr_isAddress( const tr_address * a ) { return ( a != NULL ) && ( a->type==TR_AF_INET || a->type==TR_AF_INET6 ); } - - bool tr_address_from_sockaddr_storage( tr_address * setme, - tr_port * port, -@@ -123,7 +132,8 @@ tr_netOpenPeerUTPSocket( tr_session * session, - tr_port port, - bool clientIsSeed); - --int tr_netBindTCP( const tr_address * addr, -+int tr_netBindTCP( tr_session * session, -+ const tr_address * addr, - tr_port port, - bool suppressMsgs ); - -@@ -143,7 +153,9 @@ void tr_netCloseSocket( int fd ); - - void tr_netInit( void ); - --bool tr_net_hasIPv6( tr_port ); -+bool tr_net_hasIPv6( tr_session * session, tr_port port ); -+ -+void tr_netBindSocketInterface(tr_session *session, int socket); - - - /** -diff --git a/libtransmission/peer-io.c b/libtransmission/peer-io.c -index 817a6d3..a128ab9 100644 ---- a/libtransmission/peer-io.c -+++ b/libtransmission/peer-io.c -@@ -27,6 +27,7 @@ - #include "net.h" - #include "peer-common.h" /* MAX_BLOCK_SIZE */ - #include "peer-io.h" -+#include "fdlimit.h" - #include "trevent.h" /* tr_runInEventThread() */ - #include "tr-utp.h" - #include "utils.h" -@@ -160,6 +161,8 @@ didWriteWrapper( tr_peerIo * io, unsigned int bytes_transferred ) - - tr_bandwidthUsed( &io->bandwidth, TR_UP, payload, next->isPieceData, now ); - -+ tr_fdtrack_output(io->socket, payload); -+ - if( overhead > 0 ) - tr_bandwidthUsed( &io->bandwidth, TR_UP, overhead, false, now ); - -@@ -207,10 +210,16 @@ canReadWrapper( tr_peerIo * io ) - if( piece || (piece!=used) ) - { - if( piece ) -+ { - tr_bandwidthUsed( &io->bandwidth, TR_DOWN, piece, true, now ); -+ tr_fdtrack_input(io->socket, piece); -+ } - - if( used != piece ) -+ { - tr_bandwidthUsed( &io->bandwidth, TR_DOWN, used - piece, false, now ); -+ tr_fdtrack_input(io->socket, used - piece); -+ } - } - - if( overhead > 0 ) -@@ -405,6 +414,8 @@ maybeSetCongestionAlgorithm( int socket, const char * algorithm ) - #ifdef WITH_UTP - /* UTP callbacks */ - -+uint64 UTP_GetGlobalUTPBytesSent(const struct sockaddr *remote, socklen_t remotelen); -+ - static void - utp_on_read(void *closure, const unsigned char *buf, size_t buflen) - { -@@ -424,6 +435,8 @@ utp_on_read(void *closure, const unsigned char *buf, size_t buflen) - canReadWrapper( io ); - } - -+uint64 g_global_write_count = 0; -+ - static void - utp_on_write(void *closure, unsigned char *buf, size_t buflen) - { -@@ -439,6 +452,15 @@ utp_on_write(void *closure, unsigned char *buf, size_t buflen) - } - - didWriteWrapper( io, buflen ); -+ -+ g_global_write_count += buflen; -+} -+ -+uint64 UTP_GetGlobalUTPBytesSent(const struct sockaddr *remote, socklen_t remotelen) -+{ -+ (void) remote; -+ (void) remotelen; -+ return g_global_write_count; - } - - static size_t -@@ -506,7 +528,7 @@ utp_on_error(void *closure, int errcode) - } - - static void --utp_on_overhead(void *closure, bool send, size_t count, int type UNUSED) -+utp_on_overhead(void *closure, uint8_t send, size_t count, int type UNUSED) - { - tr_peerIo *io = closure; - assert( tr_isPeerIo( io ) ); -@@ -567,7 +589,7 @@ dummy_on_error( void * closure UNUSED, int errcode UNUSED ) - } - - static void --dummy_on_overhead( void *closure UNUSED, bool send UNUSED, size_t count UNUSED, int type UNUSED ) -+dummy_on_overhead( void *closure UNUSED, uint8_t send UNUSED, size_t count UNUSED, int type UNUSED ) - { - return; - } -@@ -635,10 +657,14 @@ tr_peerIoNew( tr_session * session, - io->socket, EV_READ, event_read_cb, io ); - io->event_write = event_new( session->event_base, - io->socket, EV_WRITE, event_write_cb, io ); -+ -+ tr_netBindSocketInterface(session, io->socket); -+ - } - #ifdef WITH_UTP - else { - UTP_SetSockopt( utp_socket, SO_RCVBUF, UTP_READ_BUFFER_SIZE ); -+ - dbgmsg( io, "%s", "calling UTP_SetCallbacks &utp_function_table" ); - UTP_SetCallbacks( utp_socket, - &utp_function_table, -diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c -index ce8b0a5..a2263b9 100644 ---- a/libtransmission/peer-mgr.c -+++ b/libtransmission/peer-mgr.c -@@ -141,7 +141,7 @@ struct peer_atom - }; - - #ifdef NDEBUG --#define tr_isAtom(a) (TRUE) -+#define tr_isAtom(a) (true) - #else - static bool - tr_isAtom( const struct peer_atom * atom ) -diff --git a/libtransmission/resolver.c b/libtransmission/resolver.c -new file mode 100644 -index 0000000..e3ec6f9 ---- /dev/null -+++ b/libtransmission/resolver.c -@@ -0,0 +1,157 @@ -+/****************************************************************************** -+ * -+ * $Id$ -+ * -+ * Copyright (c) Transmission authors and contributors -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ *****************************************************************************/ -+ -+#include <assert.h> -+ -+#include "transmission.h" -+#include "list.h" -+#include "net.h" -+#include "platform.h" -+#include "session.h" -+#include "utils.h" -+#include "trevent.h" -+#include "resolver.h" -+ -+/* If the number of tasks waiting in the queue divided -+ * by the current number of workers is greater than this -+ * number, a new worker thread is created. */ -+#define WORKER_LOAD 5 -+ -+/* Never create more worker threads than this. */ -+#define WORKER_MAX 10 -+ -+typedef struct -+{ -+ tr_session * session; -+ char * node; -+ char * service; -+ int type; -+ tr_resolver_callback callback; -+ void * user_data; -+} -+resolver_task; -+ -+typedef struct -+{ -+ const char * err; -+ tr_address addr; -+ tr_resolver_callback callback; -+ void * user_data; -+} -+resolver_result; -+ -+static tr_list * queue; /* resolver_task */ -+static tr_lock * lock; -+static int workers, tasks; -+ -+static void spawn_workers(void); -+ -+static void -+notify( void * vres ) -+{ -+ resolver_result * res = vres; -+ res->callback( res->err, &res->addr, res->user_data ); -+ tr_free( res ); -+} -+ -+static void -+worker( void * varg UNUSED ) -+{ -+ while( 1 ) -+ { -+ resolver_task * task; -+ resolver_result * res; -+ -+ tr_lockLock( lock ); -+ if( !queue ) -+ { -+ tr_lockUnlock( lock ); -+ break; -+ } -+ task = tr_list_pop_front( &queue ); -+ tasks--; -+ tr_lockUnlock( lock ); -+ -+ res = tr_new0( resolver_result, 1 ); -+ res->addr.type = task->type; -+ res->err = tr_netGetAddress( task->node, task->service, &res->addr ); -+ res->callback = task->callback; -+ res->user_data = task->user_data; -+ -+ tr_runInEventThread( task->session, notify, res ); -+ tr_free( task->node ); -+ tr_free( task->service ); -+ tr_free( task ); -+ } -+ -+ tr_lockLock( lock ); -+ workers--; -+ tr_lockUnlock( lock ); -+} -+ -+static void spawn_workers(void) -+{ -+ tr_lockLock( lock ); -+ if( queue && ( workers < 1 || tasks / workers > WORKER_LOAD ) -+ && workers < WORKER_MAX ) -+ { -+ workers++; -+ tr_threadNew( worker, NULL ); -+ } -+ tr_lockUnlock( lock ); -+} -+ -+void -+tr_resolve_address( tr_session * session, -+ const char * node, -+ const char * service, -+ int type, -+ tr_resolver_callback callback, -+ void * user_data ) -+{ -+ resolver_task * task; -+ -+ assert( callback != NULL ); -+ -+ task = tr_new0( resolver_task, 1 ); -+ task->session = session; -+ task->node = tr_strdup( node ); -+ task->service = tr_strdup( service ); -+ task->type = type; -+ task->callback = callback; -+ task->user_data = user_data; -+ -+ if( !lock ) -+ { -+ assert( tr_amInEventThread( session ) ); -+ lock = tr_lockNew( ); -+ } -+ tr_lockLock( lock ); -+ tr_list_append( &queue, task ); -+ tasks++; -+ tr_lockUnlock( lock ); -+ -+ spawn_workers( ); -+} -diff --git a/libtransmission/resolver.h b/libtransmission/resolver.h -new file mode 100644 -index 0000000..ce44813 ---- /dev/null -+++ b/libtransmission/resolver.h -@@ -0,0 +1,71 @@ -+/****************************************************************************** -+ * $Id$ -+ * -+ * Copyright (c) Transmission authors and contributors -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ *****************************************************************************/ -+ -+#ifndef __TRANSMISSION__ -+#error only libtransmission should #include this header. -+#endif -+ -+#ifndef _TR_RESOLVER_H_ -+#define _TR_RESOLVER_H_ -+ -+/** -+ * If the address resolution fails, @a err will be a string -+ * description of the error. Otherwise, @a err will be NULL -+ * and @a addr will contain the resolved address. -+ */ -+typedef void ( * tr_resolver_callback )( const char * err, -+ const tr_address * addr, -+ void * user_data ); -+ -+/** -+ * Resolve a hostname asynchronously by calling getaddrinfo(3) in -+ * another thread. If you do not care about blocking the current -+ * thread, you can just use tr_netGetAddress(). -+ * -+ * @param session @a callback will be run in the event thread of this -+ * session. -+ * @param node The name of the node to resolve. This will generally -+ * just be the hostname. -+ * @param service Same meaning as for getaddrinfo(3). Usually you -+ * can just set this to NULL. -+ * @param type The address type to prefer, either @a TR_AF_INET or -+ * @a TR_AF_INET6. Any other value will cause no particular -+ * type to be preferred and so the first valid address found -+ * will be passed to @a callback. -+ * @param callback Function to call with the result (or an error message). -+ * It will be run in the event thread of @a session by -+ * tr_runInEventThread(). -+ * @param user_data User data to pass to @a callback. -+ * -+ * @see tr_netGetAddress() -+ * @see tr_runInEventThread() -+ */ -+void tr_resolve_address( tr_session * session, -+ const char * node, -+ const char * service, -+ int type, -+ tr_resolver_callback callback, -+ void * user_data ); -+ -+#endif /* _TR_RESOLVER_H_ */ -diff --git a/libtransmission/resume.c b/libtransmission/resume.c -index a879515..341279e 100644 ---- a/libtransmission/resume.c -+++ b/libtransmission/resume.c -@@ -660,7 +660,11 @@ tr_torrentSaveResume( tr_torrent * tor ) - - filename = getResumeFilename( tor ); - if(( err = tr_bencToFile( &top, TR_FMT_BENC, filename ))) -+ { -+ bool was = tor->isStopping; - tr_torrentSetLocalError( tor, "Unable to save resume file: %s", tr_strerror( err ) ); -+ tor->isStopping = was; -+ } - tr_free( filename ); - - tr_bencFree( &top ); -diff --git a/libtransmission/session.c b/libtransmission/session.c -index c135032..f1dfdcd 100644 ---- a/libtransmission/session.c -+++ b/libtransmission/session.c -@@ -37,6 +37,7 @@ - #include "fdlimit.h" - #include "list.h" - #include "net.h" -+#include "net-interfaces.h" - #include "peer-io.h" - #include "peer-mgr.h" - #include "platform.h" /* tr_lock, tr_getTorrentDir(), tr_getFreeSpace() */ -@@ -55,6 +56,17 @@ - #include "version.h" - #include "web.h" - -+#ifdef HAVE_LIBNETLINK -+#include <stdio.h> -+#include <stdlib.h> -+#include <sys/socket.h> -+#include <libnetlink.h> -+ -+#define RTNLGRP_MSGS \ -+ (RTNLGRP_IPV4_IFADDR|RTNLGRP_IPV4_ROUTE|RTNLGRP_IPV6_IFADDR|RTNLGRP_IPV6_ROUTE) -+ -+#endif /* HAVE_LIBNETLINK */ -+ - enum - { - #ifdef TR_LIGHTWEIGHT -@@ -64,7 +76,12 @@ enum - DEFAULT_CACHE_SIZE_MB = 4, - DEFAULT_PREFETCH_ENABLED = true, - #endif -- SAVE_INTERVAL_SECS = 360 -+ SAVE_INTERVAL_SECS = 360, -+#ifdef HAVE_LIBNETLINK -+ NET_IF_POLL_INTERVAL_SECS = 30, -+#else -+ NET_IF_POLL_INTERVAL_SECS = 3, -+#endif /* HAVE_LIBNETLINK */ - }; - - -@@ -197,17 +214,19 @@ open_incoming_peer_port( tr_session * session ) - - /* bind an ipv4 port to listen for incoming peers... */ - b = session->public_ipv4; -- b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, false ); -+ b->socket = tr_netBindTCP( session, &b->addr, session->private_peer_port, false ); - if( b->socket >= 0 ) { -+ tr_netBindSocketInterface(session, b->socket); - b->ev = event_new( session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session ); - event_add( b->ev, NULL ); - } - - /* and do the exact same thing for ipv6, if it's supported... */ -- if( tr_net_hasIPv6( session->private_peer_port ) ) { -+ if( tr_net_hasIPv6( session, session->private_peer_port ) ) { - b = session->public_ipv6; -- b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, false ); -+ b->socket = tr_netBindTCP( session, &b->addr, session->private_peer_port, false ); - if( b->socket >= 0 ) { -+ tr_netBindSocketInterface(session, b->socket); - b->ev = event_new( session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session ); - event_add( b->ev, NULL ); - } -@@ -366,6 +385,7 @@ tr_sessionGetDefaultSettings( tr_benc * d ) - tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 ); - tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, TR_DEFAULT_BIND_ADDRESS_IPV4 ); - tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, TR_DEFAULT_BIND_ADDRESS_IPV6 ); -+ tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_INTERFACE, "" ); - tr_bencDictAddBool( d, TR_PREFS_KEY_START, true ); - tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, false ); - } -@@ -437,6 +457,7 @@ tr_sessionGetSettings( tr_session * s, struct tr_benc * d ) - tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent ); - tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, tr_address_to_string( &s->public_ipv4->addr ) ); - tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, tr_address_to_string( &s->public_ipv6->addr ) ); -+ tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_INTERFACE, s->publicInterface ); - tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) ); - tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) ); - } -@@ -553,6 +574,7 @@ onSaveTimer( int foo UNUSED, short bar UNUSED, void * vsession ) - ***/ - - static void tr_sessionInitImpl( void * ); -+static void peerPortChanged( void * session ); - - struct init_data - { -@@ -660,6 +682,203 @@ onNowTimer( int foo UNUSED, short bar UNUSED, void * vsession ) - /* fprintf( stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time(), (size_t)tv.tv_usec ); */ - } - -+static void tr_getNetworkInterfaces( tr_session * session ) -+{ -+ dbgmsg( "tr_getNetworkInterfaces: Refreshing the list of network interfaces..."); -+ tr_interfacesFree( session->networkInterfaces ); -+ session->networkInterfaces = tr_net_interfaces(); -+ dbgmsg( "tr_getNetworkInterfaces: Refreshed."); -+} -+ -+static tr_interface * tr_sessionGetInterfaceNamed(char * device, tr_session * session ) -+{ -+ return tr_FindInterfaceByName(session->networkInterfaces, device); -+} -+ -+/** -+ * If public interface name is set, refresh the bind ip addresses -+ * ie the session attributes public_ipv4 and public_ipv6 -+ * NOTE: here we don't remember a previous state of the -+ * public_interface string. So clearing this setting may not register -+ * until client restarts. We would have to add a hook funcion for GUI. -+ * At the moment this is hidden setting only, so we dont bother. -+ */ -+static void tr_refreshPublicIp( tr_session * session ) -+{ -+ tr_address old_public_ipv4_addr = session->public_ipv4->addr; -+ tr_address old_public_ipv6_addr = session->public_ipv6->addr; -+ -+ /* If user wants to bind to a specific device -+ * (ppp0, my PPTP VPN for instance). -+ */ -+ if( session->publicInterface && strlen(session->publicInterface) > 0 ) -+ { -+ tr_address * addr_ipv4 = NULL; -+ tr_address * addr_ipv6 = NULL; -+ -+ tr_interface * foundInterface = -+ tr_sessionGetInterfaceNamed(session->publicInterface, session); -+ -+ if (foundInterface) -+ { -+ if (foundInterface->af4) /* != AF_UNSPEC */ -+ { -+ tr_address ipv4null; -+ tr_address_from_string(&ipv4null, TR_DEFAULT_BIND_ADDRESS_IPV4); -+ -+ /* Check that we don't accidentally bind to all -+ * interfaces (0.0.0.0). -+ */ -+ if (0 != tr_address_compare(&ipv4null, &(foundInterface->ipv4))) -+ { -+ addr_ipv4 = &(foundInterface->ipv4); -+ } -+ } -+ if (foundInterface->af6) /* != AF_UNSPEC */ -+ { -+ tr_address ipv6null; -+ tr_address_from_string(&ipv6null, TR_DEFAULT_BIND_ADDRESS_IPV6); -+ -+ /* Check that we don't accidentally bind to all -+ * interfaces (::). -+ */ -+ if (0 != tr_address_compare(&ipv6null, &(foundInterface->ipv6))) -+ { -+ addr_ipv6 = &(foundInterface->ipv6); -+ } -+ } -+ } -+ -+ if (!addr_ipv4) -+ { -+ addr_ipv4 = unavailableBindAddress(TR_AF_INET); -+ } -+ -+ if (!addr_ipv6) -+ { -+ addr_ipv6 = unavailableBindAddress(TR_AF_INET6); -+ } -+ -+ /* if either v4 or v6 bind address has changed */ -+ if(tr_address_compare( addr_ipv4, &old_public_ipv4_addr ) || -+ tr_address_compare( addr_ipv6, &old_public_ipv6_addr )) -+ { -+ session->public_ipv4->addr = * addr_ipv4; -+ session->public_ipv6->addr = * addr_ipv6; -+ -+ /* restart future connections to bind on the new ip address */ -+ if( session->isLPDEnabled ) -+ tr_lpdUninit( session ); -+ -+ if( session->isDHTEnabled ) -+ { -+ tr_dhtUninit( session ); -+ tr_dhtInit( session ); -+ } -+ if( session->isLPDEnabled ) -+ tr_lpdInit( session, &session->public_ipv4->addr ); -+ -+ peerPortChanged( session ); -+ } -+ } -+} -+ -+ -+static void networkIFRefresh(tr_session * session) -+{ -+ assert( tr_isSession( session ) ); -+ tr_getNetworkInterfaces( session ); -+ tr_refreshPublicIp( session ); -+} -+ -+ -+/** -+ * Periodically reload the list of network interfaces -+ */ -+static void onNetworkIFTimer( int foo UNUSED, short bar UNUSED, void * vsession ) -+{ -+ tr_session * session = vsession; -+ -+ assert( tr_isSession( session ) ); -+ assert( session->networkInterfacesTimer != NULL ); -+ -+ dbgmsg( -+ "onNetworkIFTimer: the timer has timed out. Next timeout in %d secs.", -+ NET_IF_POLL_INTERVAL_SECS ); -+ -+ networkIFRefresh(session); -+ tr_timerAdd( session->networkInterfacesTimer, NET_IF_POLL_INTERVAL_SECS, 0 ); -+} -+ -+/** -+ * libnetlink support -+ */ -+ -+#ifdef HAVE_LIBNETLINK -+ -+/* -+ * Change this value to -1 to cause rtnl_listen to exit. -+ * [As per code inspection of iproute2 2.6.37] -+ */ -+static int g_listen = 0; -+ -+/* -+ * libnetlink will listen for kernel events and notify of the -+ * types we have registered for. -+ */ -+static int -+netlinkMessageCallback(const struct sockaddr_nl *who UNUSED, struct nlmsghdr *n, void *vsession) -+{ -+ switch(n->nlmsg_type) -+ { -+ case RTM_NEWLINK: -+ case RTM_DELLINK: -+ case RTM_GETLINK: -+ case RTM_SETLINK: -+ case RTM_NEWADDR: -+ case RTM_DELADDR: -+ case RTM_GETADDR: -+ case RTM_NEWROUTE: -+ case RTM_DELROUTE: -+ case RTM_GETROUTE: -+ case RTM_NEWRULE: -+ case RTM_DELRULE: -+ case RTM_GETRULE: -+ networkIFRefresh((tr_session *)vsession); -+ break; -+ -+ default: -+ break; -+ } -+ return g_listen; -+} -+ -+/* -+ * libnetlink rtnl_listen() blocks forever. -+ * Run it on a separate thread. -+ */ -+static void -+netlinkListenThreadFunc( void * vsession ) -+{ -+ struct rtnl_handle rth; -+ unsigned int groups = RTNLGRP_MSGS; -+ -+ if (rtnl_open(&rth, groups) >= 0) -+ { -+ if (rtnl_listen(&rth, netlinkMessageCallback, vsession)<0) -+ { -+ fprintf(stderr, "rtnl_listen existed.\n"); -+ } -+ } -+ else -+ { -+ fprintf(stderr, "rtnl_open failed."); -+ } -+} -+ -+#endif /* HAVE_LIBNETLINK */ -+ -+ - static void loadBlocklists( tr_session * session ); - - static void -@@ -710,6 +929,22 @@ tr_sessionInitImpl( void * vdata ) - - assert( tr_isSession( session ) ); - -+ tr_sessionSet( session, &settings ); -+ -+ /* ^^ here we set the public_ipv4 bindinfo and other settings -+ * so we are safe to go after here -+ */ -+ session->networkInterfacesTimer = evtimer_new( session->event_base, onNetworkIFTimer, session ); -+ onNetworkIFTimer( 0, 0, session ); -+ -+#ifdef HAVE_LIBNETLINK -+ /* If we have LIBNETLINK support we can listen for the kernel events associated with -+ * Links and Routes coming and going. We just simply refresh our network interface -+ * knowledge when interesting events fire. -+ */ -+ tr_threadNew( netlinkListenThreadFunc, session ); -+#endif /* HAVE_LIBNETLINK */ -+ - session->saveTimer = evtimer_new( session->event_base, onSaveTimer, session ); - tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 ); - -@@ -723,8 +958,6 @@ tr_sessionInitImpl( void * vdata ) - - tr_webInit( session ); - -- tr_sessionSet( session, &settings ); -- - tr_udpInit( session ); - - if( session->isLPDEnabled ) -@@ -844,6 +1077,9 @@ sessionSetImpl( void * vdata ) - b.socket = -1; - session->public_ipv6 = tr_memdup( &b, sizeof( struct tr_bindinfo ) ); - -+ if( tr_bencDictFindStr( settings, TR_PREFS_KEY_BIND_INTERFACE, &str ) ) -+ tr_sessionSetPublicInterface( session, str ); -+ - /* incoming peer port */ - if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i ) ) - session->randomPortLow = i; -@@ -1077,7 +1313,14 @@ peerPortChanged( void * session ) - tr_sharedPortChanged( session ); - - while(( tor = tr_torrentNext( session, tor ))) -- tr_torrentChangeMyPort( tor ); -+ { -+ if (tor->isRunning) -+ { -+ tr_torrentStop( tor ); -+ tr_torrentStart( tor ); -+ tr_torrentChangeMyPort( tor ); -+ } -+ } - } - - static void -@@ -1750,6 +1993,12 @@ sessionCloseImpl( void * vsession ) - event_free( session->nowTimer ); - session->nowTimer = NULL; - -+ evtimer_del( session->networkInterfacesTimer ); -+ tr_free( session->networkInterfacesTimer ); -+ session->networkInterfacesTimer = NULL; -+ -+ tr_interfacesFree( session->networkInterfaces ); -+ - tr_verifyClose( session ); - tr_sharedClose( session ); - tr_rpcClose( &session->rpcServer ); -@@ -1795,11 +2044,19 @@ sessionCloseImpl( void * vsession ) - tr_statsClose( session ); - tr_peerMgrFree( session->peerMgr ); - -+// BUG. Advertised IP changes to default IF somewhere after here ... -+ -+ tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE ); -+ - closeBlocklists( session ); - - tr_fdClose( session ); - -+ tr_webClose( session, TR_WEB_CLOSE_NOW ); -+ - session->isClosed = true; -+ -+ exit(0); - } - - static int -@@ -2594,6 +2851,23 @@ tr_sessionGetRPCBindAddress( const tr_session * session ) - ***** - ****/ - -+void -+tr_sessionSetPublicInterface( tr_session * session, -+ const char * publicInterface ) -+{ -+ assert( tr_isSession( session ) ); -+ -+ if( publicInterface != session->publicInterface ) -+ { -+ tr_free( session->publicInterface ); -+ session->publicInterface = tr_strdup( publicInterface ); -+ } -+} -+ -+/**** -+***** -+****/ -+ - bool - tr_sessionIsTorrentDoneScriptEnabled( const tr_session * session ) - { -@@ -2679,6 +2953,7 @@ tr_sessionSetQueueStalledMinutes( tr_session * session, int minutes ) - assert( minutes > 0 ); - - session->queueStalledMinutes = minutes; -+ - } - - void -diff --git a/libtransmission/session.h b/libtransmission/session.h -index 022d16f..152ecc6 100644 ---- a/libtransmission/session.h -+++ b/libtransmission/session.h -@@ -30,6 +30,7 @@ - #include "bandwidth.h" - #include "bencode.h" - #include "bitfield.h" -+#include "net-interfaces.h" - #include "utils.h" - - typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t; -@@ -211,6 +212,9 @@ struct tr_session - - struct event * nowTimer; - struct event * saveTimer; -+ struct event * networkInterfacesTimer; -+ -+ tr_interface ** networkInterfaces; - - /* monitors the "global pool" speeds */ - struct tr_bandwidth bandwidth; -@@ -219,8 +223,10 @@ struct tr_session - - uint16_t idleLimitMinutes; - -+ /* these attribute store the public bind address details */ - struct tr_bindinfo * public_ipv4; - struct tr_bindinfo * public_ipv6; -+ char * publicInterface; - - uint8_t peer_id[PEER_ID_LEN+1]; - }; -@@ -261,6 +267,7 @@ const struct tr_address* tr_sessionGetPublicAddress( const tr_session * sessio - int tr_af_type, - bool * is_default_value ); - -+void tr_sessionSetPublicInterface( tr_session * session, const char * publicInterface ); - - struct tr_bindsockets * tr_sessionGetBindSockets( tr_session * ); - -diff --git a/libtransmission/tr-dht.h b/libtransmission/tr-dht.h -index 3c244ce..3c235ce 100644 ---- a/libtransmission/tr-dht.h -+++ b/libtransmission/tr-dht.h -@@ -27,6 +27,9 @@ - #error only libtransmission should #include this header. - #endif - -+#ifndef __TR_DHT_H_ -+#define __TR_DHT_H_ -+ - enum - { - TR_DHT_STOPPED = 0, -@@ -47,3 +50,5 @@ void tr_dhtUpkeep( tr_session * ); - void tr_dhtCallback(unsigned char *buf, int buflen, - struct sockaddr *from, socklen_t fromlen, - void *sv); -+ -+#endif /* __TR_DHT_H_ */ -diff --git a/libtransmission/tr-lpd.c b/libtransmission/tr-lpd.c -index 361e4a4..f6ad1dc 100644 ---- a/libtransmission/tr-lpd.c -+++ b/libtransmission/tr-lpd.c -@@ -295,6 +295,7 @@ int tr_lpdInit( tr_session* ss, tr_address* tr_addr UNUSED ) - &opt_on, sizeof opt_on ) < 0 ) - goto fail; - -+ tr_netBindSocketInterface(ss, lpd_socket); - memset( &lpd_mcastAddr, 0, sizeof lpd_mcastAddr ); - lpd_mcastAddr.sin_family = AF_INET; - lpd_mcastAddr.sin_port = htons( lpd_mcastPort ); -@@ -330,6 +331,8 @@ int tr_lpdInit( tr_session* ss, tr_address* tr_addr UNUSED ) - if( evutil_make_socket_nonblocking( lpd_socket2 ) < 0 ) - goto fail; - -+ tr_netBindSocketInterface(ss, lpd_socket2); -+ - /* configure outbound multicast TTL */ - if( setsockopt( lpd_socket2, IPPROTO_IP, IP_MULTICAST_TTL, - &scope, sizeof scope ) < 0 ) -diff --git a/libtransmission/tr-udp.c b/libtransmission/tr-udp.c -index 99ee6be..9a61ff8 100644 ---- a/libtransmission/tr-udp.c -+++ b/libtransmission/tr-udp.c -@@ -145,6 +145,8 @@ rebind_ipv6(tr_session *ss, bool force) - setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - #endif - -+ tr_netBindSocketInterface(ss, s); -+ - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - if(ipv6) -@@ -251,6 +253,8 @@ tr_udpInit(tr_session *ss) - goto ipv6; - } - -+ tr_netBindSocketInterface(ss, ss->udp_socket); -+ - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET, &is_default); -diff --git a/libtransmission/tr-utp.c b/libtransmission/tr-utp.c -index 2d78e67..b0b1fb4 100644 ---- a/libtransmission/tr-utp.c -+++ b/libtransmission/tr-utp.c -@@ -45,6 +45,12 @@ THE SOFTWARE. - - #ifndef WITH_UTP - -+/* if no uTP we need a dummy definition for UTPSocket */ -+struct UTPSocket -+{ -+ int sock; -+}; -+ - void - UTP_Close(struct UTPSocket * socket) - { -@@ -61,7 +67,7 @@ UTP_RBDrained(struct UTPSocket *socket) - assert( 0 ); /* FIXME: this is too much for the long term, but probably needed in the short term */ - } - --bool -+uint8_t - UTP_Write(struct UTPSocket *socket, size_t count) - { - tr_nerr( MY_NAME, "UTP_RBDrained(%p, %zu) was called.", socket, count ); -diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h -index 5402ea4..507206e 100644 ---- a/libtransmission/transmission.h -+++ b/libtransmission/transmission.h -@@ -165,6 +165,7 @@ const char* tr_getDefaultDownloadDir( void ); - #define TR_PREFS_KEY_ALT_SPEED_TIME_DAY "alt-speed-time-day" - #define TR_PREFS_KEY_BIND_ADDRESS_IPV4 "bind-address-ipv4" - #define TR_PREFS_KEY_BIND_ADDRESS_IPV6 "bind-address-ipv6" -+#define TR_PREFS_KEY_BIND_INTERFACE "bind-interface" - #define TR_PREFS_KEY_BLOCKLIST_ENABLED "blocklist-enabled" - #define TR_PREFS_KEY_BLOCKLIST_URL "blocklist-url" - #define TR_PREFS_KEY_MAX_CACHE_SIZE_MB "cache-size-mb" -diff --git a/macosx/Controller.m b/macosx/Controller.m -index 34aa462..b317e95 100644 ---- a/macosx/Controller.m -+++ b/macosx/Controller.m -@@ -262,6 +262,8 @@ static void sleepCallback(void * controller, io_service_t y, natural_t messageTy - tr_bencDictAddStr(&settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, [[fDefaults stringForKey: @"BindAddressIPv4"] UTF8String]); - if ([fDefaults objectForKey: @"BindAddressIPv6"]) - tr_bencDictAddStr(&settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, [[fDefaults stringForKey: @"BindAddressIPv6"] UTF8String]); -+ if ([fDefaults objectForKey: @"BindInterface"]) -+ tr_bencDictAddStr(&settings, TR_PREFS_KEY_BIND_INTERFACE, [[fDefaults stringForKey: @"BindInterface"] UTF8String]); - - tr_bencDictAddBool(&settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, [fDefaults boolForKey: @"BlocklistNew"]); - if ([fDefaults objectForKey: @"BlocklistURL"]) -diff --git a/third-party/libutp/utp_config.h b/third-party/libutp/utp_config.h -index 7ee870a..644c33d 100644 ---- a/third-party/libutp/utp_config.h -+++ b/third-party/libutp/utp_config.h -@@ -5,8 +5,8 @@ - // This should return the global number of bytes sent, used for determining dynamic - // packet size based on rate - --#warning implement this in libtransmission --uint64 UTP_GetGlobalUTPBytesSent(const struct sockaddr *remote, socklen_t remotelen) { return 0; } -+// #warning implement this in libtransmission -+uint64 UTP_GetGlobalUTPBytesSent(const struct sockaddr *remote, socklen_t remotelen); - - enum bandwidth_type_t { - payload_bandwidth, connect_overhead, |
