[#3171] Add Option To Specify Outgoing Connection Interface

This commit is contained in:
Doadin 2018-05-02 00:14:02 -04:00 committed by Unknown
parent af2972f697
commit 196aa48727
9 changed files with 122 additions and 10 deletions

View File

@ -9,6 +9,7 @@
* Enforced the use of the "deluge.plugins" namespace to reduce package * Enforced the use of the "deluge.plugins" namespace to reduce package
names clashing beetween regular packages and deluge plugins. names clashing beetween regular packages and deluge plugins.
* Fix potential for host_id collision when creating hostlist entries. * Fix potential for host_id collision when creating hostlist entries.
* Add Option To Specify Outgoing Connection Interface.
==== Core ==== ==== Core ====
* Make the distinction between adding to the session new unmanaged torrents * Make the distinction between adding to the session new unmanaged torrents

View File

@ -101,7 +101,7 @@ DELUGE_VER = deluge.common.get_version()
class Core(component.Component): class Core(component.Component):
def __init__(self, listen_interface=None, read_only_config_keys=None): def __init__(self, listen_interface=None, outgoing_interface=None, read_only_config_keys=None):
component.Component.__init__(self, 'Core') component.Component.__init__(self, 'Core')
# Start the libtorrent session. # Start the libtorrent session.
@ -163,6 +163,13 @@ class Core(component.Component):
self.config['listen_interface'] = listen_interface self.config['listen_interface'] = listen_interface
else: else:
log.error('Invalid listen interface (must be IP Address): %s', listen_interface) log.error('Invalid listen interface (must be IP Address): %s', listen_interface)
self.__old_outgoing_interface = None
if outgoing_interface:
if deluge.common.is_ip(outgoing_interface):
self.__old_outgoing_interface = self.config['outgoing_interface']
self.config['outgoing_interface'] = outgoing_interface
else:
log.error('Invalid outgoing interface (must be IP Address): %s', outgoing_interface)
# New release check information # New release check information
self.__new_release = None self.__new_release = None
@ -197,6 +204,9 @@ class Core(component.Component):
if self.__old_interface: if self.__old_interface:
self.config['listen_interface'] = self.__old_interface self.config['listen_interface'] = self.__old_interface
if self.__old_outgoing_interface:
self.config['outgoing_interface'] = self.__old_outgoing_interface
# Make sure the config file has been saved # Make sure the config file has been saved
self.config.save() self.config.save()

View File

@ -65,11 +65,12 @@ def is_daemon_running(pid_file):
class Daemon(object): class Daemon(object):
"""The Deluge Daemon class""" """The Deluge Daemon class"""
def __init__(self, listen_interface=None, interface=None, port=None, standalone=False, def __init__(self, listen_interface=None, outgoing_interface=None, interface=None, port=None, standalone=False,
read_only_config_keys=None): read_only_config_keys=None):
""" """
Args: Args:
listen_interface (str, optional): The IP address to listen to bittorrent connections on. listen_interface (str, optional): The IP address to listen to bittorrent connections on.
outgoing_interface (str, optional): The IP address to open outgoing BitTorrent connections on.
interface (str, optional): The IP address the daemon will listen for UI connections on. interface (str, optional): The IP address the daemon will listen for UI connections on.
port (int, optional): The port the daemon will listen for UI connections on. port (int, optional): The port the daemon will listen for UI connections on.
standalone (bool, optional): If True the client is in Standalone mode otherwise, if standalone (bool, optional): If True the client is in Standalone mode otherwise, if
@ -98,6 +99,7 @@ class Daemon(object):
# Start the core as a thread and join it until it's done # Start the core as a thread and join it until it's done
self.core = Core(listen_interface=listen_interface, self.core = Core(listen_interface=listen_interface,
outgoing_interface=outgoing_interface,
read_only_config_keys=read_only_config_keys) read_only_config_keys=read_only_config_keys)
if port is None: if port is None:
@ -115,7 +117,8 @@ class Daemon(object):
interface=interface interface=interface
) )
log.debug('Listening to UI on: %s:%s and bittorrent on: %s', interface, port, listen_interface) log.debug('Listening to UI on: %s:%s and bittorrent on: %s Making connections out on: %s',
interface, port, listen_interface, outgoing_interface)
def start(self): def start(self):
# Register the daemon and the core RPCs # Register the daemon and the core RPCs

View File

@ -29,6 +29,8 @@ def add_daemon_options(parser):
help=_('Port to listen for UI connections on')) help=_('Port to listen for UI connections on'))
group.add_argument('-i', '--interface', metavar='<ip-addr>', dest='listen_interface', action='store', group.add_argument('-i', '--interface', metavar='<ip-addr>', dest='listen_interface', action='store',
help=_('IP address to listen for BitTorrent connections')) help=_('IP address to listen for BitTorrent connections'))
group.add_argument('-o', '--outinterface', metavar='<ip-addr>', dest='outgoing_interface',
action='store', help=_('The IP address for outgoing BitTorrent connections.'))
group.add_argument('--read-only-config-keys', metavar='<comma-separated-keys>', action='store', group.add_argument('--read-only-config-keys', metavar='<comma-separated-keys>', action='store',
help=_('Config keys to be unmodified by `set_config` RPC'), type=str, default='') help=_('Config keys to be unmodified by `set_config` RPC'), type=str, default='')
parser.add_process_arg_group() parser.add_process_arg_group()
@ -73,6 +75,7 @@ def start_daemon(skip_start=False):
try: try:
from deluge.core.daemon import Daemon from deluge.core.daemon import Daemon
daemon = Daemon(listen_interface=options.listen_interface, daemon = Daemon(listen_interface=options.listen_interface,
outgoing_interface=options.outgoing_interface,
interface=options.ui_interface, interface=options.ui_interface,
port=options.port, port=options.port,
read_only_config_keys=options.read_only_config_keys.split(',')) read_only_config_keys=options.read_only_config_keys.split(','))

View File

@ -47,6 +47,7 @@ DEFAULT_PREFS = {
'download_location': deluge.common.get_default_download_dir(), 'download_location': deluge.common.get_default_download_dir(),
'listen_ports': [6881, 6891], 'listen_ports': [6881, 6891],
'listen_interface': '', 'listen_interface': '',
'outgoing_interface': '',
'random_port': True, 'random_port': True,
'listen_random_port': None, 'listen_random_port': None,
'listen_use_sys_port': False, 'listen_use_sys_port': False,
@ -187,6 +188,9 @@ class PreferencesManager(component.Component):
def _on_set_listen_interface(self, key, value): def _on_set_listen_interface(self, key, value):
self.__set_listen_on() self.__set_listen_on()
def _on_set_outgoing_interface(self, key, value):
self.__set_outgoing_on()
def _on_set_random_port(self, key, value): def _on_set_random_port(self, key, value):
self.__set_listen_on() self.__set_listen_on()
@ -210,6 +214,13 @@ class PreferencesManager(component.Component):
{'listen_system_port_fallback': self.config['listen_use_sys_port'], {'listen_system_port_fallback': self.config['listen_use_sys_port'],
'listen_interfaces': ''.join(interfaces)}) 'listen_interfaces': ''.join(interfaces)})
def __set_outgoing_on(self):
""" Set the interface address for outgoing BitTorrent connections."""
outinterface = self.config['outgoing_interface'].strip()
outinterface = outinterface if outinterface else '0.0.0.0'
self.core.apply_session_settings(
{'outgoing_interfaces': outinterface})
def _on_set_outgoing_ports(self, key, value): def _on_set_outgoing_ports(self, key, value):
self.__set_outgoing_ports() self.__set_outgoing_ports()

View File

@ -83,6 +83,10 @@ class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler):
interface = ipt.get_value().strip() interface = ipt.get_value().strip()
if is_ip(interface) or not interface: if is_ip(interface) or not interface:
conf_dict['listen_interface'] = interface conf_dict['listen_interface'] = interface
elif ipt.name == 'outgoing_interface':
outinterface = ipt.get_value().strip()
if is_ip(outinterface) or not outinterface:
conf_dict['outgoing_interface'] = outinterface
elif ipt.name.startswith('proxy_'): elif ipt.name.startswith('proxy_'):
if ipt.name == 'proxy_type': if ipt.name == 'proxy_type':
conf_dict.setdefault('proxy', {})['type'] = ipt.get_value() conf_dict.setdefault('proxy', {})['type'] = ipt.get_value()
@ -240,10 +244,19 @@ class NetworkPane(BasePreferencePane):
value=out_ports[1], min_val=0, max_val=65535) value=out_ports[1], min_val=0, max_val=65535)
self.outto.set_depend(outrand, inverse=True) self.outto.set_depend(outrand, inverse=True)
self.add_header(_('Interface'), space_above=True) self.add_header(_('Incoming Interface'), space_above=True)
self.add_text_input('listen_interface', '%s:' % _('IP address of the interface to listen on ' self.add_text_input(
'(leave empty for default)'), 'listen_interface',
core_conf['listen_interface']) _('IP address of the interface to listen on (leave empty for default):'),
core_conf['listen_interface'],
)
self.add_header(_('Outgoing Interface'), space_above=True)
self.add_text_input(
'outgoing_interface',
_('IP address of the interface to open outgoing connections on. (leave empty for default):'),
core_conf['outgoing_interface'],
)
self.add_header('TOS', space_above=True) self.add_header('TOS', space_above=True)
self.add_text_input('peer_tos', 'Peer TOS Byte:', core_conf['peer_tos']) self.add_text_input('peer_tos', 'Peer TOS Byte:', core_conf['peer_tos'])

View File

@ -2906,6 +2906,56 @@ used sparingly.</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkFrame" id="frame9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment17">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xscale">0</property>
<property name="top_padding">5</property>
<property name="left_padding">12</property>
<child>
<object class="GtkEntry" id="entry_outgoing_interface">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">IP address for outgoing BitTorrent connections. Leave this empty if you want to use the default.</property>
<property name="max_length">15</property>
<property name="invisible_char">●</property>
<property name="width_chars">15</property>
<property name="truncate_multiline">True</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Outgoing Address</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">5</property>
<property name="position">2</property>
</packing>
</child>
<child> <child>
<object class="GtkFrame" id="frame26"> <object class="GtkFrame" id="frame26">
<property name="visible">True</property> <property name="visible">True</property>
@ -3028,7 +3078,7 @@ used sparingly.</property>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="padding">5</property> <property name="padding">5</property>
<property name="position">2</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -3190,7 +3240,7 @@ used sparingly.</property>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="padding">5</property> <property name="padding">5</property>
<property name="position">3</property> <property name="position">4</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -3361,7 +3411,7 @@ used sparingly.</property>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="padding">5</property> <property name="padding">5</property>
<property name="position">4</property> <property name="position">5</property>
</packing> </packing>
</child> </child>
</object> </object>

View File

@ -317,6 +317,7 @@ class Preferences(component.Component):
'spin_outgoing_port_max': ('value', lambda: self.core_config['outgoing_ports'][1]), 'spin_outgoing_port_max': ('value', lambda: self.core_config['outgoing_ports'][1]),
'chk_random_outgoing_ports': ('active', 'random_outgoing_ports'), 'chk_random_outgoing_ports': ('active', 'random_outgoing_ports'),
'entry_interface': ('text', 'listen_interface'), 'entry_interface': ('text', 'listen_interface'),
'entry_outgoing_interface': ('text', 'outgoing_interface'),
'entry_peer_tos': ('text', 'peer_tos'), 'entry_peer_tos': ('text', 'peer_tos'),
'chk_dht': ('active', 'dht'), 'chk_dht': ('active', 'dht'),
'chk_upnp': ('active', 'upnp'), 'chk_upnp': ('active', 'upnp'),
@ -519,8 +520,11 @@ class Preferences(component.Component):
new_core_config['random_outgoing_ports'] = self.builder.get_object( new_core_config['random_outgoing_ports'] = self.builder.get_object(
'chk_random_outgoing_ports').get_active() 'chk_random_outgoing_ports').get_active()
incoming_address = self.builder.get_object('entry_interface').get_text().strip() incoming_address = self.builder.get_object('entry_interface').get_text().strip()
outgoing_address = self.builder.get_object('entry_outgoing_interface').get_text().strip()
if deluge.common.is_ip(incoming_address) or not incoming_address: if deluge.common.is_ip(incoming_address) or not incoming_address:
new_core_config['listen_interface'] = incoming_address new_core_config['listen_interface'] = incoming_address
if deluge.common.is_ip(outgoing_address) or not outgoing_address:
new_core_config['outgoing_interface'] = outgoing_address
new_core_config['peer_tos'] = self.builder.get_object('entry_peer_tos').get_text() new_core_config['peer_tos'] = self.builder.get_object('entry_peer_tos').get_text()
new_core_config['dht'] = self.builder.get_object('chk_dht').get_active() new_core_config['dht'] = self.builder.get_object('chk_dht').get_active()
new_core_config['upnp'] = self.builder.get_object('chk_upnp').get_active() new_core_config['upnp'] = self.builder.get_object('chk_upnp').get_active()

View File

@ -90,6 +90,23 @@ Deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, {
}); });
optMan.bind('listen_ports', this.listenPort); optMan.bind('listen_ports', this.listenPort);
fieldset = this.add({
xtype: 'fieldset',
border: false,
title: _('Outgoing Address'),
style: 'margin-bottom: 5px; padding-bottom: 0px;',
autoHeight: true,
labelWidth: 1,
defaultType: 'textfield'
});
optMan.bind('outgoing_interface', fieldset.add({
name: 'outgoing_interface',
fieldLabel: '',
labelSeparator: '',
width: 200,
vtype: 'IPAddress'
}));
fieldset = this.add({ fieldset = this.add({
xtype: 'fieldset', xtype: 'fieldset',
border: false, border: false,