Compare commits

..

4 Commits

Author SHA1 Message Date
emdee
a3ad33495a Merge for tox_irc_sync 2022-10-27 06:50:47 +00:00
emdee
564e24fba5 Added onion support 2022-10-26 08:58:09 +00:00
emdee
14c7156308 First refactor for tox_irc_sync 2022-10-23 22:14:35 +00:00
emdee
048caad578 bootstrap_iNmapInfo 2022-10-18 00:16:56 +00:00
4 changed files with 401 additions and 160 deletions

View File

@@ -81,4 +81,7 @@ Others include:
This was the basis for the TokTok/py-toxcore-c code until recently. This was the basis for the TokTok/py-toxcore-c code until recently.
To our point of view, the ability of CTYPEs to follow code in the To our point of view, the ability of CTYPEs to follow code in the
debugger is crucial. debugger is a crucial advantage.
Work on this project is suspended until the
[MultiDevice](https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC) problem is solved. Fork me!

View File

@@ -416,7 +416,7 @@ class Tox:
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def self_get_toxid(self, address=None): def self_get_toxid(self, address=None):
return self_get_address(self, address=None) return self.self_get_address(address)
def self_get_address(self, address=None): def self_get_address(self, address=None):
""" """
@@ -1896,7 +1896,7 @@ class Tox:
raise ToxError(f"group_new {error.value}") raise ToxError(f"group_new {error.value}")
return result return result
def group_join(self, chat_id, password, nick, status): def group_join(self, chat_id, password, nick, status=''):
"""Joins a group chat with specified Chat ID. """Joins a group chat with specified Chat ID.
This function creates a new group chat object, adds it to the This function creates a new group chat object, adds it to the
@@ -1938,7 +1938,7 @@ class Tox:
byref(error)) byref(error))
if error.value: if error.value:
LOG_ERROR(f"group_join {error.value}") LOG_ERROR(f"group_join {error.value}")
raise ToxError("group_join {error.value}") raise ToxError(f"group_join {error.value}")
return result return result
def group_reconnect(self, group_number): def group_reconnect(self, group_number):
@@ -2147,8 +2147,8 @@ class Tox:
result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, group_number, result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, group_number,
key, byref(error)) key, byref(error))
if error.value: if error.value:
LOG_ERROR(f" {error.value}") LOG_ERROR(f" {TOX_ERR_FRIEND_GET_PUBLIC_KEY[error.value]}")
raise ToxError(f" {error.value}") raise ToxError(f"{TOX_ERR_FRIEND_GET_PUBLIC_KEY[error.value]}")
return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
@@ -2436,6 +2436,7 @@ class Tox:
return result return result
def groups_get_list(self): def groups_get_list(self):
raise NotImplementedError('tox_groups_get_list')
groups_list_size = self.group_get_number_groups() groups_list_size = self.group_get_number_groups()
groups_list = create_string_buffer(sizeof(c_uint32) * groups_list_size) groups_list = create_string_buffer(sizeof(c_uint32) * groups_list_size)
groups_list = POINTER(c_uint32)(groups_list) groups_list = POINTER(c_uint32)(groups_list)
@@ -2661,7 +2662,7 @@ class Tox:
raise ToxError(f"group_send_private_message {TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE[error.value]}") raise ToxError(f"group_send_private_message {TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE[error.value]}")
return result return result
def group_send_message(self, group_number, type, message): def group_send_message(self, group_number, type_, message):
""" """
Send a text chat message to the group. Send a text chat message to the group.
@@ -2673,7 +2674,7 @@ class Tox:
then reassemble the fragments. Messages may not be empty. then reassemble the fragments. Messages may not be empty.
:param group_number: The group number of the group the message is intended for. :param group_number: The group number of the group the message is intended for.
:param type: Message type (normal, action, ...). :param type_: Message type (normal, action, ...).
:param message: A non-NULL pointer to the first element of a byte array containing the message text. :param message: A non-NULL pointer to the first element of a byte array containing the message text.
:return True on success. :return True on success.
@@ -2687,7 +2688,7 @@ class Tox:
# bool tox_group_send_message(const Tox *tox, uint32_t group_number, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t *message_id, Tox_Err_Group_Send_Message *error) # bool tox_group_send_message(const Tox *tox, uint32_t group_number, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t *message_id, Tox_Err_Group_Send_Message *error)
result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer,
group_number, group_number,
type, type_,
message, message,
len(message), len(message),
# dunno # dunno
@@ -2794,7 +2795,7 @@ class Tox:
# result = f(byref(error)) # result = f(byref(error))
# return result # return result
def group_invite_accept(self, invite_data, friend_number, nick, status, password=''): def group_invite_accept(self, invite_data, friend_number, nick, password=None):
""" """
Accept an invite to a group chat that the client previously received from a friend. The invite Accept an invite to a group chat that the client previously received from a friend. The invite
is only valid while the inviter is present in the group. is only valid while the inviter is present in the group.
@@ -3080,9 +3081,12 @@ class Tox:
Set the callback for the `group_moderation` event. Pass NULL to unset. Set the callback for the `group_moderation` event. Pass NULL to unset.
This event is triggered when a moderator or founder executes a moderation event. This event is triggered when a moderator or founder executes a moderation event.
(tox_data->tox, group_number, source_peer_number, target_peer_number,
(Tox_Group_Mod_Event)mod_type, tox_data->user_data);
TOX_GROUP_MOD_EVENT = [0,1,2,3,4] TOX_GROUP_MOD_EVENT['MODERATOR']
""" """
LOG_DEBUG(f"callback_group_moderation") # LOG_DEBUG(f"callback_group_moderation")
if callback is None: if callback is None:
self.group_moderation_cb = None self.group_moderation_cb = None
LOG_DEBUG(f"tox_callback_group_moderation") LOG_DEBUG(f"tox_callback_group_moderation")
@@ -3117,5 +3121,3 @@ class Tox:
raise ToxError("tox_group_toggle_set_ignore {error.value}") raise ToxError("tox_group_toggle_set_ignore {error.value}")
return result return result
# ToDo from JF/toxcore
# tox_group_set_ignore

View File

@@ -4,6 +4,7 @@ import os
import sys import sys
import argparse import argparse
import re import re
import traceback
import logging import logging
import shutil import shutil
import json import json
@@ -14,9 +15,6 @@ import unittest
from random import Random from random import Random
random = Random() random = Random()
from PyQt5 import QtCore, QtWidgets
from qtpy.QtWidgets import QApplication
try: try:
import coloredlogs import coloredlogs
if 'COLOREDLOGS_LEVEL_STYLES' not in os.environ: if 'COLOREDLOGS_LEVEL_STYLES' not in os.environ:
@@ -24,6 +22,10 @@ try:
# https://pypi.org/project/coloredlogs/ # https://pypi.org/project/coloredlogs/
except ImportError as e: except ImportError as e:
coloredlogs = False coloredlogs = False
try:
import stem
except ImportError as e:
stem = False
import wrapper import wrapper
from wrapper.toxcore_enums_and_consts import TOX_CONNECTION, TOX_USER_STATUS from wrapper.toxcore_enums_and_consts import TOX_CONNECTION, TOX_USER_STATUS
@@ -55,14 +57,15 @@ else:
connection_opts={'IO': 'TCP', 'PORT': 6666} connection_opts={'IO': 'TCP', 'PORT': 6666}
intf = Mserver.ServerInterface(connection_opts=connection_opts) intf = Mserver.ServerInterface(connection_opts=connection_opts)
dbg_opts = { 'interface': intf } dbg_opts = { 'interface': intf }
print('Starting TCP server listening on port 6666.') print(f'Starting TCP server listening on port 6666.')
debug(dbg_opts=dbg_opts) debug(dbg_opts=dbg_opts)
return return
# self._audio_thread.isAlive # self._audio_thread.isAlive
iTHREAD_TIMEOUT = 1 iTHREAD_TIMEOUT = 1
iTHREAD_SLEEP = 1 iTHREAD_SLEEP = 1
iTHREAD_JOINS = 5 iTHREAD_JOINS = 8
iNODES=6
lToxSamplerates = [8000, 12000, 16000, 24000, 48000] lToxSamplerates = [8000, 12000, 16000, 24000, 48000]
lToxSampleratesK = [8, 12, 16, 24, 48] lToxSampleratesK = [8, 12, 16, 24, 48]
@@ -70,6 +73,7 @@ lBOOLEANS = [
'local_discovery_enabled', 'local_discovery_enabled',
'udp_enabled', 'udp_enabled',
'ipv6_enabled', 'ipv6_enabled',
'trace_enabled',
'compact_mode', 'compact_mode',
'allow_inline', 'allow_inline',
'notifications', 'notifications',
@@ -87,6 +91,7 @@ sTOX_VERSION = "1000002018"
bHAVE_NMAP = shutil.which('nmap') bHAVE_NMAP = shutil.which('nmap')
bHAVE_JQ = shutil.which('jq') bHAVE_JQ = shutil.which('jq')
bHAVE_BASH = shutil.which('bash') bHAVE_BASH = shutil.which('bash')
bHAVE_TORR = shutil.which('tor-resolve')
lDEAD_BS = [ lDEAD_BS = [
# [notice] Have tried resolving or connecting to address # [notice] Have tried resolving or connecting to address
@@ -94,12 +99,15 @@ lDEAD_BS = [
'104.244.74.69', '104.244.74.69',
'172.93.52.70', '172.93.52.70',
'tox.abilinski.com', 'tox.abilinski.com',
'tox.novg.net',
# Failed to resolve "tox3.plastiras.org". # Failed to resolve "tox3.plastiras.org".
"tox3.plastiras.org", "tox3.plastiras.org",
] ]
def assert_main_thread(): def assert_main_thread():
from PyQt5 import QtCore, QtWidgets
from qtpy.QtWidgets import QApplication
# this "instance" method is very useful! # this "instance" method is very useful!
app_thread = QtWidgets.QApplication.instance().thread() app_thread = QtWidgets.QApplication.instance().thread()
curr_thread = QtCore.QThread.currentThread() curr_thread = QtCore.QThread.currentThread()
@@ -249,7 +257,7 @@ def oMainArgparser(_=None):
logfile = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'tests_toxygen.log') logfile = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'tests_toxygen.log')
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('--proxy_host', '--proxy-host', type=str, parser.add_argument('--proxy_host', '--proxy-host', type=str,
default='', default='',
help='proxy host') help='proxy host')
@@ -264,24 +272,29 @@ def oMainArgparser(_=None):
parser.add_argument('--ipv6_enabled', type=str, default=bIpV6, parser.add_argument('--ipv6_enabled', type=str, default=bIpV6,
choices=lIpV6Choices, choices=lIpV6Choices,
help=f"En/Disable ipv6 - default {bIpV6}") help=f"En/Disable ipv6 - default {bIpV6}")
parser.add_argument('--trace_enabled',type=str,
default='True' if os.environ.get('DEBUG') else 'False',
choices=['True','False'],
help='Debugging from toxcore logger_trace or env DEBUG=1')
parser.add_argument('--download_nodes_list', type=str, default='False', parser.add_argument('--download_nodes_list', type=str, default='False',
choices=['True', 'False'], choices=['True', 'False'],
help='Download nodes list') help='Download nodes list')
parser.add_argument('--nodes_json', type=str, parser.add_argument('--nodes_json', type=str,
default='') default='')
parser.add_argument('--network', type=str, parser.add_argument('--network', type=str,
choices=['old', 'new', 'local', 'newlocal'], choices=['old', 'main', 'local'],
default='new') default='main')
parser.add_argument('--download_nodes_url', type=str, parser.add_argument('--download_nodes_url', type=str,
default='https://nodes.tox.chat/json') default='https://nodes.tox.chat/json')
parser.add_argument('--logfile', default=logfile, parser.add_argument('--logfile', default=logfile,
help='Filename for logging') help='Filename for logging - start with + for stdout too')
parser.add_argument('--loglevel', default=logging.INFO, type=int, parser.add_argument('--loglevel', default=logging.INFO, type=int,
# choices=[logging.info,logging.trace,logging.debug,logging.error] # choices=[logging.info,logging.trace,logging.debug,logging.error]
help='Threshold for logging (lower is more) default: 20') help='Threshold for logging (lower is more) default: 20')
parser.add_argument('--tcp_port', '--tcp-port', default=0, type=int, parser.add_argument('--tcp_port', '--tcp-port', default=0, type=int,
help='tcp port') help='tcp port')
parser.add_argument('--mode', type=int, default=2, parser.add_argument('--mode', type=int, default=2,
choices=[0,1,2],
help='Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0') help='Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0')
parser.add_argument('--sleep', type=str, default='time', parser.add_argument('--sleep', type=str, default='time',
# could expand this to tk, gtk, gevent... # could expand this to tk, gtk, gevent...
@@ -289,24 +302,68 @@ def oMainArgparser(_=None):
help='Sleep method - one of qt, gevent , time') help='Sleep method - one of qt, gevent , time')
return parser return parser
def vOargsToxPreamble(oArgs, Tox, ToxTest): def vSetupLogging(oArgs):
global LOG
add = None
kwargs = dict(level=oArgs.loglevel, kwargs = dict(level=oArgs.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if oArgs.logfile: if oArgs.logfile:
add = oArgs.logfile.startswith('+')
sub = oArgs.logfile.startswith('-')
if add or sub:
oArgs.logfile = oArgs.logfile[1:]
kwargs['filename'] = oArgs.logfile kwargs['filename'] = oArgs.logfile
if coloredlogs:
# https://pypi.org/project/coloredlogs/
aKw = dict(level=oArgs.loglevel,
logger=LOG,
fmt='%(name)s %(levelname)s %(message)s'
)
coloredlogs.install(**aKw)
if add:
oHandler = logging.FileHandler(oArgs.logfile)
LOG.addHandler(oHandler)
else:
logging.basicConfig(**kwargs) logging.basicConfig(**kwargs)
if add:
oHandler = logging.StreamHandler(sys.stdout)
LOG.addHandler(oHandler)
methods = set([x for x in dir(Tox) if not x[0].isupper() logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S')
and not x[0] == '_']) logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S'
docs = "".join([getattr(ToxTest, x).__doc__ for x in dir(ToxTest) logging._defaultFormatter.default_msec_format = ''
if getattr(ToxTest, x).__doc__ is not None]) LOG.info(f"Setting loglevel to {oArgs.loglevel!s}")
tested = set(re.findall(r't:(.*?)\n', docs))
not_tested = methods.difference(tested)
logging.info('Test Coverage: %.2f%%' % (len(tested) * 100.0 / len(methods))) def setup_logging(oArgs):
if len(not_tested): global LOG
logging.info('Not tested:\n %s' % "\n ".join(sorted(list(not_tested)))) if coloredlogs:
aKw = dict(level=oArgs.loglevel,
logger=LOG,
fmt='%(name)s %(levelname)s %(message)s')
if oArgs.logfile:
oFd = open(oArgs.logfile, 'wt')
setattr(oArgs, 'log_oFd', oFd)
aKw['stream'] = oFd
coloredlogs.install(**aKw)
if oArgs.logfile:
oHandler = logging.StreamHandler(stream=sys.stdout)
LOG.addHandler(oHandler)
else:
aKw = dict(level=oArgs.loglevel,
format='%(name)s %(levelname)-4s %(message)s')
if oArgs.logfile:
aKw['filename'] = oArgs.logfile
logging.basicConfig(**aKw)
logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S')
logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S'
logging._defaultFormatter.default_msec_format = ''
LOG.setLevel(oArgs.loglevel)
# LOG.trace = lambda l: LOG.log(0, repr(l))
LOG.info(f"Setting loglevel to {oArgs.loglevel!s}")
def signal_handler(num, f): def signal_handler(num, f):
from trepan.interfaces import server as Mserver from trepan.interfaces import server as Mserver
@@ -371,6 +428,8 @@ DEFAULT_NODES_COUNT = 8
global aNODES global aNODES
aNODES = {} aNODES = {}
import functools
# @functools.lru_cache(maxsize=12)
def generate_nodes(oArgs=None, def generate_nodes(oArgs=None,
nodes_count=DEFAULT_NODES_COUNT, nodes_count=DEFAULT_NODES_COUNT,
ipv='ipv4', ipv='ipv4',
@@ -378,11 +437,15 @@ def generate_nodes(oArgs=None,
global aNODES global aNODES
sKey = ipv sKey = ipv
sKey += ',0' if udp_not_tcp else ',1' sKey += ',0' if udp_not_tcp else ',1'
if sKey in aNODES: return aNODES[sKey] if sKey in aNODES and aNODES[sKey]:
return aNODES[sKey]
sFile = _get_nodes_path(oArgs=oArgs) sFile = _get_nodes_path(oArgs=oArgs)
aNODES[sKey] = generate_nodes_from_file(sFile, assert os.path.exists(sFile), sFile
lNodes = generate_nodes_from_file(sFile,
nodes_count=nodes_count, nodes_count=nodes_count,
ipv=ipv, udp_not_tcp=udp_not_tcp) ipv=ipv, udp_not_tcp=udp_not_tcp)
assert lNodes
aNODES[sKey] = lNodes
return aNODES[sKey] return aNODES[sKey]
aNODES_CACHE = {} aNODES_CACHE = {}
@@ -396,7 +459,7 @@ I had a conversation with @irungentoo on IRC about whether we really need to cal
""" """
global aNODES_CACHE global aNODES_CACHE
key = sFile +',' +ipv key = ipv
key += ',0' if udp_not_tcp else ',1' key += ',0' if udp_not_tcp else ',1'
if key in aNODES_CACHE: if key in aNODES_CACHE:
sorted_nodes = aNODES_CACHE[key] sorted_nodes = aNODES_CACHE[key]
@@ -422,12 +485,14 @@ I had a conversation with @irungentoo on IRC about whether we really need to cal
nodes = [] nodes = []
elts = [(node[ipv], node['tcp_ports'], node['public_key'],) \ elts = [(node[ipv], node['tcp_ports'], node['public_key'],) \
for node in json_nodes if node[ipv] != 'NONE' \ for node in json_nodes if node[ipv] != 'NONE' \
and node['last_ping'] > 0
and node["status_tcp"] in [True, "true"] and node["status_tcp"] in [True, "true"]
] ]
for (ipv4, ports, public_key,) in elts: for (ipv, ports, public_key,) in elts:
for port in ports: for port in ports:
nodes += [(ipv4, port, public_key)] nodes += [(ipv, port, public_key)]
if not nodes:
LOG.warn(f'empty generate_nodes from {sFile} {json_nodes!r}')
return []
sorted_nodes = nodes sorted_nodes = nodes
aNODES_CACHE[key] = sorted_nodes aNODES_CACHE[key] = sorted_nodes
@@ -454,93 +519,302 @@ def bootstrap_local(self, elts, lToxes):
else: else:
iRet = os.system("netstat -nle4|grep -q :33") iRet = os.system("netstat -nle4|grep -q :33")
if iRet > 0: if iRet > 0:
LOG.warn('bootstraping local No local DHT running') LOG.warn(f'bootstraping local No local DHT running')
LOG.info('bootstraping local') LOG.info(f'bootstraping local')
return bootstrap_good(self, elts, lToxes) return bootstrap_udp(self, elts, lToxes)
def sDNSClean(l):
# list(set(l).difference(lBAD_NS))
return [elt for elt in l if elt not in lDEAD_BS]
def sMapaddressResolv(target, iPort=9051):
if stem == False: return ''
from stem import StreamStatus
from stem.control import EventType, Controller
import getpass
if os.path.exists('/var/run/tor/control'):
controller = Controller.from_socket_file(path='/var/run/tor/control')
else:
controller = Controller.from_port(port=iPort)
try:
sys.stdout.flush()
p = getpass.unix_getpass(prompt='Controller Password: ', stream=sys.stderr)
controller.authenticate(p)
map_dict = {"0.0.0.0": target}
map_ret = controller.map_address(map_dict)
return map_ret
except Exception as e:
LOG.exception(e)
finally:
del controller
def sTorResolve(target,
verbose=False,
sHost='127.0.0.1',
iPort=9050,
SOCK_TIMEOUT_SECONDS=10.0,
SOCK_TIMEOUT_TRIES=3,
):
MAX_INFO_RESPONSE_PACKET_LENGTH = 8
if verbose:
os.system("tor-resolve -4 " +target)
# os.system("strace tor-resolve -4 "+target+" 2>&1|grep '^sen\|^rec'")
seb = b"\o004\o360\o000\o000\o000\o000\o000\o001\o000"
seb = b"\x04\xf0\x00\x00\x00\x00\x00\x01\x00"
seb += bytes(target, 'US-ASCII') + b"\x00"
assert len(seb) == 10+len(target), str(len(seb))+repr(seb)
LOG.debug(f"0 Sending {len(seb)} to The TOR proxy {seb}")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((sHost, iPort))
sock.settimeout(SOCK_TIMEOUT_SECONDS)
oRet = sock.sendall(seb)
i = 0
data = ''
while i < SOCK_TIMEOUT_TRIES:
i += 1
sleep(3)
lReady = select.select([sock.fileno()], [], [],
SOCK_TIMEOUT_SECONDS)
if not lReady[0]: continue
try:
flags=socket.MSG_WAITALL
data = sock.recv(MAX_INFO_RESPONSE_PACKET_LENGTH, flags)
except socket.timeout:
LOG.warn("4 The TOR proxy " \
+repr((sHost, iPort)) \
+" didnt reply in " + str(SOCK_TIMEOUT_SECONDS) + " sec."
+" #" +str(i))
except Exception as e:
LOG.error("4 The TOR proxy " \
+repr((sHost, iPort)) \
+" errored with " + str(e)
+" #" +str(i))
sock.close()
raise SystemExit(4)
else:
if len(data) > 0: break
if len(data) == 0:
if i > SOCK_TIMEOUT_TRIES:
sLabel = "5 No reply #"
else:
sLabel = "5 No data #"
LOG.info(sLabel +f"{i} from {sHost} {iPort}" )
sock.close()
raise SystemExit(5)
assert len(data) >= 8
packet_sf = data[1]
if packet_sf == 90:
# , "%d" % packet_sf
assert f"{packet_sf}" == "90", f"packet_sf = {packet_sf}"
return f"{data[4]}.{data[5]}.{data[6]}.{data[7]}"
else:
# 91
LOG.warn(f"tor-resolve failed for {target} from {sHost} {iPort}" )
return ''
def sDNSLookup(host):
ipv = 0
if host in lDEAD_BS:
LOG.warn(f"address skipped because in lDEAD_BS {host}")
return ''
# return host
try:
s = host.replace('.','')
int(s)
except:
try:
s = host.replace(':','')
int(s)
except: pass
else:
ipv = 6
else:
ipv = 4
if ipv > 0:
# LOG.debug(f"{ipv} IP address {host}")
return host
ip = ''
if host.endswith('.tox') or host.endswith('.tox.onion'):
if stem:
ip = sMapaddressResolv(host)
if ip: return ip
ip = sTorResolve(host)
if ip: return ip
if not bHAVE_TORR:
LOG.warn(f"onion address skipped because no tor-resolve {host}")
return ''
try:
sOut = f"/tmp/TR{os.getpid()}.log"
i = os.system(f"tor-resolve -4 {host} > {sOUT}")
if not i:
LOG.warn(f"onion address skipped because tor-resolve on {host}")
return ''
ip = open(sOut, 'rt').read()
if ip.endswith('failed.'):
LOG.warn(f"onion address skipped because tor-resolve failed on {host}")
return ''
LOG.debug(f"onion address tor-resolve {ip} on {host}")
return ip
except:
pass
else:
try:
ip = socket.gethostbyname(host)
return ip
except:
# drop through
pass
if ip == '':
try:
sOut = f"/tmp/TR{os.getpid()}.log"
i = os.system(f"dig {host}|grep ^{host}|sed -e 's/.* //'> {sOUT}")
if not i:
LOG.warn(f"address skipped because dig failed on {host}")
return ''
ip = open(sOut, 'rt').read().strip()
LOG.debug(f"address dig {ip} on {host}")
return ip
except:
ip = host
LOG.debug(f'sDNSLookup {host} -> {ip}')
return ip
def bootstrap_good(lelts, lToxes): def bootstrap_good(lelts, lToxes):
LOG.info('bootstraping udp') return bootstrap_udp(lelts, lToxes)
def bootstrap_udp(lelts, lToxes):
for elt in lToxes: for elt in lToxes:
LOG.debug(f'DHT bootstraping {len(lelts)}')
for largs in lelts: for largs in lelts:
host, port, key = largs host, port, key = largs
if largs[0] in lDEAD_BS: continue ip = sDNSLookup(host)
try: if not ip:
host = socket.gethostbyname(largs[0]) LOG.warn(f'bootstrap_udp to {host} did not resolve')
except:
continue continue
assert len(key) == 64, key assert len(key) == 64, key
if type(port) == str: if type(port) == str:
port = int(port) port = int(port)
try: try:
oRet = elt.bootstrap(host, oRet = elt.bootstrap(ip,
port, port,
largs[2]) key)
except Exception as e: except Exception as e:
LOG.error('bootstrap to ' +host +':' +str(largs[1]) \ LOG.error(f'bootstrap to {host}:' +str(largs[1]) \
+' ' +str(e)) +' ' +str(e))
continue continue
if not oRet: if not oRet:
LOG.warn('bootstrap failed to ' +host +' : ' +str(oRet)) LOG.warn(f'bootstrap failed to {host} : ' +str(oRet))
elif elt.self_get_connection_status() != TOX_CONNECTION['NONE']: elif elt.self_get_connection_status() != TOX_CONNECTION['NONE']:
LOG.info('bootstrap to ' +host +' connected') LOG.info(f'bootstrap to {host} connected')
break break
else: else:
LOG.debug('bootstrap to ' +host +' not connected') LOG.debug(f'bootstrap to {host} not connected')
pass
def bootstrap_tcp(lelts, lToxes): def bootstrap_tcp(lelts, lToxes):
LOG.info('bootstraping tcp')
for elt in lToxes: for elt in lToxes:
LOG.debug(f'Relay bootstapping {len(lelts)}')
for largs in lelts: for largs in lelts:
if largs[0] in lDEAD_BS: continue host, port, key = largs
ip = sDNSLookup(host)
if not ip:
LOG.warn(f'bootstrap_tcp to {host} did not resolve {ip}')
# continue
ip = host
assert len(key) == 64, key
if type(port) == str:
port = int(port)
try: try:
host = socket.gethostbyname(largs[0]) oRet = elt.add_tcp_relay(ip,
except: port,
continue key)
try:
oRet = elt.add_tcp_relay(host,
int(largs[1]),
largs[2])
except Exception as e: except Exception as e:
LOG.error('bootstrap_tcp to ' +largs[0] +' : ' +str(e)) LOG.error(f'bootstrap_tcp to {host} : ' +str(e))
continue continue
if not oRet: if not oRet:
LOG.warn('bootstrap_tcp failed to ' +largs[0] +' : ' +str(oRet)) LOG.warn(f'bootstrap_tcp failed to {host} : ' +str(oRet))
elif elt.self_get_connection_status() != TOX_CONNECTION['NONE']: elif elt.self_get_connection_status() != TOX_CONNECTION['NONE']:
LOG.info('bootstrap_tcp to ' +largs[0] +' connected') LOG.info(f'bootstrap_tcp to {host} connected')
break break
else: else:
LOG.debug('bootstrap_tcp to ' +largs[0] +' not connected') LOG.debug(f'bootstrap_tcp to {host} but not connected')
pass
def setup_logging(oArgs): def iNmapInfo(sProt, sHost, sPort, key=None, environ=None, bTest=False):
global LOG sFile = os.path.join("/tmp", f"{sHost}.{os.getpid()}.nmap")
if coloredlogs: if sProt in ['socks', 'socks5', 'tcp4']:
aKw = dict(level=oArgs.loglevel, cmd = f"nmap -Pn -n -sT -p T:{sPort} {sHost} | grep /tcp >{sFile}"
logger=LOG,
fmt='%(name)s %(levelname)s %(message)s')
if False and oArgs.logfile:
oFd = open(oArgs.logfile, 'wt')
setattr(oArgs, 'log_oFd', oFd)
aKw['stream'] = oFd
coloredlogs.install(**aKw)
# logging._defaultFormatter = coloredlogs.Formatter(datefmt='%m-%d %H:%M:%S')
if oArgs.logfile:
oHandler = logging.StreamHandler(stream=sys.stdout)
LOG.addHandler(oHandler)
else: else:
aKw = dict(level=oArgs.loglevel, cmd = f"nmap -Pn -n -sU -p U:{sPort} {sHost} | grep /tcp >{sFile}"
format='%(name)s %(levelname)-4s %(message)s') iRet = os.system(cmd)
if oArgs.logfile: LOG.debug(f"iNmapInfo cmd={cmd} {iRet}")
aKw['filename'] = oArgs.logfile if iRet != 0:
logging.basicConfig(**aKw) return iRet
assert os.path.exists(sFile), sFile
with open(sFile, 'rt') as oFd:
l = oFd.readlines()
assert len(l)
s = '\n'.join([s.strip() for s in l])
LOG.debug(f"iNmapInfo: {s}")
return 0
def bootstrap_iNmapInfo(lElts, oArgs, bIS_LOCAL=False, iNODES=iNODES):
logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S') if not bIS_LOCAL and not bAreWeConnected():
logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S' LOG.warn(f"bootstrap_iNmapInfo not local and NOT CONNECTED")
logging._defaultFormatter.default_msec_format = '' return True
env = dict()
LOG.setLevel(oArgs.loglevel) if oArgs.proxy_type == 2:
LOG.trace = lambda l: LOG.log(0, repr(l)) protocol='socks'
LOG.info(f"Setting loglevel to {oArgs.loglevel!s}") elif oArgs.proxy_type == 1:
protocol='https'
else:
protocol='ipv4'
env = os.environ
lRetval = []
for elts in lElts[:iNODES]:
host, port, key = elts
ip = sDNSLookup(host)
if not ip:
LOG.info('bootstrap_iNmapInfo to {host} did not resolve')
continue
assert len(key) == 64, key
if type(port) == str:
port = int(port)
iRet = -1
try:
iRet = iNmapInfo(protocol, ip, port, key)
if iRet != 0:
LOG.warn('iNmapInfo to ' +repr(host) +' retval=' +str(iRet))
lRetval += [False]
else:
LOG.info(f'bootstrap_iNmapInfo '
+f" net={oArgs.network}"
+f" prot={protocol}"
+f" proxy={oArgs.proxy_type}"
+f' {elts[:2]!r}'
)
lRetval += [True]
except Exception as e:
LOG.error('iNmapInfo to {host} : ' +str(e) \
+'\n' + traceback.format_exc())
lRetval += [False]
return any(lRetval)
def caseFactory(cases): def caseFactory(cases):
"""We want the tests run in order.""" """We want the tests run in order."""

View File

@@ -21,7 +21,7 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# #
"""Originanly from https://github.com/oxij/PyTox c-toxcore-02 branch """Originaly from https://github.com/oxij/PyTox c-toxcore-02 branch
which itself was forked from https://github.com/aitjcize/PyTox/ which itself was forked from https://github.com/aitjcize/PyTox/
Modified to work with Modified to work with
@@ -81,7 +81,6 @@ try:
except ImportError: except ImportError:
bIS_NOT_TOXYGEN = True bIS_NOT_TOXYGEN = True
iNODES=8
# from PyQt5 import QtCore # from PyQt5 import QtCore
if 'QtCore' in globals(): if 'QtCore' in globals():
def qt_sleep(fSec): def qt_sleep(fSec):
@@ -122,59 +121,6 @@ if not hasattr(unittest, 'skip'):
return _wrap1 return _wrap1
unittest.skip = unittest_skip unittest.skip = unittest_skip
def iNmapInfo(sProt, sHost, sPort, key=None, environ=None, bTest=False):
sFile = os.path.join("/tmp", f"{sHost}.{os.getpid()}.nmap")
if sProt in ['socks', 'socks5', 'tcp4']:
cmd = f"nmap -Pn -n -sT -p T:{sPort} {sHost} | grep /tcp >{sFile}"
else:
cmd = f"nmap -Pn -n -sU -p U:{sPort} {sHost} | grep /tcp >{sFile}"
iRet = os.system(cmd)
LOG.debug(f"iNmapInfo cmd={cmd} {iRet}")
if iRet != 0:
return iRet
assert os.path.exists(sFile), sFile
with open(sFile, 'rt') as oFd:
l = oFd.readlines()
assert len(l)
s = '\n'.join([s.strip() for s in l])
LOG.debug(f"iNmapInfo: {s}")
return 0
def bootstrap_iNmapInfo(lElts):
if not bIS_LOCAL and not ts.bAreWeConnected():
LOG.warn(f"bootstrap_iNmapInfo not local and NOT CONNECTED")
return True
env = dict()
if oTOX_OARGS.proxy_type == 2:
protocol='socks'
elif oTOX_OARGS.proxy_type == 1:
protocol='https'
else:
protocol='ipv4'
env = os.environ
lRetval = []
for elts in lElts[:iNODES]:
if elts[0] in ts.lDEAD_BS: continue
iRet = -1
try:
iRet = iNmapInfo(protocol, *elts)
if iRet != 0:
LOG.warn('iNmapInfo to ' +repr(elts[0]) +' retval=' +str(iRet))
lRetval += [False]
else:
LOG.info(f'bootstrap_iNmapInfo '
+f" net={oTOX_OARGS.network}"
+f" prot={protocol}"
+f" proxy={oTOX_OARGS.proxy_type}"
+f' {elts[:2]!r}'
)
lRetval += [True]
except Exception as e:
LOG.error('iNmapInfo to ' +repr(elts[0]) +' : ' +str(e) \
+'\n' + traceback.format_exc())
lRetval += [False]
return any(lRetval)
class ToxOptions(): class ToxOptions():
def __init__(self): def __init__(self):
self.ipv6_enabled = True self.ipv6_enabled = True
@@ -348,15 +294,15 @@ class ToxSuite(unittest.TestCase):
LOG.warn('we are NOT CONNECTED ') LOG.warn('we are NOT CONNECTED ')
elif oTOX_OARGS.proxy_port > 0: elif oTOX_OARGS.proxy_port > 0:
random.shuffle(self.lUdp) random.shuffle(self.lUdp)
# LOG.debug(f"call_bootstrap ts.bootstrap_good {self.lUdp[:2]}") # LOG.debug(f"call_bootstrap ts.bootstrap_udp {self.lUdp[:2]}")
ts.bootstrap_good(self.lUdp[:iNODES], [self.alice, self.bob]) ts.bootstrap_udp(self.lUdp[:iNODES], [self.alice, self.bob])
random.shuffle(self.lTcp) random.shuffle(self.lTcp)
# LOG.debug(f"call_bootstrap ts.bootstrap_tcp {self.lTcp[:8]}") # LOG.debug(f"call_bootstrap ts.bootstrap_tcp {self.lTcp[:8]}")
ts.bootstrap_tcp(self.lTcp[:iNODES], [self.alice, self.bob]) ts.bootstrap_tcp(self.lTcp[:iNODES], [self.alice, self.bob])
else: else:
random.shuffle(self.lUdp) random.shuffle(self.lUdp)
# LOG.debug(f"call_bootstrap ts.bootstrap_good {self.lUdp[:8]}") # LOG.debug(f"call_bootstrap ts.bootstrap_udp {self.lUdp[:8]}")
ts.bootstrap_good(self.lUdp[:8], [self.alice, self.bob]) ts.bootstrap_udp(self.lUdp[:8], [self.alice, self.bob])
def loop_until_connected(self): def loop_until_connected(self):
""" """
@@ -816,7 +762,7 @@ class ToxSuite(unittest.TestCase):
lRetval = [] lRetval = []
random.shuffle(lElts) random.shuffle(lElts)
# assert # assert
bootstrap_iNmapInfo(lElts) ts.bootstrap_iNmapInfo(lElts, oTOX_OARGS, bIS_LOCAL, iNODES=8)
def test_self_get_secret_key(self): # works def test_self_get_secret_key(self): # works
""" """
@@ -1646,6 +1592,22 @@ class ToxSuite(unittest.TestCase):
else: else:
LOG.info("passed test_tox_savedata") LOG.info("passed test_tox_savedata")
def vOargsToxPreamble(oArgs, Tox, ToxTest):
ts.vSetupLogging()
methods = set([x for x in dir(Tox) if not x[0].isupper()
and not x[0] == '_'])
docs = "".join([getattr(ToxTest, x).__doc__ for x in dir(ToxTest)
if getattr(ToxTest, x).__doc__ is not None])
tested = set(re.findall(r't:(.*?)\n', docs))
not_tested = methods.difference(tested)
logging.info('Test Coverage: %.2f%%' % (len(tested) * 100.0 / len(methods)))
if len(not_tested):
logging.info('Not tested:\n %s' % "\n ".join(sorted(list(not_tested))))
### ###
def iMain(oArgs): def iMain(oArgs):