2017-01-11 17:00:00 +01:00
import errno
2013-06-22 01:49:50 +02:00
import threading
import time
import random
import shared
2015-11-22 16:18:59 +01:00
import select
2013-06-22 01:49:50 +02:00
import socks
import socket
import sys
2013-06-24 21:51:01 +02:00
import tr
2013-06-22 01:49:50 +02:00
from class_sendDataThread import *
from class_receiveDataThread import *
2017-02-22 09:34:54 +01:00
from bmconfigparser import BMConfigParser
2015-11-22 16:18:59 +01:00
from helper_threading import *
2017-02-08 13:41:56 +01:00
import knownnodes
import queues
2017-01-11 17:00:00 +01:00
import state
2013-06-22 01:49:50 +02:00
# For each stream to which we connect, several outgoingSynSender threads
# will exist and will collectively create 8 connections with peers.
2015-11-22 16:18:59 +01:00
class outgoingSynSender ( threading . Thread , StoppableThread ) :
2013-06-22 01:49:50 +02:00
def __init__ ( self ) :
2015-11-18 16:22:17 +01:00
threading . Thread . __init__ ( self , name = " outgoingSynSender " )
2015-11-22 16:18:59 +01:00
self . initStop ( )
2017-02-28 09:40:28 +01:00
random . seed ( )
2013-06-22 01:49:50 +02:00
def setup ( self , streamNumber , selfInitiatedConnections ) :
self . streamNumber = streamNumber
self . selfInitiatedConnections = selfInitiatedConnections
2014-02-06 14:16:07 +01:00
def _getPeer ( self ) :
# If the user has specified a trusted peer then we'll only
# ever connect to that. Otherwise we'll pick a random one from
# the known nodes
2017-01-12 06:58:35 +01:00
if state . trustedPeer :
2017-02-09 11:53:33 +01:00
with knownnodes . knownNodesLock :
peer = state . trustedPeer
knownnodes . knownNodes [ self . streamNumber ] [ peer ] = time . time ( )
2014-02-06 14:16:07 +01:00
else :
2017-02-09 11:53:33 +01:00
while not self . _stopped :
2017-02-27 23:30:31 +01:00
try :
with knownnodes . knownNodesLock :
2017-02-09 11:53:33 +01:00
peer , = random . sample ( knownnodes . knownNodes [ self . streamNumber ] , 1 )
2017-02-27 23:30:31 +01:00
priority = ( 183600 - ( time . time ( ) - knownnodes . knownNodes [ self . streamNumber ] [ peer ] ) ) / 183600 # 2 days and 3 hours
except ValueError : # no known nodes
self . stop . wait ( 1 )
continue
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) != ' none ' :
2016-06-07 12:23:47 +02:00
if peer . host . find ( " .onion " ) == - 1 :
2016-11-14 20:20:41 +01:00
priority / = 10 # hidden services have 10x priority over plain net
else :
2016-11-08 08:49:43 +01:00
# don't connect to self
2017-01-11 14:27:19 +01:00
if peer . host == BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' onionhostname ' ) and peer . port == BMConfigParser ( ) . getint ( " bitmessagesettings " , " onionport " ) :
2016-11-08 08:49:43 +01:00
continue
2016-06-07 12:23:47 +02:00
elif peer . host . find ( " .onion " ) != - 1 : # onion address and so proxy
continue
if priority < = 0.001 : # everyone has at least this much priority
priority = 0.001
if ( random . random ( ) < = priority ) :
2016-03-18 16:39:29 +01:00
break
2017-01-16 23:38:18 +01:00
self . stop . wait ( 0.01 ) # prevent CPU hogging if something is broken
2016-06-07 21:59:48 +02:00
try :
return peer
except NameError :
2017-01-12 06:58:35 +01:00
return state . Peer ( ' 127.0.0.1 ' , 8444 )
2015-11-22 16:18:59 +01:00
def stopThread ( self ) :
super ( outgoingSynSender , self ) . stopThread ( )
try :
self . sock . shutdown ( socket . SHUT_RDWR )
except :
pass
2014-02-06 14:16:07 +01:00
2013-06-22 01:49:50 +02:00
def run ( self ) :
2017-01-11 14:27:19 +01:00
while BMConfigParser ( ) . safeGetBoolean ( ' bitmessagesettings ' , ' dontconnect ' ) and not self . _stopped :
2015-11-22 16:18:59 +01:00
self . stop . wait ( 2 )
2017-01-11 14:27:19 +01:00
while BMConfigParser ( ) . safeGetBoolean ( ' bitmessagesettings ' , ' sendoutgoingconnections ' ) and not self . _stopped :
2015-11-18 16:22:17 +01:00
self . name = " outgoingSynSender "
2017-01-13 02:12:11 +01:00
maximumConnections = 1 if state . trustedPeer else BMConfigParser ( ) . safeGetInt ( ' bitmessagesettings ' , ' maxoutboundconnections ' )
2017-02-09 11:53:33 +01:00
while len ( self . selfInitiatedConnections [ self . streamNumber ] ) > = maximumConnections and not self . _stopped :
2015-11-22 16:18:59 +01:00
self . stop . wait ( 10 )
2017-01-14 23:20:15 +01:00
if state . shutdown :
2013-06-22 01:49:50 +02:00
break
2014-02-06 14:16:07 +01:00
peer = self . _getPeer ( )
2013-08-01 18:16:31 +02:00
while peer in shared . alreadyAttemptedConnectionsList or peer . host in shared . connectedHostsList :
2013-06-22 01:49:50 +02:00
# print 'choosing new sample'
2014-02-06 14:16:07 +01:00
peer = self . _getPeer ( )
2015-11-22 16:18:59 +01:00
self . stop . wait ( 1 )
2017-02-09 11:53:33 +01:00
if self . _stopped :
2015-11-22 16:18:59 +01:00
break
2013-06-24 21:51:01 +02:00
# Clear out the shared.alreadyAttemptedConnectionsList every half
2013-06-22 01:49:50 +02:00
# hour so that this program will again attempt a connection
# to any nodes, even ones it has already tried.
2017-02-28 00:12:49 +01:00
with shared . alreadyAttemptedConnectionsListLock :
if ( time . time ( ) - shared . alreadyAttemptedConnectionsListResetTime ) > 1800 :
shared . alreadyAttemptedConnectionsList . clear ( )
shared . alreadyAttemptedConnectionsListResetTime = int (
time . time ( ) )
2013-07-30 22:23:18 +02:00
shared . alreadyAttemptedConnectionsList [ peer ] = 0
2017-02-09 11:53:33 +01:00
if self . _stopped :
2016-06-18 20:35:16 +02:00
break
2016-01-26 11:54:11 +01:00
self . name = " outgoingSynSender- " + peer . host . replace ( " : " , " . " ) # log parser field separator
2016-10-20 02:49:07 +02:00
address_family = socket . AF_INET
# Proxy IP is IPv6. Unlikely but possible
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) != ' none ' :
if " : " in BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' sockshostname ' ) :
2016-10-20 02:49:07 +02:00
address_family = socket . AF_INET6
# No proxy, and destination is IPv6
elif peer . host . find ( ' : ' ) > = 0 :
2014-02-16 17:21:20 +01:00
address_family = socket . AF_INET6
2014-04-28 00:05:43 +02:00
try :
2015-11-22 16:18:59 +01:00
self . sock = socks . socksocket ( address_family , socket . SOCK_STREAM )
2014-04-28 00:05:43 +02:00
except :
"""
The line can fail on Windows systems which aren ' t
64 - bit compatiable :
File " C: \ Python27 \ lib \ socket.py " , line 187 , in __init__
_sock = _realsocket ( family , type , proto )
error : [ Errno 10047 ] An address incompatible with the requested protocol was used
So let us remove the offending address from our knownNodes file .
"""
2017-02-09 11:53:33 +01:00
with knownnodes . knownNodesLock :
try :
del knownnodes . knownNodes [ self . streamNumber ] [ peer ]
2017-02-09 21:04:07 +01:00
except KeyError :
2017-02-09 11:53:33 +01:00
pass
2017-02-08 13:41:56 +01:00
logger . debug ( ' deleting ' + str ( peer ) + ' from knownnodes.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible. ' )
2014-04-28 00:05:43 +02:00
continue
2013-06-22 01:49:50 +02:00
# This option apparently avoids the TIME_WAIT state so that we
# can rebind faster
2015-11-22 16:18:59 +01:00
self . sock . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 )
self . sock . settimeout ( 20 )
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) == ' none ' and shared . verbose > = 2 :
2015-11-18 16:22:17 +01:00
logger . debug ( ' Trying an outgoing connection to ' + str ( peer ) )
2013-06-29 19:29:35 +02:00
2013-06-22 01:49:50 +02:00
# sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2017-01-11 14:27:19 +01:00
elif BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) == ' SOCKS4a ' :
2013-06-24 21:51:01 +02:00
if shared . verbose > = 2 :
2015-11-18 16:22:17 +01:00
logger . debug ( ' (Using SOCKS4a) Trying an outgoing connection to ' + str ( peer ) )
2013-06-29 19:29:35 +02:00
2013-06-22 01:49:50 +02:00
proxytype = socks . PROXY_TYPE_SOCKS4
2017-01-11 14:27:19 +01:00
sockshostname = BMConfigParser ( ) . get (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' sockshostname ' )
2017-01-11 14:27:19 +01:00
socksport = BMConfigParser ( ) . getint (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' socksport ' )
rdns = True # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . getboolean ( ' bitmessagesettings ' , ' socksauthentication ' ) :
socksusername = BMConfigParser ( ) . get (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' socksusername ' )
2017-01-11 14:27:19 +01:00
sockspassword = BMConfigParser ( ) . get (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' sockspassword ' )
2015-11-22 16:18:59 +01:00
self . sock . setproxy (
2013-06-22 01:49:50 +02:00
proxytype , sockshostname , socksport , rdns , socksusername , sockspassword )
else :
2015-11-22 16:18:59 +01:00
self . sock . setproxy (
2013-06-22 01:49:50 +02:00
proxytype , sockshostname , socksport , rdns )
2017-01-11 14:27:19 +01:00
elif BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) == ' SOCKS5 ' :
2013-06-24 21:51:01 +02:00
if shared . verbose > = 2 :
2015-11-18 16:22:17 +01:00
logger . debug ( ' (Using SOCKS5) Trying an outgoing connection to ' + str ( peer ) )
2013-06-29 19:29:35 +02:00
2013-06-22 01:49:50 +02:00
proxytype = socks . PROXY_TYPE_SOCKS5
2017-01-11 14:27:19 +01:00
sockshostname = BMConfigParser ( ) . get (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' sockshostname ' )
2017-01-11 14:27:19 +01:00
socksport = BMConfigParser ( ) . getint (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' socksport ' )
rdns = True # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . getboolean ( ' bitmessagesettings ' , ' socksauthentication ' ) :
socksusername = BMConfigParser ( ) . get (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' socksusername ' )
2017-01-11 14:27:19 +01:00
sockspassword = BMConfigParser ( ) . get (
2013-06-22 01:49:50 +02:00
' bitmessagesettings ' , ' sockspassword ' )
2015-11-22 16:18:59 +01:00
self . sock . setproxy (
2013-06-22 01:49:50 +02:00
proxytype , sockshostname , socksport , rdns , socksusername , sockspassword )
else :
2015-11-22 16:18:59 +01:00
self . sock . setproxy (
2013-06-22 01:49:50 +02:00
proxytype , sockshostname , socksport , rdns )
try :
2015-11-22 16:18:59 +01:00
self . sock . connect ( ( peer . host , peer . port ) )
2017-01-16 23:38:18 +01:00
if self . _stopped :
self . sock . shutdown ( socket . SHUT_RDWR )
self . sock . close ( )
return
2017-02-02 15:52:32 +01:00
sendDataThreadQueue = Queue . Queue ( ) # Used to submit information to the send data thread for this connection.
2016-02-18 00:53:13 +01:00
sd = sendDataThread ( sendDataThreadQueue )
2017-03-19 22:08:00 +01:00
sd . setup ( self . sock , peer . host , peer . port , self . streamNumber )
2016-02-18 00:53:13 +01:00
sd . start ( )
rd = receiveDataThread ( )
rd . daemon = True # close the main program even if there are threads left
2015-11-22 16:18:59 +01:00
rd . setup ( self . sock ,
2013-12-30 04:36:23 +01:00
peer . host ,
peer . port ,
self . streamNumber ,
self . selfInitiatedConnections ,
2016-02-18 00:53:13 +01:00
sendDataThreadQueue ,
sd . objectHashHolderInstance )
2013-06-22 01:49:50 +02:00
rd . start ( )
2013-06-29 19:29:35 +02:00
2013-06-22 01:49:50 +02:00
sd . sendVersionMessage ( )
2016-02-18 00:53:13 +01:00
logger . debug ( str ( self ) + ' connected to ' + str ( peer ) + ' during an outgoing attempt. ' )
2013-06-22 01:49:50 +02:00
except socks . GeneralProxyError as err :
2016-10-20 01:46:48 +02:00
if err [ 0 ] [ 0 ] in [ 7 , 8 , 9 ] :
logger . error ( ' Error communicating with proxy: %s ' , str ( err ) )
2017-02-08 13:41:56 +01:00
queues . UISignalQueue . put ( (
2016-10-20 01:46:48 +02:00
' updateStatusBar ' ,
tr . _translate (
" MainWindow " , " Problem communicating with proxy: % 1. Please check your network settings. " ) . arg ( str ( err [ 0 ] [ 1 ] ) )
) )
self . stop . wait ( 1 )
continue
elif shared . verbose > = 2 :
2015-11-18 16:22:17 +01:00
logger . debug ( ' Could NOT connect to ' + str ( peer ) + ' during outgoing attempt. ' + str ( err ) )
2013-06-29 19:29:35 +02:00
2014-09-10 22:47:51 +02:00
deletedPeer = None
2017-02-08 13:41:56 +01:00
with knownnodes . knownNodesLock :
2014-09-10 22:47:51 +02:00
"""
2017-02-08 13:41:56 +01:00
It is remotely possible that peer is no longer in knownnodes . knownNodes .
2014-09-10 22:47:51 +02:00
This could happen if two outgoingSynSender threads both try to
connect to the same peer , both fail , and then both try to remove
2017-02-08 13:41:56 +01:00
it from knownnodes . knownNodes . This is unlikely because of the
2014-09-10 22:47:51 +02:00
alreadyAttemptedConnectionsList but because we clear that list once
every half hour , it can happen .
"""
2017-02-08 13:41:56 +01:00
if peer in knownnodes . knownNodes [ self . streamNumber ] :
timeLastSeen = knownnodes . knownNodes [ self . streamNumber ] [ peer ]
if ( int ( time . time ( ) ) - timeLastSeen ) > 172800 and len ( knownnodes . knownNodes [ self . streamNumber ] ) > 1000 : # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownnodes.knownNodes data-structure.
del knownnodes . knownNodes [ self . streamNumber ] [ peer ]
2014-09-10 22:47:51 +02:00
deletedPeer = peer
if deletedPeer :
2017-02-08 13:41:56 +01:00
str ( ' deleting ' + str ( peer ) + ' from knownnodes.knownNodes because it is more than 48 hours old and we could not connect to it. ' )
2013-06-29 19:29:35 +02:00
2013-06-22 01:49:50 +02:00
except socks . Socks5AuthError as err :
2017-02-08 13:41:56 +01:00
queues . UISignalQueue . put ( (
2016-04-30 11:07:27 +02:00
' updateStatusBar ' , tr . _translate (
2016-10-20 02:49:07 +02:00
" MainWindow " , " SOCKS5 Authentication problem: % 1. Please check your SOCKS5 settings. " ) . arg ( str ( err ) ) ) )
2013-06-22 01:49:50 +02:00
except socks . Socks5Error as err :
2016-10-20 01:46:48 +02:00
if err [ 0 ] [ 0 ] in [ 3 , 4 , 5 , 6 ] :
2016-06-10 12:43:37 +02:00
# this is a more bening "error": host unreachable, network unreachable, connection refused, TTL expired
2016-10-20 01:46:48 +02:00
logger . debug ( ' SOCKS5 error: %s ' , str ( err ) )
2016-06-10 12:43:37 +02:00
else :
2016-10-20 01:46:48 +02:00
logger . error ( ' SOCKS5 error: %s ' , str ( err ) )
2017-01-11 17:00:00 +01:00
if err [ 0 ] [ 0 ] == 4 or err [ 0 ] [ 0 ] == 2 :
2017-01-12 19:18:56 +01:00
state . networkProtocolAvailability [ protocol . networkType ( peer . host ) ] = False
2013-06-22 01:49:50 +02:00
except socks . Socks4Error as err :
2015-11-18 16:22:17 +01:00
logger . error ( ' Socks4Error: ' + str ( err ) )
2013-06-22 01:49:50 +02:00
except socket . error as err :
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) [ 0 : 5 ] == ' SOCKS ' :
2015-11-18 16:22:17 +01:00
logger . error ( ' Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str ( err ) )
2013-06-22 01:49:50 +02:00
else :
2017-01-12 19:18:56 +01:00
if err [ 0 ] == errno . ENETUNREACH :
state . networkProtocolAvailability [ protocol . networkType ( peer . host ) ] = False
2013-06-24 21:51:01 +02:00
if shared . verbose > = 1 :
2015-11-26 02:35:59 +01:00
logger . debug ( ' Could NOT connect to ' + str ( peer ) + ' during outgoing attempt. ' + str ( err ) )
2013-06-29 19:29:35 +02:00
2014-09-10 22:47:51 +02:00
deletedPeer = None
2017-02-08 13:41:56 +01:00
with knownnodes . knownNodesLock :
2014-09-10 22:47:51 +02:00
"""
2017-02-08 13:41:56 +01:00
It is remotely possible that peer is no longer in knownnodes . knownNodes .
2014-09-10 22:47:51 +02:00
This could happen if two outgoingSynSender threads both try to
connect to the same peer , both fail , and then both try to remove
2017-02-08 13:41:56 +01:00
it from knownnodes . knownNodes . This is unlikely because of the
2014-09-10 22:47:51 +02:00
alreadyAttemptedConnectionsList but because we clear that list once
every half hour , it can happen .
"""
2017-02-08 13:41:56 +01:00
if peer in knownnodes . knownNodes [ self . streamNumber ] :
timeLastSeen = knownnodes . knownNodes [ self . streamNumber ] [ peer ]
if ( int ( time . time ( ) ) - timeLastSeen ) > 172800 and len ( knownnodes . knownNodes [ self . streamNumber ] ) > 1000 : # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownnodes.knownNodes data-structure.
del knownnodes . knownNodes [ self . streamNumber ] [ peer ]
2014-09-10 22:47:51 +02:00
deletedPeer = peer
if deletedPeer :
2017-02-08 13:41:56 +01:00
logger . debug ( ' deleting ' + str ( peer ) + ' from knownnodes.knownNodes because it is more than 48 hours old and we could not connect to it. ' )
2013-06-29 19:29:35 +02:00
2013-06-22 01:49:50 +02:00
except Exception as err :
import traceback
2015-11-18 16:22:17 +01:00
logger . exception ( ' An exception has occurred in the outgoingSynSender thread that was not caught by other exception types: ' )
2015-11-22 16:18:59 +01:00
self . stop . wait ( 0.1 )