2019-12-12 08:47:33 +00:00
"""
2019-06-15 04:26:10 +00:00
Onionr - Private P2P Communication
2018-01-18 23:25:10 +00:00
2019-12-12 08:47:33 +00:00
Netcontroller library , used to control / work with Tor and send requests through them
"""
import os
import base64
import subprocess
import signal
import time
import multiprocessing
import platform # For windows sigkill workaround
from onionrtypes import BooleanSuccessState
import logger
from . . import getopenport
from . . import watchdog
from . import customtorrc
from . import gentorrc
from . import addbridges
from . import torbinary
from utils import identifyhome
2020-02-08 10:24:19 +00:00
from utils import box_print
2019-12-12 08:47:33 +00:00
"""
2018-01-18 23:25:10 +00:00
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 , either version 3 of the License , or
( at your option ) any later version .
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 .
You should have received a copy of the GNU General Public License
along with this program . If not , see < https : / / www . gnu . org / licenses / > .
2019-12-12 08:47:33 +00:00
"""
2019-12-10 10:46:36 +00:00
2019-08-11 18:53:38 +00:00
TOR_KILL_WAIT = 3
2019-12-12 08:47:33 +00:00
addbridges = addbridges . add_bridges
2019-09-06 09:31:13 +00:00
2018-01-18 23:25:10 +00:00
class NetController :
2020-02-08 10:24:19 +00:00
""" Handle Tor daemon and onion service setup on Tor. """
2018-03-03 07:35:13 +00:00
2018-12-09 17:29:39 +00:00
def __init__ ( self , hsPort , apiServerIP = ' 127.0.0.1 ' ) :
2019-03-29 03:33:14 +00:00
# set data dir
2019-07-15 03:01:56 +00:00
self . dataDir = identifyhome . identify_home ( )
2019-12-12 08:47:33 +00:00
self . socksPort = getopenport . get_open_port ( )
2018-09-26 04:58:11 +00:00
self . torConfigLocation = self . dataDir + ' torrc '
2018-01-18 23:25:10 +00:00
self . readyState = False
2018-01-19 09:16:38 +00:00
self . hsPort = hsPort
2018-01-27 08:43:36 +00:00
self . _torInstnace = ' '
2018-01-19 21:28:34 +00:00
self . myID = ' '
2018-12-09 17:29:39 +00:00
self . apiServerIP = apiServerIP
2019-12-12 08:47:33 +00:00
self . torBinary = torbinary . tor_binary ( )
2018-09-05 04:06:17 +00:00
2019-12-12 08:47:33 +00:00
def startTor ( self , gen_torrc = True ) - > BooleanSuccessState :
"""
2018-02-04 03:44:29 +00:00
Start Tor with onion service on port 80 & socks proxy on random port
2019-12-12 08:47:33 +00:00
"""
2019-08-10 01:04:56 +00:00
if gen_torrc :
2019-12-12 08:47:33 +00:00
gentorrc . generate_torrc ( self , self . apiServerIP )
2018-03-03 07:35:13 +00:00
2018-01-28 01:53:24 +00:00
if os . path . exists ( ' ./tor ' ) :
2018-09-05 04:06:17 +00:00
self . torBinary = ' ./tor '
2018-03-03 07:35:13 +00:00
elif os . path . exists ( ' /usr/bin/tor ' ) :
2018-09-05 04:06:17 +00:00
self . torBinary = ' /usr/bin/tor '
2018-01-28 01:53:24 +00:00
else :
2018-09-05 04:06:17 +00:00
self . torBinary = ' tor '
2018-03-03 07:35:13 +00:00
2018-01-28 01:53:24 +00:00
try :
2018-09-05 04:06:17 +00:00
tor = subprocess . Popen ( [ self . torBinary , ' -f ' , self . torConfigLocation ] , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
2018-01-28 01:53:24 +00:00
except FileNotFoundError :
2019-06-19 20:29:27 +00:00
logger . fatal ( " Tor was not found in your path or the Onionr directory. Please install Tor and try again. " , terminal = True )
2019-10-08 22:26:44 +00:00
return False
2018-02-22 09:33:30 +00:00
else :
# Test Tor Version
2019-12-12 08:47:33 +00:00
torVersion = subprocess . Popen ( [ self . torBinary , ' --version ' ] ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE )
2018-02-22 09:33:30 +00:00
for line in iter ( torVersion . stdout . readline , b ' ' ) :
if ' Tor 0.2. ' in line . decode ( ) :
2019-06-19 20:29:27 +00:00
logger . fatal ( ' Tor 0.3+ required ' , terminal = True )
2019-10-08 22:26:44 +00:00
return False
2018-02-22 09:33:30 +00:00
torVersion . kill ( )
2018-01-20 00:59:05 +00:00
# wait for tor to get to 100% bootstrap
2018-05-18 06:22:16 +00:00
try :
for line in iter ( tor . stdout . readline , b ' ' ) :
2020-02-08 10:24:19 +00:00
for word in ( ' bootstrapped ' , ' % ' ) :
if word not in line . decode ( ) . lower ( ) :
break
else :
if ' 100 ' not in line . decode ( ) :
logger . info ( line . decode ( ) . strip ( ) , terminal = True )
2019-06-14 18:02:02 +00:00
if ' bootstrapped 100 ' in line . decode ( ) . lower ( ) :
2019-06-19 20:29:27 +00:00
logger . info ( line . decode ( ) )
2018-05-18 06:22:16 +00:00
break
2019-06-14 18:02:02 +00:00
elif ' opening socks listener ' in line . decode ( ) . lower ( ) :
2018-05-18 06:22:16 +00:00
logger . debug ( line . decode ( ) . replace ( ' \n ' , ' ' ) )
2019-10-08 22:26:44 +00:00
else :
if ' err ' in line . decode ( ) :
logger . error ( line . decode ( ) . replace ( ' \n ' , ' ' ) )
elif ' warn ' in line . decode ( ) :
logger . warn ( line . decode ( ) . replace ( ' \n ' , ' ' ) )
else :
logger . debug ( line . decode ( ) . replace ( ' \n ' , ' ' ) )
2018-05-18 06:22:16 +00:00
else :
2019-06-19 20:29:27 +00:00
logger . fatal ( ' Failed to start Tor. Maybe a stray instance of Tor used by Onionr is still running? This can also be a result of file permissions being too open ' , terminal = True )
2018-05-18 06:22:16 +00:00
return False
except KeyboardInterrupt :
2019-07-20 00:01:16 +00:00
logger . fatal ( ' Got keyboard interrupt. Onionr will exit soon. ' , timestamp = False , terminal = True )
2018-01-28 01:53:24 +00:00
return False
2019-03-29 03:33:14 +00:00
2018-12-09 17:29:39 +00:00
try :
myID = open ( self . dataDir + ' hs/hostname ' , ' r ' )
self . myID = myID . read ( ) . replace ( ' \n ' , ' ' )
myID . close ( )
except FileNotFoundError :
self . myID = " "
2018-03-03 07:35:13 +00:00
2019-12-12 08:47:33 +00:00
with open ( self . dataDir + ' torPid.txt ' , ' w ' ) as tor_pid_file :
tor_pid_file . write ( str ( tor . pid ) )
2018-02-04 03:44:29 +00:00
2019-12-12 08:47:33 +00:00
multiprocessing . Process ( target = watchdog . watchdog ,
args = [ os . getpid ( ) , tor . pid ] ) . start ( )
2020-02-12 01:49:44 +00:00
logger . info ( ' Finished starting Tor. ' , terminal = True )
self . readyState = True
2018-01-28 01:53:24 +00:00
return True
2018-02-04 03:44:29 +00:00
2018-01-27 08:43:36 +00:00
def killTor ( self ) :
2019-12-12 08:47:33 +00:00
"""
2018-02-04 03:44:29 +00:00
Properly kill tor based on pid saved to file
2019-12-12 08:47:33 +00:00
"""
2018-03-03 07:35:13 +00:00
2018-01-27 08:43:36 +00:00
try :
2018-09-26 04:58:11 +00:00
pid = open ( self . dataDir + ' torPid.txt ' , ' r ' )
2018-01-27 08:43:36 +00:00
pidN = pid . read ( )
pid . close ( )
except FileNotFoundError :
return
2018-03-03 07:35:13 +00:00
2018-01-27 08:43:36 +00:00
try :
int ( pidN )
2019-12-12 08:47:33 +00:00
except ValueError :
2018-01-27 08:43:36 +00:00
return
2018-03-03 07:35:13 +00:00
2018-02-21 02:44:56 +00:00
try :
2019-06-15 01:31:01 +00:00
try :
os . kill ( int ( pidN ) , signal . SIGTERM )
except PermissionError :
# seems to happen on win 10
pass
2018-09-26 04:58:11 +00:00
os . remove ( self . dataDir + ' torPid.txt ' )
2018-02-21 02:44:56 +00:00
except ProcessLookupError :
pass
except FileNotFoundError :
pass
2020-01-21 08:34:15 +00:00
2019-09-05 09:40:31 +00:00
try :
time . sleep ( TOR_KILL_WAIT )
except KeyboardInterrupt :
pass
2019-08-28 02:15:56 +00:00
if ' windows ' == platform . system ( ) . lower ( ) :
2019-12-12 08:47:33 +00:00
os . system ( f ' taskkill /PID { pidN } /F ' )
2019-08-28 02:15:56 +00:00
time . sleep ( 0.5 )
return
2019-08-11 18:53:38 +00:00
try :
os . kill ( int ( pidN ) , signal . SIGKILL )
2019-12-12 08:47:33 +00:00
except ( ProcessLookupError , PermissionError ) :
2019-08-11 18:53:38 +00:00
pass
2019-10-08 22:26:44 +00:00
try :
os . remove ( self . dataDir + ' tordata/lock ' )
except FileNotFoundError :
pass