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