diff options
Diffstat (limited to 'net-p2p/transmission/files/transmission-2.76-bind-to-interface.patch')
| -rw-r--r-- | net-p2p/transmission/files/transmission-2.76-bind-to-interface.patch | 2207 |
1 files changed, 0 insertions, 2207 deletions
diff --git a/net-p2p/transmission/files/transmission-2.76-bind-to-interface.patch b/net-p2p/transmission/files/transmission-2.76-bind-to-interface.patch deleted file mode 100644 index 543d2085..00000000 --- a/net-p2p/transmission/files/transmission-2.76-bind-to-interface.patch +++ /dev/null @@ -1,2207 +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 | 25 +++ - 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 | 4 - 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, 1409 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 c9a4f16..a075d3e 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 @@ - A2A4EA0B0DE106E8000CE197 /* ConvertUTF.h */, - A2A4EA0A0DE106E8000CE197 /* ConvertUTF.c */, - 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 ed72070..719a1a7 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 2e91872..0698c7b 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" -@@ -1195,6 +1198,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); -@@ -1230,9 +1236,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 8278ed6..c5778dc 100644 ---- a/libtransmission/Makefile.am -+++ b/libtransmission/Makefile.am -@@ -40,12 +40,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 \ -@@ -130,6 +134,7 @@ TESTS = \ - json-test \ - magnet-test \ - metainfo-test \ -+ net-interfaces-test \ - peer-msgs-test \ - rpc-test \ - test-peer-id \ -@@ -189,6 +194,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 2322dd7..2031f9a 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 97f0ce1..6f7332c 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 0a672cb..4ce8338 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; -@@ -313,7 +397,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; -@@ -338,6 +422,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) -@@ -386,14 +471,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; -@@ -401,7 +486,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) -@@ -432,6 +517,7 @@ void - tr_netCloseSocket (int fd) - { - evutil_closesocket (fd); -+ tr_fdtrack_close (fd); - } - - void -@@ -647,9 +733,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 4cc583c..b9e5dce 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 cb2ab13..4c37887 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); - -@@ -208,9 +211,11 @@ canReadWrapper (tr_peerIo * io) - { - 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 +410,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 +431,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 +448,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 +524,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 +585,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,6 +653,9 @@ 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 { -diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c -index 230c30d..388c502 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 0cf08cf..efaa6b7 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 19d8907..0d15824 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 */ - }; - - -@@ -199,17 +216,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); - } -@@ -368,6 +387,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); - } -@@ -439,6 +459,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)); - } -@@ -555,6 +576,7 @@ onSaveTimer (int foo UNUSED, short bar UNUSED, void * vsession) - ***/ - - static void tr_sessionInitImpl (void *); -+static void peerPortChanged (void * session); - - struct init_data - { -@@ -662,6 +684,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 -@@ -712,6 +931,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); - -@@ -725,8 +960,6 @@ tr_sessionInitImpl (void * vdata) - - tr_webInit (session); - -- tr_sessionSet (session, &settings); -- - tr_udpInit (session); - - if (session->isLPDEnabled) -@@ -846,6 +1079,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; -@@ -1079,7 +1315,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 -@@ -1752,6 +1995,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); -@@ -1797,11 +2046,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 -@@ -2596,6 +2853,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) - { -@@ -2681,6 +2955,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 3c5f7b6..7acca06 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 7a28e3c..1702319 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 b8e4570..745e883 100644 ---- a/libtransmission/tr-lpd.c -+++ b/libtransmission/tr-lpd.c -@@ -295,6 +295,8 @@ 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 +332,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 8c343ed..775152c 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 f7eb7d0..2cbf155 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 b91f8ae..bcf8317 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 ffb3d55..4c9cc97 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, |
