summaryrefslogtreecommitdiff
path: root/net-p2p/transmission/files/transmission-2.76-bind-to-interface.patch
diff options
context:
space:
mode:
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.patch2207
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,