begining of socks5 proxy support. error handling is missing. see #799

This commit is contained in:
Yann Leboulanger 2007-01-24 21:50:59 +00:00
parent 0014e6e597
commit 9a6b090506
5 changed files with 179 additions and 8 deletions

View File

@ -17,6 +17,7 @@
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="destroy" handler="on_manage_proxies_window_destroy" last_modification_time="Wed, 08 Jun 2005 17:33:08 GMT"/>
<child>
@ -209,7 +210,8 @@
<child>
<widget class="GtkComboBox" id="proxytype_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes">HTTP Connect</property>
<property name="items" translatable="yes">HTTP Connect
SOCKS5</property>
<property name="add_tearoffs">False</property>
<property name="focus_on_click">True</property>
<signal name="changed" handler="on_proxytype_combobox_changed" last_modification_time="Wed, 08 Jun 2005 17:45:26 GMT"/>

View File

@ -326,6 +326,7 @@ class Connection(ConnectionHandlers):
proxy['port'] = gajim.config.get_per('proxies', p, 'port')
proxy['user'] = gajim.config.get_per('proxies', p, 'user')
proxy['password'] = gajim.config.get_per('proxies', p, 'pass')
proxy['type'] = gajim.config.get_per('proxies', p, 'type')
else:
proxy = None

View File

@ -88,6 +88,8 @@ class NBCommonClient(CommonClient):
self.NonBlockingTLS.PlugOut()
if self.__dict__.has_key('NBHTTPPROXYsocket'):
self.NBHTTPPROXYsocket.PlugOut()
if self.__dict__.has_key('NBSOCKS5PROXYsocket'):
self.NBSOCKS5PROXYsocket.PlugOut()
if self.__dict__.has_key('NonBlockingTcp'):
self.NonBlockingTcp.PlugOut()
@ -102,9 +104,18 @@ class NBCommonClient(CommonClient):
server = (self.Server, self.Port)
self._Server, self._Proxy, self._Ssl = server , proxy, ssl
self.on_stream_start = on_stream_start
if proxy:
self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected,
self._on_connected_failure, proxy, server)
if proxy:
if proxy.has_key('type'):
type_ = proxy['type']
if type_ == 'socks5':
self.socket = transports_nb.NBSOCKS5PROXYsocket(self._on_connected,
self._on_connected_failure, proxy, server)
elif type_ == 'http':
self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected,
self._on_connected_failure, proxy, server)
else:
self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected,
self._on_connected_failure, proxy, server)
else:
self.connected = 'tcp'
self.socket = transports_nb.NonBlockingTcp(self._on_connected,

View File

@ -15,6 +15,7 @@
## GNU General Public License for more details.
import socket,select,base64,dispatcher_nb
import struct
from simplexml import ustr
from client import PlugIn
from idlequeue import IdleObject
@ -885,6 +886,8 @@ class NBHTTPPROXYsocket(NonBlockingTcp):
self.DEBUG('Invalid proxy reply: %s %s %s' % (proto, code, desc),'error')
self._owner.disconnected()
return
if len(reply) != 2:
pass
self.onreceive(self._on_proxy_auth)
def _on_proxy_auth(self, reply):
@ -901,3 +904,152 @@ class NBHTTPPROXYsocket(NonBlockingTcp):
def DEBUG(self, text, severity):
''' Overwrites DEBUG tag to allow debug output be presented as "CONNECTproxy".'''
return self._owner.DEBUG(DBG_CONNECT_PROXY, text, severity)
class NBSOCKS5PROXYsocket(NonBlockingTcp):
'''SOCKS5 proxy connection class. Uses TCPsocket as the base class
redefines only connect method. Allows to use SOCKS5 proxies with
(optionally) simple authentication (only USERNAME/PASSWORD auth).
'''
def __init__(self, on_connect = None, on_connect_failure = None,
proxy = None, server = None, use_srv = True):
''' Caches proxy and target addresses.
'proxy' argument is a dictionary with mandatory keys 'host' and 'port'
(proxy address) and optional keys 'user' and 'password' to use for
authentication. 'server' argument is a tuple of host and port -
just like TCPsocket uses. '''
self.on_connect_proxy = on_connect
self.on_connect_failure = on_connect_failure
NonBlockingTcp.__init__(self, self._on_tcp_connect, on_connect_failure,
server, use_srv)
self.DBG_LINE=DBG_CONNECT_PROXY
self.server = server
self.proxy = proxy
self.ipaddr = None
def plugin(self, owner):
''' Starts connection. Used interally. Returns non-empty string on
success.'''
owner.debug_flags.append(DBG_CONNECT_PROXY)
NonBlockingTcp.plugin(self, owner)
def connect(self, dupe = None):
''' Starts connection. Connects to proxy, supplies login and password to
it (if were specified while creating instance). Instructs proxy to make
connection to the target server. Returns non-empty sting on success.
'''
NonBlockingTcp.connect(self, (self.proxy['host'], self.proxy['port']))
def _on_tcp_connect(self):
self.DEBUG('Proxy server contacted, performing authentification', 'start')
if self.proxy.has_key('user') and self.proxy.has_key('password'):
to_send = '\x05\x02\x00\x02'
else:
to_send = '\x05\x01\x00'
self.onreceive(self._on_greeting_sent)
self.send(to_send)
def _on_greeting_sent(self, reply):
if reply is None:
return
if len(reply) != 2:
raise error('Invalid proxy reply')
if reply[0] != '\x05':
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected()
return
if reply[1] == '\x00':
return self._on_proxy_auth('\x01\x00')
elif reply[1] == '\x02':
# TODO: Do authentification
self.onreceive(self._on_proxy_auth)
else:
if reply[1] == '\xff':
self.DEBUG('Authentification to proxy impossible: no acceptable '
'auth method', 'error')
else:
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected()
return
def _on_proxy_auth(self, reply):
if reply is None:
return
if len(reply) != 2:
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected()
raise error('Invalid proxy reply')
if reply[0] != '\x01':
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected()
raise error('Invalid proxy reply')
if reply[1] != '\x00':
self.DEBUG('Authentification to proxy failed', 'error')
self._owner.disconnected()
return
self.DEBUG('Authentification successfull. Jabber server contacted.','ok')
# Request connection
req = "\x05\x01\x00"
# If the given destination address is an IP address, we'll
# use the IPv4 address request even if remote resolving was specified.
try:
self.ipaddr = socket.inet_aton(self.server[0])
req = req + "\x01" + ipaddr
except socket.error:
# Well it's not an IP number, so it's probably a DNS name.
# if self.__proxy[3]==True:
# Resolve remotely
self.ipaddr = None
req = req + "\x03" + chr(len(self.server[0])) + self.server[0]
# else:
# # Resolve locally
# self.ipaddr = socket.inet_aton(socket.gethostbyname(self.server[0]))
# req = req + "\x01" + ipaddr
req = req + struct.pack(">H",self.server[1])
self.onreceive(self._on_req_sent)
self.send(req)
def _on_req_sent(self, reply):
if reply is None:
return
if len(reply) < 10:
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected()
raise error('Invalid proxy reply')
if reply[0] != '\x05':
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected()
raise error('Invalid proxy reply')
if reply[1] != "\x00":
# Connection failed
self._owner.disconnected()
if ord(reply[1])<9:
errors = ['general SOCKS server failure',
'connection not allowed by ruleset',
'Network unreachable',
'Host unreachable',
'Connection refused',
'TTL expired',
'Command not supported',
'Address type not supported'
]
txt = errors[ord(reply[1])-1]
else:
txt = 'Invalid proxy reply'
self.DEBUG(txt, 'error')
return
# Get the bound address/port
elif reply[3] == "\x01":
begin, end = 3, 7
elif reply[3] == "\x03":
begin, end = 4, 4 + reply[4]
else:
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected()
return
if self.on_connect_proxy:
self.on_connect_proxy()
def DEBUG(self, text, severity):
''' Overwrites DEBUG tag to allow debug output be presented as "CONNECTproxy".'''
return self._owner.DEBUG(DBG_CONNECT_PROXY, text, severity)

View File

@ -1655,6 +1655,7 @@ class ManageProxiesWindow:
self.window.set_transient_for(gajim.interface.roster.window)
self.proxies_treeview = self.xml.get_widget('proxies_treeview')
self.proxyname_entry = self.xml.get_widget('proxyname_entry')
self.proxytype_combobox = self.xml.get_widget('proxytype_combobox')
self.init_list()
self.xml.signal_autoconnect(self)
self.window.show_all()
@ -1670,7 +1671,7 @@ class ManageProxiesWindow:
def init_list(self):
self.xml.get_widget('remove_proxy_button').set_sensitive(False)
self.xml.get_widget('proxytype_combobox').set_sensitive(False)
self.proxytype_combobox.set_sensitive(False)
self.xml.get_widget('proxy_table').set_sensitive(False)
model = gtk.ListStore(str)
self.proxies_treeview.set_model(model)
@ -1755,7 +1756,9 @@ class ManageProxiesWindow:
'user'))
proxypass_entry.set_text(gajim.config.get_per('proxies', proxy,
'pass'))
#FIXME: if we have several proxy types, set the combobox
proxytype = gajim.config.get_per('proxies', proxy, 'type')
types = ['http', 'socks5']
self.proxytype_combobox.set_active(types.index(proxytype))
if gajim.config.get_per('proxies', proxy, 'user'):
useauth_checkbutton.set_active(True)
@ -1782,8 +1785,10 @@ class ManageProxiesWindow:
model.set_value(iter, 0, new_name)
def on_proxytype_combobox_changed(self, widget):
#FIXME: if we have several proxy types take them into account
pass
types = ['http', 'socks5']
type_ = self.proxytype_combobox.get_active()
proxy = self.proxyname_entry.get_text().decode('utf-8')
gajim.config.set_per('proxies', proxy, 'type', types[type_])
def on_proxyhost_entry_changed(self, widget):
value = widget.get_text().decode('utf-8')