proxy65_manager resolves socks5 proxies at gajim
startup and account login
This commit is contained in:
parent
8d15ac84be
commit
b90e546927
|
@ -97,23 +97,6 @@ class ConnectionBytestream:
|
||||||
gajim.socks5queue.remove_receiver(host['idx'])
|
gajim.socks5queue.remove_receiver(host['idx'])
|
||||||
gajim.socks5queue.remove_sender(host['idx'])
|
gajim.socks5queue.remove_sender(host['idx'])
|
||||||
|
|
||||||
def get_cached_proxies(self, proxy):
|
|
||||||
''' get cached entries for proxy and request the cache again '''
|
|
||||||
host = gajim.config.get_per('ft_proxies65_cache', proxy, 'host')
|
|
||||||
port = gajim.config.get_per('ft_proxies65_cache', proxy, 'port')
|
|
||||||
jid = gajim.config.get_per('ft_proxies65_cache', proxy, 'jid')
|
|
||||||
|
|
||||||
iq = common.xmpp.Protocol(name = 'iq', to = proxy, typ = 'get')
|
|
||||||
query = iq.setTag('query')
|
|
||||||
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
|
||||||
# FIXME bad logic - this should be somewhere else!
|
|
||||||
# this line should be put somewhere else
|
|
||||||
# self.connection.send(iq)
|
|
||||||
# ensure that we don;t return empty vars
|
|
||||||
if None not in (host, port, jid) or '' not in (host, port, jid):
|
|
||||||
return (host, port, jid)
|
|
||||||
return (None, None, None)
|
|
||||||
|
|
||||||
def send_socks5_info(self, file_props, fast = True, receiver = None,
|
def send_socks5_info(self, file_props, fast = True, receiver = None,
|
||||||
sender = None):
|
sender = None):
|
||||||
''' send iq for the present streamhosts and proxies '''
|
''' send iq for the present streamhosts and proxies '''
|
||||||
|
@ -131,7 +114,7 @@ class ConnectionBytestream:
|
||||||
if fast and cfg_proxies:
|
if fast and cfg_proxies:
|
||||||
proxies = map(lambda e:e.strip(), cfg_proxies.split(','))
|
proxies = map(lambda e:e.strip(), cfg_proxies.split(','))
|
||||||
for proxy in proxies:
|
for proxy in proxies:
|
||||||
(host, _port, jid) = self.get_cached_proxies(proxy)
|
(host, _port, jid) = gajim.proxy65_manager.get_proxy(proxy)
|
||||||
if host is None:
|
if host is None:
|
||||||
continue
|
continue
|
||||||
host_dict={
|
host_dict={
|
||||||
|
@ -361,32 +344,8 @@ class ConnectionBytestream:
|
||||||
frm = helpers.get_full_jid_from_iq(iq_obj)
|
frm = helpers.get_full_jid_from_iq(iq_obj)
|
||||||
real_id = unicode(iq_obj.getAttr('id'))
|
real_id = unicode(iq_obj.getAttr('id'))
|
||||||
query = iq_obj.getTag('query')
|
query = iq_obj.getTag('query')
|
||||||
streamhost = None
|
gajim.proxy65_manager.resolve_result(frm, query)
|
||||||
try:
|
|
||||||
streamhost = query.getTag('streamhost')
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if streamhost is not None: # this is a result for proxy request
|
|
||||||
jid = None
|
|
||||||
try:
|
|
||||||
jid = streamhost.getAttr('jid')
|
|
||||||
except:
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
proxyhosts = []
|
|
||||||
for item in query.getChildren():
|
|
||||||
if item.getName() == 'streamhost':
|
|
||||||
host = item.getAttr('host')
|
|
||||||
port = item.getAttr('port')
|
|
||||||
jid = item.getAttr('jid')
|
|
||||||
conf = gajim.config
|
|
||||||
conf.add_per('ft_proxies65_cache', jid)
|
|
||||||
conf.set_per('ft_proxies65_cache', jid,
|
|
||||||
'host', unicode(host))
|
|
||||||
conf.set_per('ft_proxies65_cache', jid,
|
|
||||||
'port', int(port))
|
|
||||||
conf.set_per('ft_proxies65_cache', jid,
|
|
||||||
'jid', unicode(jid))
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
try:
|
try:
|
||||||
streamhost = query.getTag('streamhost-used')
|
streamhost = query.getTag('streamhost-used')
|
||||||
except: # this bytestream result is not what we need
|
except: # this bytestream result is not what we need
|
||||||
|
@ -1521,7 +1480,13 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
||||||
if not self.connection:
|
if not self.connection:
|
||||||
return
|
return
|
||||||
self.connection.getRoster(self._on_roster_set)
|
self.connection.getRoster(self._on_roster_set)
|
||||||
|
cfg_proxies = gajim.config.get_per('accounts', self.name,
|
||||||
|
'file_transfer_proxies')
|
||||||
|
if cfg_proxies:
|
||||||
|
proxies = map(lambda e:e.strip(), cfg_proxies.split(','))
|
||||||
|
for proxy in proxies:
|
||||||
|
gajim.proxy65_manager.resolve(proxy, self.connection)
|
||||||
|
|
||||||
def _on_roster_set(self, roster):
|
def _on_roster_set(self, roster):
|
||||||
raw_roster = roster.getRaw()
|
raw_roster = roster.getRaw()
|
||||||
roster = {}
|
roster = {}
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
##
|
||||||
|
## Copyright (C) 2006 Gajim Team
|
||||||
|
##
|
||||||
|
## Contributors for this file:
|
||||||
|
## - Dimitur Kirov <dkirov@gmail.com>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
import common.xmpp
|
||||||
|
from common import gajim
|
||||||
|
from socks5 import Socks5
|
||||||
|
from common.xmpp.idlequeue import IdleObject
|
||||||
|
|
||||||
|
S_INITIAL = 0
|
||||||
|
S_STARTED = 1
|
||||||
|
S_RESOLVED = 2
|
||||||
|
S_FINISHED = 4
|
||||||
|
|
||||||
|
CONNECT_TIMEOUT = 20
|
||||||
|
|
||||||
|
class Proxy65Manager:
|
||||||
|
''' keep records for file transfer proxies. Each time account
|
||||||
|
establishes a connection to its server call proxy65manger.resolve(proxy)
|
||||||
|
for every proxy that is convigured within the account. The class takes
|
||||||
|
care to resolve and test each proxy only once.'''
|
||||||
|
def __init__(self, idlequeue):
|
||||||
|
# dict {proxy: proxy properties}
|
||||||
|
self.idlequeue = idlequeue
|
||||||
|
self.proxies = {}
|
||||||
|
|
||||||
|
def resolve(self, proxy, connection):
|
||||||
|
''' start '''
|
||||||
|
if self.proxies.has_key(proxy):
|
||||||
|
resolver = self.proxies[proxy]
|
||||||
|
else:
|
||||||
|
# proxy is being ressolved for the first time
|
||||||
|
resolver = ProxyResolver(proxy)
|
||||||
|
self.proxies[proxy] = resolver
|
||||||
|
resolver.add_connection(connection)
|
||||||
|
|
||||||
|
if resolver.state == S_FINISHED:
|
||||||
|
# resolving this proxy is already started or completed
|
||||||
|
return
|
||||||
|
|
||||||
|
def diconnect(self, connection):
|
||||||
|
for resolver in self.proxies:
|
||||||
|
resolver.disconnect(connection)
|
||||||
|
|
||||||
|
def resolve_result(self, proxy, query):
|
||||||
|
if not self.proxies.has_key(proxy):
|
||||||
|
return
|
||||||
|
jid = None
|
||||||
|
for item in query.getChildren():
|
||||||
|
if item.getName() == 'streamhost':
|
||||||
|
host = item.getAttr('host')
|
||||||
|
port = item.getAttr('port')
|
||||||
|
jid = item.getAttr('jid')
|
||||||
|
self.proxies[proxy].resolve_result(host, port, jid)
|
||||||
|
# we can have only one streamhost
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
def get_proxy(self, proxy):
|
||||||
|
if self.proxies.has_key(proxy):
|
||||||
|
resolver = self.proxies[proxy]
|
||||||
|
if resolver.state == S_FINISHED:
|
||||||
|
return (resolver.host, resolver.port, resolver.jid)
|
||||||
|
return (None, 0, None)
|
||||||
|
|
||||||
|
class ProxyResolver:
|
||||||
|
def resolve_result(self, host, port, jid):
|
||||||
|
''' test if host has a real proxy65 listening on port '''
|
||||||
|
self.host = unicode(host)
|
||||||
|
self.port = int(port)
|
||||||
|
self.jid = unicode(jid)
|
||||||
|
self.state = S_RESOLVED
|
||||||
|
self.host_tester = HostTester(self.host, self.port, self.jid,
|
||||||
|
self._on_connect_success, self._on_connect_failure)
|
||||||
|
self.host_tester.connect()
|
||||||
|
|
||||||
|
def _on_connect_success(self):
|
||||||
|
conf = gajim.config
|
||||||
|
conf.add_per('ft_proxies65_cache', self.proxy)
|
||||||
|
conf.set_per('ft_proxies65_cache', self.proxy, 'host', self.host)
|
||||||
|
conf.set_per('ft_proxies65_cache', self.proxy, 'port', self.port)
|
||||||
|
conf.set_per('ft_proxies65_cache', self.proxy, 'jid', self.jid)
|
||||||
|
self.state = S_FINISHED
|
||||||
|
|
||||||
|
def _on_connect_failure(self):
|
||||||
|
self.state = S_FINISHED
|
||||||
|
self.host = None
|
||||||
|
self.port = 0
|
||||||
|
self.jid = None
|
||||||
|
|
||||||
|
def disconnect(self, connection):
|
||||||
|
if self.host_tester:
|
||||||
|
self.host_tester.disconnect()
|
||||||
|
self.host_tester = None
|
||||||
|
if self.connections.has_key(connection):
|
||||||
|
self.connections.remove(connection)
|
||||||
|
if self.state == S_STARTED:
|
||||||
|
self.state = S_INITIAL
|
||||||
|
self.try_next_connection()
|
||||||
|
|
||||||
|
def try_next_connection(self):
|
||||||
|
''' try to resolve proxy with the next possible connection '''
|
||||||
|
if self.connections:
|
||||||
|
connection = self.connections.pop(0)
|
||||||
|
self.start_resolve(connection)
|
||||||
|
|
||||||
|
def add_connection(self, connection):
|
||||||
|
''' add a new connection in case the first fails '''
|
||||||
|
self.connections.append(connection)
|
||||||
|
if self.state == S_INITIAL:
|
||||||
|
self.start_resolve(connection)
|
||||||
|
|
||||||
|
def start_resolve(self, connection):
|
||||||
|
''' request network address from proxy '''
|
||||||
|
self.state = S_STARTED
|
||||||
|
self.active_connection = connection
|
||||||
|
iq = common.xmpp.Protocol(name = 'iq', to = self.proxy, typ = 'get')
|
||||||
|
query = iq.setTag('query')
|
||||||
|
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
||||||
|
connection.send(iq)
|
||||||
|
|
||||||
|
def __init__(self, proxy):
|
||||||
|
self.proxy = proxy
|
||||||
|
self.state = S_INITIAL
|
||||||
|
self.connections = []
|
||||||
|
self.host_tester = None
|
||||||
|
self.jid = None
|
||||||
|
self.host = None
|
||||||
|
self.port = None
|
||||||
|
|
||||||
|
class HostTester(Socks5, IdleObject):
|
||||||
|
''' fake proxy tester. '''
|
||||||
|
def __init__(self, host, port, jid, on_success, on_failure):
|
||||||
|
''' try to establish and auth to proxy at (host, port)
|
||||||
|
call on_success, or on_failure according to the result'''
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.jid = jid
|
||||||
|
self.on_success = on_success
|
||||||
|
self.on_failure = on_failure
|
||||||
|
self._sock = None
|
||||||
|
self.file_props = {}
|
||||||
|
Socks5.__init__(self, gajim.idlequeue, host, port, None, None, None)
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
''' create the socket and plug it to the idlequeue '''
|
||||||
|
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self._sock.setblocking(False)
|
||||||
|
self.fd = self._sock.fileno()
|
||||||
|
self.state = 0 # about to be connected
|
||||||
|
gajim.idlequeue.plug_idle(self, True, False)
|
||||||
|
self.do_connect()
|
||||||
|
self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def read_timeout(self):
|
||||||
|
self.idlequeue.remove_timeout(self.fd)
|
||||||
|
self.pollend()
|
||||||
|
|
||||||
|
def pollend(self):
|
||||||
|
self.disconnect()
|
||||||
|
self.on_failure()
|
||||||
|
|
||||||
|
def pollout(self):
|
||||||
|
self.idlequeue.remove_timeout(self.fd)
|
||||||
|
if self.state == 0:
|
||||||
|
self.do_connect()
|
||||||
|
return
|
||||||
|
elif self.state == 1: # send initially: version and auth types
|
||||||
|
data = self._get_auth_buff()
|
||||||
|
self.send_raw(data)
|
||||||
|
elif self.state == 3: # send 'connect' request
|
||||||
|
data = self._get_request_buff(self._get_sha1_auth())
|
||||||
|
self.send_raw(data)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
self.state += 1
|
||||||
|
# unplug and plug for reading
|
||||||
|
gajim.idlequeue.plug_idle(self, False, True)
|
||||||
|
gajim.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
|
||||||
|
|
||||||
|
def pollin(self):
|
||||||
|
self.idlequeue.remove_timeout(self.fd)
|
||||||
|
if self.state > 1:
|
||||||
|
self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
|
||||||
|
result = self.main(0)
|
||||||
|
else:
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
def main(self, timeout = 0):
|
||||||
|
''' begin negotiation. on success 'address' != 0 '''
|
||||||
|
result = 1
|
||||||
|
buff = self.receive()
|
||||||
|
if buff == '':
|
||||||
|
# end connection
|
||||||
|
self.pollend()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.state == 2: # read auth response
|
||||||
|
if buff is None or len(buff) != 2:
|
||||||
|
return None
|
||||||
|
version, method = struct.unpack('!BB', buff[:2])
|
||||||
|
if version != 0x05 or method == 0xff:
|
||||||
|
self.pollend()
|
||||||
|
self.state = 3
|
||||||
|
gajim.idlequeue.plug_idle(self, True, False)
|
||||||
|
|
||||||
|
elif self.state == 4: # get approve of our request
|
||||||
|
if buff == None:
|
||||||
|
return None
|
||||||
|
sub_buff = buff[:4]
|
||||||
|
if len(sub_buff) < 4:
|
||||||
|
return None
|
||||||
|
version, command, rsvd, address_type = struct.unpack('!BBBB', buff[:4])
|
||||||
|
addrlen, address, port = 0, 0, 0
|
||||||
|
if address_type == 0x03:
|
||||||
|
addrlen = ord(buff[4])
|
||||||
|
address = struct.unpack('!%ds' % addrlen, buff[5:addrlen + 5])
|
||||||
|
portlen = len(buff[addrlen + 5:])
|
||||||
|
if portlen == 1:
|
||||||
|
port, = struct.unpack('!B', buff[addrlen + 5])
|
||||||
|
elif portlen > 2:
|
||||||
|
port, = struct.unpack('!H', buff[addrlen + 5:])
|
||||||
|
self.disconnect()
|
||||||
|
self.on_success()
|
||||||
|
|
||||||
|
|
||||||
|
def do_connect(self):
|
||||||
|
try:
|
||||||
|
self._sock.connect((self.host, self.port))
|
||||||
|
self._sock.setblocking(False)
|
||||||
|
self._send=self._sock.send
|
||||||
|
self._recv=self._sock.recv
|
||||||
|
except Exception, ee:
|
||||||
|
(errnum, errstr) = ee
|
||||||
|
if errnum == 111:
|
||||||
|
self.on_failure()
|
||||||
|
return None
|
||||||
|
# win32 needs this
|
||||||
|
elif errnum != 10056 or self.state != 0:
|
||||||
|
return None
|
||||||
|
else: # socket is already connected
|
||||||
|
self._sock.setblocking(False)
|
||||||
|
self._send=self._sock.send
|
||||||
|
self._recv=self._sock.recv
|
||||||
|
self.buff = ''
|
||||||
|
self.state = 1 # connected
|
||||||
|
self.idlequeue.plug_idle(self, True, False)
|
||||||
|
return
|
||||||
|
|
|
@ -108,6 +108,7 @@ import common.sleepy
|
||||||
|
|
||||||
from common.xmpp import idlequeue
|
from common.xmpp import idlequeue
|
||||||
from common import nslookup
|
from common import nslookup
|
||||||
|
from common import proxy65_manager
|
||||||
from common import socks5
|
from common import socks5
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import connection
|
from common import connection
|
||||||
|
@ -1641,6 +1642,7 @@ class Interface:
|
||||||
gajim.socks5queue = socks5.SocksQueue(gajim.idlequeue,
|
gajim.socks5queue = socks5.SocksQueue(gajim.idlequeue,
|
||||||
self.handle_event_file_rcv_completed,
|
self.handle_event_file_rcv_completed,
|
||||||
self.handle_event_file_progress)
|
self.handle_event_file_progress)
|
||||||
|
gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
|
||||||
self.register_handlers()
|
self.register_handlers()
|
||||||
for account in gajim.config.get_per('accounts'):
|
for account in gajim.config.get_per('accounts'):
|
||||||
gajim.connections[account] = common.connection.Connection(account)
|
gajim.connections[account] = common.connection.Connection(account)
|
||||||
|
|
Loading…
Reference in New Issue