mirror of https://github.com/n1nj4sec/pupy.git
Add initial IGD (UPnP) support
This commit is contained in:
parent
a39670a78f
commit
e320af34ae
|
@ -40,6 +40,7 @@ import urllib2
|
|||
import getpass
|
||||
import __future__
|
||||
import bz2
|
||||
import netaddr
|
||||
#needed for scapy :
|
||||
import new
|
||||
import fractions
|
||||
|
|
|
@ -3,3 +3,4 @@ pycrypto
|
|||
psutil
|
||||
pyaml
|
||||
rsa
|
||||
netaddr
|
||||
|
|
|
@ -14,7 +14,7 @@ PYTHONVC="https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-
|
|||
PYCRYPTO32="http://www.voidspace.org.uk/downloads/pycrypto26/pycrypto-2.6.win32-py2.7.exe"
|
||||
PYCRYPTO64="http://www.voidspace.org.uk/downloads/pycrypto26/pycrypto-2.6.win-amd64-py2.7.exe"
|
||||
|
||||
PACKAGES="rpyc psutil pyaml rsa pefile image rsa netaddr pypiwin32 win_inet_pton"
|
||||
PACKAGES="rpyc psutil pyaml rsa pefile image rsa netaddr pypiwin32 win_inet_pton netaddr"
|
||||
|
||||
BUILDENV=${1:-`pwd`/buildenv}
|
||||
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from pupylib.PupyModule import *
|
||||
from pupylib.utils.term import colorize
|
||||
|
||||
__class_name__ = "IGDClient"
|
||||
|
||||
class IGDCMDClient(object):
|
||||
def __init__(self):
|
||||
self.igdc = None
|
||||
|
||||
def init(self, IGDClient, args, log):
|
||||
"""
|
||||
initiate the IGDClient
|
||||
"""
|
||||
|
||||
self.igdc = IGDClient(
|
||||
args.source, args.url,
|
||||
args.DEBUG, args.pretty_print)
|
||||
self.log = log
|
||||
|
||||
def show(self, values):
|
||||
if hasattr(values, 'iterkeys'):
|
||||
column_size = max([len(x) for x in values.iterkeys()])
|
||||
fmt = '{{:<{}}}'.format(column_size)
|
||||
for k, v in values.iteritems():
|
||||
if k.startswith('New'):
|
||||
k = k[3:]
|
||||
self.log(colorize(fmt.format(k), 'yellow')+' {}'.format(v))
|
||||
else:
|
||||
values = list(values)
|
||||
columns = []
|
||||
column_sizes = {}
|
||||
for value in values:
|
||||
for column, cvalue in value.iteritems():
|
||||
if not column in columns:
|
||||
if column.startswith('New'):
|
||||
columnlen = len(column) - 3
|
||||
else:
|
||||
columnlen = len(column)
|
||||
|
||||
columns.append(column)
|
||||
column_sizes[column] = max(len(str(cvalue)), columnlen)
|
||||
else:
|
||||
column_sizes[column] = max(column_sizes[column], len(str(cvalue)))
|
||||
|
||||
lines = []
|
||||
header = ''
|
||||
for column in columns:
|
||||
fmt = ' {{:<{}}} '.format(column_sizes[column])
|
||||
if column.startswith('New'):
|
||||
column = column[3:]
|
||||
header += colorize(fmt.format(column), 'yellow')
|
||||
lines.append(header)
|
||||
|
||||
for value in values:
|
||||
row = ''
|
||||
for column in columns:
|
||||
fmt = ' {{:<{}}} '.format(column_sizes[column])
|
||||
row += fmt.format(value[column])
|
||||
lines.append(row)
|
||||
|
||||
self.log('\n'.join(lines))
|
||||
|
||||
def addPM(self, args):
|
||||
self.igdc.AddPortMapping(
|
||||
args.intIP, args.extPort,
|
||||
args.proto, args.intPort,
|
||||
args.enabled, args.duration,
|
||||
args.desc, args.remote)
|
||||
|
||||
def delPM(self, args):
|
||||
self.igdc.DeletePortMapping(
|
||||
args.extPort,
|
||||
args.proto, args.remote)
|
||||
|
||||
def getExtIP(self, args):
|
||||
extip = self.igdc.GetExternalIP()
|
||||
self.show(extip)
|
||||
|
||||
def getGPM(self, args):
|
||||
pm = self.igdc.GetGenericPortMappingEntry(args.index, True)
|
||||
self.show(pm)
|
||||
|
||||
def getSPM(self, args):
|
||||
|
||||
pm = self.igdc.GetSpecificPortMappingEntry(
|
||||
args.extPort, args.proto, args.remote)
|
||||
self.show(pm)
|
||||
|
||||
def getNRSS(self, args):
|
||||
|
||||
pm = self.igdc.GetNATRSIPStatus()
|
||||
self.show(pm)
|
||||
|
||||
def getWDD(self, args):
|
||||
|
||||
pm = self.igdc.GetWarnDisconnectDelay()
|
||||
self.show(pm)
|
||||
|
||||
def getIDT(self, args):
|
||||
|
||||
pm = self.igdc.GetIdleDisconnectTime()
|
||||
self.show(pm)
|
||||
|
||||
def getADT(self, args):
|
||||
|
||||
pm = self.igdc.GetAutoDisconnectTime()
|
||||
self.show(pm)
|
||||
|
||||
def getSI(self, args):
|
||||
pm = self.igdc.GetStatusInfo()
|
||||
self.show(pm)
|
||||
|
||||
def setWDD(self, args):
|
||||
|
||||
self.igdc.SetWarnDisconnectDelay(args.delay)
|
||||
|
||||
def setIDT(self, args):
|
||||
|
||||
self.igdc.SetIdleDisconnectTime(args.time)
|
||||
|
||||
def setADT(self, args):
|
||||
|
||||
self.igdc.SetAutoDisconnectTime(args.time)
|
||||
|
||||
def forceTerm(self, args):
|
||||
|
||||
self.igdc.ForceTermination()
|
||||
|
||||
def requestTerm(self, args):
|
||||
|
||||
self.igdc.RequestTermination()
|
||||
|
||||
def requestConn(self, args):
|
||||
|
||||
self.igdc.RequestConnection()
|
||||
|
||||
def getCT(self, args):
|
||||
|
||||
pm = self.igdc.GetConnectionTypeInfo()
|
||||
self.show(pm)
|
||||
|
||||
def setCT(self, args):
|
||||
|
||||
self.igdc.SetConnectionType(args.ct_type)
|
||||
|
||||
def custom(self, args):
|
||||
args.input_args
|
||||
iargs = json.loads(args.input_args)
|
||||
resp_xml = self.igdc.customAction(args.method_name, iargs, args.svc)
|
||||
if self.igdc.pprint:
|
||||
xml = minidom.parseString(resp_xml)
|
||||
xml.toprettyxml()
|
||||
else:
|
||||
resp_xml
|
||||
|
||||
# following are for IPv6FWControl
|
||||
def getFWStatus(self, args):
|
||||
pm = self.igdc.GetFWStatus()
|
||||
self.show(pm)
|
||||
|
||||
def addPH(self, args):
|
||||
r = self.igdc.AddPinhole(
|
||||
args.intIP,
|
||||
args.rIP,
|
||||
args.rPort,
|
||||
args.intPort,
|
||||
args.proto,
|
||||
args.lease)
|
||||
self.show(r)
|
||||
|
||||
def getOPHT(self, args):
|
||||
r = self.igdc.GetPinholeTimeout(
|
||||
args.intIP, args.rIP, args.rPort, args.intPort, args.proto)
|
||||
self.show(r)
|
||||
|
||||
def updatePH(self, args):
|
||||
self.igdc.UpdatePinhole(args.uid, args.lease)
|
||||
|
||||
def delPH(self, args):
|
||||
self.igdc.DelPinhole(args.uid)
|
||||
|
||||
def getPHPkts(self, args):
|
||||
r = self.igdc.GetPinholePkts(args.uid)
|
||||
self.show(r)
|
||||
|
||||
def chkPH(self, args):
|
||||
r = self.igdc.CheckPinhole(args.uid)
|
||||
self.show(r)
|
||||
|
||||
|
||||
@config(cat='admin')
|
||||
class IGDClient(PupyModule):
|
||||
""" UPnP IGD Client """
|
||||
|
||||
def init_argparse(self):
|
||||
cli = IGDCMDClient()
|
||||
|
||||
parser = PupyArgumentParser(
|
||||
prog='igdc',
|
||||
description=self.__doc__
|
||||
)
|
||||
parser.add_argument('-d', '--DEBUG', action='store_true',
|
||||
help='enable DEBUG output')
|
||||
|
||||
parser.add_argument(
|
||||
'-pp',
|
||||
'--pretty_print',
|
||||
action='store_true',
|
||||
help='enable xml pretty output for debug and custom action')
|
||||
parser.add_argument('-s', '--source', default='0.0.0.0',
|
||||
help='source address of requests')
|
||||
parser.add_argument('-u', '--url',
|
||||
help='control URL')
|
||||
|
||||
subparsers = parser.add_subparsers()
|
||||
|
||||
parser_start = subparsers.add_parser('add', help='add port mapping')
|
||||
parser_start.add_argument('intIP',
|
||||
help='Internal IP')
|
||||
parser_start.add_argument('intPort', type=int,
|
||||
help='Internal Port')
|
||||
parser_start.add_argument('extPort', type=int,
|
||||
help='External Port')
|
||||
parser_start.add_argument('proto', choices=['UDP', 'TCP'],
|
||||
help='Protocol')
|
||||
parser_start.add_argument('-r', '--remote', default='',
|
||||
help='remote host')
|
||||
parser_start.add_argument('-d', '--desc', default='',
|
||||
help='Description of port mapping')
|
||||
parser_start.add_argument(
|
||||
'-e',
|
||||
'--enabled',
|
||||
type=int,
|
||||
choices=[
|
||||
1,
|
||||
0],
|
||||
default=1,
|
||||
help='enable or disable port mapping')
|
||||
parser_start.add_argument('-du', '--duration', type=int, default=0,
|
||||
help='Duration of the mapping')
|
||||
parser_start.set_defaults(func=cli.addPM)
|
||||
|
||||
parser_del = subparsers.add_parser('del', help='del port mapping')
|
||||
parser_del.add_argument('extPort', type=int,
|
||||
help='External Port')
|
||||
parser_del.add_argument('proto', choices=['UDP', 'TCP'],
|
||||
help='Protocol')
|
||||
parser_del.add_argument('-r', '--remote', default='',
|
||||
help='remote host')
|
||||
parser_del.set_defaults(func=cli.delPM)
|
||||
|
||||
parser_geip = subparsers.add_parser('getextip', help='get external IP')
|
||||
parser_geip.set_defaults(func=cli.getExtIP)
|
||||
|
||||
parser_gpm = subparsers.add_parser('getgpm', help='get generic pm entry')
|
||||
parser_gpm.add_argument('-i', '--index', type=int,
|
||||
help='index of PM entry')
|
||||
parser_gpm.set_defaults(func=cli.getGPM)
|
||||
|
||||
parser_spm = subparsers.add_parser(
|
||||
'getspm', help='get specific port mapping')
|
||||
parser_spm.add_argument('extPort', type=int,
|
||||
help='External Port')
|
||||
parser_spm.add_argument('proto', choices=['UDP', 'TCP'],
|
||||
help='Protocol')
|
||||
parser_spm.add_argument('-r', '--remote', default='',
|
||||
help='remote host')
|
||||
parser_spm.set_defaults(func=cli.getSPM)
|
||||
|
||||
parser_nrss = subparsers.add_parser(
|
||||
'getnrss', help='get NAT and RSIP status')
|
||||
parser_nrss.set_defaults(func=cli.getNRSS)
|
||||
|
||||
parser_gwdd = subparsers.add_parser(
|
||||
'getwdd', help='get warn disconnect delay')
|
||||
parser_gwdd.set_defaults(func=cli.getWDD)
|
||||
|
||||
parser_swdd = subparsers.add_parser(
|
||||
'setwdd', help='set warn disconnect delay')
|
||||
parser_swdd.add_argument('delay', type=int,
|
||||
help='warn disconnect delay')
|
||||
parser_swdd.set_defaults(func=cli.setWDD)
|
||||
|
||||
parser_gidt = subparsers.add_parser(
|
||||
'getidt', help='get idle disconnect time')
|
||||
parser_gidt.set_defaults(func=cli.getIDT)
|
||||
|
||||
parser_sidt = subparsers.add_parser(
|
||||
'setidt', help='set idle disconnect time')
|
||||
parser_sidt.add_argument('time', type=int,
|
||||
help='idle disconnect time')
|
||||
parser_sidt.set_defaults(func=cli.setIDT)
|
||||
|
||||
parser_gadt = subparsers.add_parser(
|
||||
'getadt', help='get auto disconnect time')
|
||||
parser_gadt.set_defaults(func=cli.getADT)
|
||||
|
||||
parser_sadt = subparsers.add_parser(
|
||||
'setadt', help='set auto disconnect time')
|
||||
parser_sadt.add_argument('time', type=int,
|
||||
help='auto disconnect time')
|
||||
parser_sadt.set_defaults(func=cli.setADT)
|
||||
|
||||
parser_gsi = subparsers.add_parser('getsi', help='get status info')
|
||||
parser_gsi.set_defaults(func=cli.getSI)
|
||||
|
||||
parser_rt = subparsers.add_parser('rt', help='request termination')
|
||||
parser_rt.set_defaults(func=cli.requestTerm)
|
||||
|
||||
parser_ft = subparsers.add_parser('ft', help='force termination')
|
||||
parser_ft.set_defaults(func=cli.forceTerm)
|
||||
|
||||
parser_rc = subparsers.add_parser('rc', help='request connection')
|
||||
parser_rc.set_defaults(func=cli.requestConn)
|
||||
|
||||
parser_gct = subparsers.add_parser(
|
||||
'getct', help='get connection type info')
|
||||
parser_gct.set_defaults(func=cli.getCT)
|
||||
|
||||
parser_sct = subparsers.add_parser('setct', help='set connection type')
|
||||
parser_sct.add_argument('ct_type',
|
||||
help='connection type')
|
||||
parser_sct.set_defaults(func=cli.setCT)
|
||||
|
||||
parser_cust = subparsers.add_parser('custom', help='use custom action')
|
||||
parser_cust.add_argument('method_name',
|
||||
help='name of custom action')
|
||||
parser_cust.add_argument('-svc', type=str,
|
||||
choices=['WANIPConnection',
|
||||
'WANIPv6FirewallControl'],
|
||||
default='WANIPConnection',
|
||||
help='IGD service, default is WANIPConnection')
|
||||
parser_cust.add_argument(
|
||||
'-iargs',
|
||||
'--input_args',
|
||||
default='{}',
|
||||
help='input args, the format is same as python dict,'
|
||||
'e.g. "{\'NewPortMappingIndex\': [0, \'ui4\']}"')
|
||||
parser_cust.set_defaults(func=cli.custom)
|
||||
|
||||
# following for IPv6FWControl
|
||||
parser_gfwstatus = subparsers.add_parser(
|
||||
'getfwstatus', help='get IPv6 FW status')
|
||||
parser_gfwstatus.set_defaults(func=cli.getFWStatus)
|
||||
|
||||
parser_addph = subparsers.add_parser('addph', help='add IPv6 FW Pinhole')
|
||||
parser_addph.add_argument('intIP',
|
||||
help='Internal IP')
|
||||
parser_addph.add_argument('-intPort', type=int, default=0,
|
||||
help='Internal Port')
|
||||
parser_addph.add_argument('proto', choices=['UDP', 'TCP', 'ALL'],
|
||||
help='Protocol')
|
||||
parser_addph.add_argument('-rIP', default='',
|
||||
help='Remote IP')
|
||||
parser_addph.add_argument('-rPort', type=int, default=0,
|
||||
help='Remote Port')
|
||||
|
||||
parser_addph.add_argument('-lease', type=int, default=3600,
|
||||
help='leasetime of the pinhole')
|
||||
parser_addph.set_defaults(func=cli.addPH)
|
||||
|
||||
parser_gopht = subparsers.add_parser(
|
||||
'getopht', help='get IPv6 FW OutboundPinholeTimeout')
|
||||
parser_gopht.add_argument('-intIP', type=str, default='',
|
||||
help='Internal IP')
|
||||
parser_gopht.add_argument('-intPort', type=int, default=0,
|
||||
help='Internal Port')
|
||||
parser_gopht.add_argument(
|
||||
'-proto',
|
||||
choices=[
|
||||
'UDP',
|
||||
'TCP',
|
||||
'ALL'],
|
||||
default='ALL',
|
||||
help='Protocol')
|
||||
parser_gopht.add_argument('-rIP', default='',
|
||||
help='Remote IP')
|
||||
parser_gopht.add_argument('-rPort', type=int, default=0,
|
||||
help='Remote Port')
|
||||
parser_gopht.set_defaults(func=cli.getOPHT)
|
||||
|
||||
parser_uph = subparsers.add_parser(
|
||||
'updateph', help='update IPv6 FW pinhole')
|
||||
parser_uph.add_argument('uid', type=int, help='UniqueID of the pinhole')
|
||||
parser_uph.add_argument('lease', type=int,
|
||||
help='new leasetime of the pinhole')
|
||||
parser_uph.set_defaults(func=cli.updatePH)
|
||||
|
||||
parser_dph = subparsers.add_parser('delph', help='delete IPv6 FW pinhole')
|
||||
parser_dph.add_argument('uid', type=int, help='UniqueID of the pinhole')
|
||||
parser_dph.set_defaults(func=cli.delPH)
|
||||
|
||||
parser_gphpkts = subparsers.add_parser(
|
||||
'getphpkts', help='get number of packets go through specified IPv6FW pinhole')
|
||||
parser_gphpkts.add_argument(
|
||||
'uid', type=int, help='UniqueID of the pinhole')
|
||||
parser_gphpkts.set_defaults(func=cli.getPHPkts)
|
||||
|
||||
parser_chkph = subparsers.add_parser(
|
||||
'chkph', help='check if the specified pinhole is working')
|
||||
parser_chkph.add_argument('uid', type=int, help='UniqueID of the pinhole')
|
||||
parser_chkph.set_defaults(func=cli.chkPH)
|
||||
|
||||
self.arg_parser = parser
|
||||
self.cli = cli
|
||||
|
||||
def run(self, args):
|
||||
igdc = self.client.conn.modules['network.lib.igd'].IGDClient
|
||||
UPNPError = self.client.conn.modules['network.lib.igd'].UPNPError
|
||||
self.cli.init(igdc, args, self.log)
|
||||
self.cli.igdc.enableDebug(args.DEBUG)
|
||||
self.cli.igdc.enablePPrint(args.pretty_print)
|
||||
try:
|
||||
args.func(args)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'description'):
|
||||
self.error('IGD: {}'.format(e.description))
|
||||
else:
|
||||
self.error('Exception: {}'.format(e))
|
|
@ -0,0 +1,734 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Original code from: https://github.com/hujun-open/pyigdc
|
||||
# Reworked by Oleskii Shevchuk (@alxchk)
|
||||
# License: MIT
|
||||
|
||||
import socket
|
||||
import argparse
|
||||
import urllib2
|
||||
from StringIO import StringIO
|
||||
from httplib import HTTPResponse
|
||||
from xml.etree.cElementTree import fromstring
|
||||
from urlparse import urlparse
|
||||
import ctypes
|
||||
import os
|
||||
import netaddr
|
||||
|
||||
def str2bool(bstr):
|
||||
return bool(int(bstr))
|
||||
|
||||
def getProtoId(proto_name):
|
||||
if isinstance(proto_name, int):
|
||||
if proto_name > 0 and proto_name <= 65535:
|
||||
return proto_name
|
||||
|
||||
proto_name = 'IPPROTO_{}'.format(proto_name)
|
||||
if not hasattr(socket, proto_name):
|
||||
return False
|
||||
|
||||
return getattr(socket, proto_name)
|
||||
|
||||
class UPNPError(Exception):
|
||||
def __init__(self, hcode, ucode, udes):
|
||||
"""
|
||||
hcode is the http error code
|
||||
ucode is the upnp error code
|
||||
udes is the upnp error description
|
||||
"""
|
||||
self.http_code = hcode
|
||||
self.code = ucode
|
||||
self.description = udes
|
||||
|
||||
def __str__(self):
|
||||
return "HTTP Error Code {hc}, UPnP Error Code {c}, {d}"\
|
||||
.format(hc=self.http_code, c=self.code, d=self.description)
|
||||
|
||||
|
||||
class FakeSocket(StringIO):
|
||||
def makefile(self, *args, **kw):
|
||||
return self
|
||||
|
||||
|
||||
def httpparse(fp):
|
||||
socket = FakeSocket(fp.read())
|
||||
response = HTTPResponse(socket)
|
||||
response.begin()
|
||||
return response
|
||||
|
||||
|
||||
# sendSOAP is based on part of source code from miranda-upnp.
|
||||
class IGDClient:
|
||||
"""
|
||||
UPnP IGD v1 Client class, supports all actions
|
||||
"""
|
||||
|
||||
UPNPTYPEDICT = {
|
||||
'NewAutoDisconnectTime': int,
|
||||
'NewIdleDisconnectTime': int,
|
||||
'NewWarnDisconnectDelay': int,
|
||||
'NewPortMappingNumberOfEntries': int,
|
||||
'NewLeaseDuration': int,
|
||||
'NewExternalPort': int,
|
||||
'NewInternalPort': int,
|
||||
'NewRSIPAvailable': str2bool,
|
||||
'NewNATEnabled': str2bool,
|
||||
'NewEnabled': str2bool,
|
||||
'FirewallEnabled': str2bool,
|
||||
'InboundPinholeAllowed': str2bool,
|
||||
'OutboundPinholeTimeout': int,
|
||||
'UniqueID': int,
|
||||
'PinholePackets': int,
|
||||
'IsWorking': str2bool,
|
||||
}
|
||||
|
||||
NS = {
|
||||
'device': 'urn:schemas-upnp-org:device-1-0',
|
||||
'control': 'urn:schemas-upnp-org:control-1-0',
|
||||
'soap': 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
bindIP='0.0.0.0',
|
||||
ctrlURL=None,
|
||||
service="WANIPC",
|
||||
edebug=False,
|
||||
pprint=False,
|
||||
timeout=2.0):
|
||||
"""
|
||||
- intIP is the source address of the request packet, which implies the source interface
|
||||
- ctrlURL is the the control URL of IGD server, client will do discovery if it is None
|
||||
"""
|
||||
self.debug = edebug
|
||||
self.pprint = pprint
|
||||
self.isv6 = False
|
||||
self.timeout = timeout
|
||||
|
||||
if ctrlURL:
|
||||
self.ctrlURL = urlparse(self.ctrlURL)
|
||||
self.bindIP = self._getOutgoingLocalAddress(self.ctrlURL.hostname)
|
||||
self.isv6 = self.bindIP.version == 6
|
||||
else:
|
||||
self.ctrlURL = None
|
||||
self.bindIP = netaddr.IPAddress(bindIP)
|
||||
self.isv6 = self.bindIP.version == 6
|
||||
|
||||
if self.isv6:
|
||||
self.igdsvc = "IP6FWCTL"
|
||||
else:
|
||||
self.igdsvc = "WANIPC"
|
||||
|
||||
self.discovery()
|
||||
|
||||
if self.available:
|
||||
self.intIP = self._getOutgoingLocalAddress()
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
return self.ctrlURL != None
|
||||
|
||||
def enableDebug(self, d=True):
|
||||
"""
|
||||
enable debug output
|
||||
"""
|
||||
self.debug = d
|
||||
|
||||
def enablePPrint(self, p=True):
|
||||
"""
|
||||
enable pretty print for XML output
|
||||
"""
|
||||
self.pprint = p
|
||||
|
||||
def _getOutgoingLocalAddress(self):
|
||||
remote_addr = netaddr.IPAddress(urlparse(self.ctrlURL).hostname)
|
||||
rcon = socket.socket(
|
||||
socket.AF_INET if remote_addr.version == 4 else socket.AF_INET6,
|
||||
)
|
||||
rcon.connect((remote_addr.format(), 1900))
|
||||
return netaddr.IPAddress(rcon.getsockname()[0])
|
||||
|
||||
def _get1stTagText(self, xmls, tagname_list):
|
||||
"""
|
||||
return 1st tag's value in the xmls
|
||||
"""
|
||||
dom = fromstring(xmls)
|
||||
r = {}
|
||||
for tagn in tagname_list:
|
||||
try:
|
||||
txt_node = dom.find('.//{}'.format(tagn))
|
||||
if txt_node is not None:
|
||||
if tagn in self.UPNPTYPEDICT:
|
||||
r[tagn] = self.UPNPTYPEDICT[tagn](txt_node.text)
|
||||
else:
|
||||
r[tagn] = txt_node.text
|
||||
else:
|
||||
r[tagn] = None
|
||||
except:
|
||||
print"xml parse err: {tag} not found".format(tag=tagn)
|
||||
|
||||
return r
|
||||
|
||||
def _parseErrMsg(self, err_resp):
|
||||
"""
|
||||
parse UPnP error message, err_resp is the returned XML in http body
|
||||
reurn UPnP error code and error description
|
||||
"""
|
||||
dom = fromstring(err_resp)
|
||||
err_code = dom.find('.//control:errorCode', self.NS)
|
||||
err_desc = dom.find('.//control:errorDescription', self.NS)
|
||||
return (err_code.text, err_desc.text)
|
||||
|
||||
def discovery(self):
|
||||
"""
|
||||
Find IGD device and its control URL via UPnP multicast discovery
|
||||
"""
|
||||
if not self.isv6:
|
||||
up_disc = '\r\n'.join([
|
||||
'M-SEARCH * HTTP/1.1',
|
||||
'HOST:239.255.255.250:1900',
|
||||
'ST:upnp:rootdevice',
|
||||
'MX:2',
|
||||
'MAN:"ssdp:discover"'
|
||||
]) + '\r\n' * 2
|
||||
|
||||
sock = socket.socket(
|
||||
socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
|
||||
sock.bind((self.bindIP.format(), 19110))
|
||||
sock.sendto(up_disc, ("239.255.255.250", 1900))
|
||||
|
||||
else:
|
||||
if self.bindIP.is_link_local():
|
||||
dst_ip = "ff02::c"
|
||||
else:
|
||||
dst_ip = "ff05::c"
|
||||
up_disc = '\r\n'.join([
|
||||
'M-SEARCH * HTTP/1.1',
|
||||
'HOST:[{dst}]:1900'.format(dst=dst_ip),
|
||||
'ST:upnp:rootdevice',
|
||||
'MX:2',
|
||||
'MAN:"ssdp:discover"'
|
||||
]) + '\r\n' * 2
|
||||
|
||||
sock = socket.socket(
|
||||
socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
|
||||
if self.debug:
|
||||
print "trying to bind to address:", self.bindIP
|
||||
|
||||
socketaddr = socket.getaddrinfo(
|
||||
self.bindIP.format(), 19110)[-1:][0][-1:][0]
|
||||
sock.bind(socketaddr)
|
||||
sock.sendto(up_disc, (dst_ip, 1900))
|
||||
|
||||
if self.debug:
|
||||
print "Discovery: ----- tx request -----\n " + up_disc
|
||||
|
||||
sock.settimeout(self.timeout)
|
||||
try:
|
||||
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
|
||||
except socket.error:
|
||||
return
|
||||
|
||||
sock.close()
|
||||
|
||||
if self.debug:
|
||||
print "Discovery: ----- rx reply -----\n " + data
|
||||
|
||||
descURL = httpparse(StringIO(data)).getheader('location')
|
||||
descXMLs = urllib2.urlopen(descURL).read()
|
||||
self.pr = urlparse(descURL)
|
||||
baseURL = self.pr.scheme + "://" + self.pr.netloc
|
||||
dom = fromstring(descXMLs)
|
||||
|
||||
if self.igdsvc == "WANIPC":
|
||||
svctype = 'urn:schemas-upnp-org:service:WANIPConnection'
|
||||
else:
|
||||
svctype = 'urn:schemas-upnp-org:service:WANIPv6FirewallControl'
|
||||
|
||||
for e in dom.findall('.//device:service', self.NS):
|
||||
stn = e.find('device:serviceType', self.NS)
|
||||
if not stn is None:
|
||||
if stn.text[0:-2] == svctype:
|
||||
cun = e.find('device:controlURL', self.NS).text
|
||||
self.ctrlURL = baseURL + cun
|
||||
break
|
||||
|
||||
if self.debug:
|
||||
print "control URL is ", self.ctrlURL
|
||||
|
||||
def AddPortMapping(self, extPort, proto, intPort, enabled=1, duration=0, intIP=None, desc='', remoteHost=''):
|
||||
upnp_method = 'AddPortMapping'
|
||||
sendArgs = {
|
||||
'NewPortMappingDescription': (desc, 'string'),
|
||||
'NewLeaseDuration': (duration, 'ui4'),
|
||||
'NewInternalClient': (intIP or self.intIP, 'string'),
|
||||
'NewEnabled': (enabled, 'boolean'),
|
||||
'NewExternalPort': (extPort, 'ui2'),
|
||||
'NewRemoteHost': (remoteHost, 'string'),
|
||||
'NewProtocol': (proto, 'string'),
|
||||
'NewInternalPort': (intPort, 'ui2')
|
||||
}
|
||||
|
||||
self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
def DeletePortMapping(self, extPort, proto, remoteHost=''):
|
||||
upnp_method = 'DeletePortMapping'
|
||||
sendArgs = {
|
||||
'NewExternalPort': (extPort, 'ui2'),
|
||||
'NewRemoteHost': (remoteHost, 'string'),
|
||||
'NewProtocol': (proto, 'string')
|
||||
}
|
||||
|
||||
self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
def GetExternalIP(self):
|
||||
upnp_method = 'GetExternalIPAddress'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewExternalIPAddress"
|
||||
])
|
||||
|
||||
def GetGenericPortMappingEntryAll(self):
|
||||
index = 0
|
||||
items = []
|
||||
while True:
|
||||
try:
|
||||
items.append(self.GetGenericPortMappingEntry(index))
|
||||
except UPNPError as e:
|
||||
break
|
||||
|
||||
index += 1
|
||||
|
||||
return items
|
||||
|
||||
def GetGenericPortMappingEntry(self, index=None, hideErr=False):
|
||||
if index is None:
|
||||
return self.GetGenericPortMappingEntryAll()
|
||||
|
||||
upnp_method = 'GetGenericPortMappingEntry'
|
||||
sendArgs = {
|
||||
'NewPortMappingIndex': (index, 'ui4'),
|
||||
}
|
||||
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs, hideErr=hideErr
|
||||
)
|
||||
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewExternalPort", "NewRemoteHost",
|
||||
"NewProtocol", "NewInternalPort",
|
||||
"NewInternalClient", "NewPortMappingDescription",
|
||||
"NewLeaseDuration", "NewEnabled"
|
||||
])
|
||||
|
||||
def GetSpecificPortMappingEntry(self, extPort, proto, remote):
|
||||
upnp_method = 'GetSpecificPortMappingEntry'
|
||||
sendArgs = {
|
||||
'NewExternalPort': (extPort, 'ui2'),
|
||||
'NewRemoteHost': (remote, 'string'),
|
||||
'NewProtocol': (proto, 'string'),
|
||||
}
|
||||
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewInternalPort",
|
||||
"NewInternalClient", "NewPortMappingDescription",
|
||||
"NewLeaseDuration", "NewEnabled"
|
||||
])
|
||||
|
||||
def GetNATRSIPStatus(self):
|
||||
upnp_method = 'GetNATRSIPStatus'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewRSIPAvailable",
|
||||
"NewNATEnabled",
|
||||
])
|
||||
|
||||
def GetWarnDisconnectDelay(self):
|
||||
upnp_method = 'GetWarnDisconnectDelay'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewWarnDisconnectDelay",
|
||||
])
|
||||
|
||||
def GetIdleDisconnectTime(self):
|
||||
upnp_method = 'GetIdleDisconnectTime'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewIdleDisconnectTime",
|
||||
])
|
||||
|
||||
def GetAutoDisconnectTime(self):
|
||||
upnp_method = 'GetAutoDisconnectTime'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewAutoDisconnectTime",
|
||||
])
|
||||
|
||||
def GetStatusInfo(self):
|
||||
upnp_method = 'GetStatusInfo'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewConnectionStatus",
|
||||
"NewLastConnectionError",
|
||||
"NewUptime"
|
||||
])
|
||||
|
||||
def SetWarnDisconnectDelay(self, delay):
|
||||
upnp_method = 'SetWarnDisconnectDelay'
|
||||
sendArgs = {
|
||||
'NewWarnDisconnectDelay': (delay, 'ui4'),
|
||||
}
|
||||
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
def SetIdleDisconnectTime(self, disconnect_time):
|
||||
upnp_method = 'SetIdleDisconnectTime'
|
||||
sendArgs = {
|
||||
'NewIdleDisconnectTime': (disconnect_time, 'ui4'),
|
||||
}
|
||||
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
def SetAutoDisconnectTime(self, disconnect_time):
|
||||
upnp_method = 'SetAutoDisconnectTime'
|
||||
sendArgs = {
|
||||
'NewAutoDisconnectTime': (disconnect_time, 'ui4'),
|
||||
}
|
||||
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
def ForceTermination(self):
|
||||
upnp_method = 'ForceTermination'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL, upnp_method, sendArgs
|
||||
)
|
||||
|
||||
def RequestTermination(self):
|
||||
upnp_method = 'RequestTermination'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
|
||||
def RequestConnection(self):
|
||||
upnp_method = 'RequestConnection'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
|
||||
def GetConnectionTypeInfo(self):
|
||||
upnp_method = 'GetConnectionTypeInfo'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"NewConnectionType",
|
||||
"NewPossibleConnectionTypes", ])
|
||||
|
||||
def SetConnectionType(self, ctype):
|
||||
upnp_method = 'SetConnectionType'
|
||||
sendArgs = {
|
||||
'NewConnectionType': (ctype, 'string'),
|
||||
}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPConnection:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
|
||||
def customAction(self, method_name, in_args={}, svc="WANIPConnection"):
|
||||
"""
|
||||
this is for the vendor specific action
|
||||
in_args is a dict,
|
||||
svc is the IGD service,
|
||||
the format is :
|
||||
key is the argument name
|
||||
value is a two element list, 1st one is the value of arguement, 2nd
|
||||
is the UPnP data type defined in the spec. following is an example:
|
||||
{'NewPortMappingIndex': [0, 'ui4'],}
|
||||
|
||||
"""
|
||||
upnp_method = method_name
|
||||
sendArgs = dict(in_args)
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:{svc}:1'.format(
|
||||
svc=svc),
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs
|
||||
)
|
||||
|
||||
return resp_xml
|
||||
|
||||
def sendSOAP(self, hostName, serviceType, controlURL, actionName,
|
||||
actionArguments, hideErr=False):
|
||||
"""
|
||||
send a SOAP request and get the response
|
||||
"""
|
||||
argList = ''
|
||||
|
||||
if not controlURL:
|
||||
self.discovery()
|
||||
|
||||
# Create a string containing all of the SOAP action's arguments and
|
||||
# values
|
||||
for arg, (val, dt) in actionArguments.iteritems():
|
||||
argList += '<%s>%s</%s>' % (arg, val, arg)
|
||||
|
||||
# Create the SOAP request
|
||||
soapBody = '<?xml version="1.0"?>' \
|
||||
'<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope" ' \
|
||||
'SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' \
|
||||
'<SOAP-ENV:Body>' \
|
||||
'<m:{} xmlns:m="{}">' \
|
||||
'{}' \
|
||||
'</m:{}>' \
|
||||
'</SOAP-ENV:Body>' \
|
||||
'</SOAP-ENV:Envelope>'.format(
|
||||
actionName,
|
||||
serviceType,
|
||||
argList,
|
||||
actionName
|
||||
)
|
||||
|
||||
try:
|
||||
response = urllib2.urlopen(
|
||||
urllib2.Request(controlURL, soapBody, {
|
||||
'Content-Type': 'text/xml',
|
||||
'SOAPAction': '"{}#{}"'.format(
|
||||
serviceType,
|
||||
actionName
|
||||
)
|
||||
}))
|
||||
except urllib2.HTTPError as e:
|
||||
err_code, err_desc = self._parseErrMsg(e.read())
|
||||
raise UPNPError(e.code, err_code, err_desc)
|
||||
|
||||
return response.read()
|
||||
|
||||
# following are for IP6FWControl
|
||||
def GetFWStatus(self):
|
||||
upnp_method = 'GetFirewallStatus'
|
||||
sendArgs = {}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPv6FirewallControl:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"FirewallEnabled", "InboundPinholeAllowed"])
|
||||
|
||||
def AddPinhole(
|
||||
self,
|
||||
iclient,
|
||||
rhost="",
|
||||
rport=0,
|
||||
iport=0,
|
||||
proto=65535,
|
||||
leasetime=3600):
|
||||
upnp_method = "AddPinhole"
|
||||
pid = getProtoId(proto)
|
||||
if not pid:
|
||||
print proto, " is not a supported protocol"
|
||||
return
|
||||
sendArgs = {
|
||||
"RemoteHost": (rhost, 'string'),
|
||||
"RemotePort": (rport, 'ui2'),
|
||||
"InternalClient": (iclient, 'string'),
|
||||
"InternalPort": (iport, 'ui2'),
|
||||
"Protocol": (pid, 'ui2'),
|
||||
"LeaseTime": (leasetime, 'ui4'),
|
||||
}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPv6FirewallControl:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"UniqueID", ])
|
||||
|
||||
def GetPinholeTimeout(
|
||||
self,
|
||||
iclient="",
|
||||
rhost="",
|
||||
rport=0,
|
||||
iport=0,
|
||||
proto=65535):
|
||||
upnp_method = "GetOutboundPinholeTimeout"
|
||||
pid = getProtoId(proto)
|
||||
if not pid:
|
||||
print proto, " is not a supported protocol"
|
||||
return
|
||||
sendArgs = {
|
||||
"RemoteHost": (rhost, 'string'),
|
||||
"RemotePort": (rport, 'ui2'),
|
||||
"InternalClient": (iclient, 'string'),
|
||||
"InternalPort": (iport, 'ui2'),
|
||||
"Protocol": (pid, 'ui2'),
|
||||
}
|
||||
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPv6FirewallControl:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"OutboundPinholeTimeout",
|
||||
])
|
||||
|
||||
def UpdatePinhole(self, uid, lease):
|
||||
upnp_method = "UpdatePinhole"
|
||||
sendArgs = {
|
||||
"UniqueID": (uid, 'ui2'),
|
||||
"NewLeaseTime": (lease, 'ui4'),
|
||||
}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPv6FirewallControl:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs
|
||||
)
|
||||
|
||||
def DelPinhole(self, uid):
|
||||
upnp_method = "DeletePinhole"
|
||||
sendArgs = {
|
||||
"UniqueID": (uid, 'ui2'),
|
||||
}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPv6FirewallControl:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs
|
||||
)
|
||||
|
||||
def GetPinholePkts(self, uid):
|
||||
upnp_method = "GetPinholePackets"
|
||||
sendArgs = {
|
||||
"UniqueID": (uid, 'ui2'),
|
||||
}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPv6FirewallControl:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"PinholePackets",
|
||||
])
|
||||
|
||||
def CheckPinhole(self, uid):
|
||||
upnp_method = "CheckPinholeWorking"
|
||||
sendArgs = {
|
||||
"UniqueID": (uid, 'ui2'),
|
||||
}
|
||||
resp_xml = self.sendSOAP(
|
||||
self.pr.netloc,
|
||||
'urn:schemas-upnp-org:service:WANIPv6FirewallControl:1',
|
||||
self.ctrlURL,
|
||||
upnp_method,
|
||||
sendArgs)
|
||||
if resp_xml:
|
||||
return self._get1stTagText(resp_xml, [
|
||||
"IsWorking",
|
||||
])
|
|
@ -21,6 +21,8 @@ from threading import Thread, RLock
|
|||
from streams.PupySocketStream import addGetPeer
|
||||
from network.lib.connection import PupyConnection
|
||||
|
||||
from network.lib.igd import IGDClient, UPNPError
|
||||
|
||||
class PupyTCPServer(ThreadedServer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
|
@ -34,12 +36,24 @@ class PupyTCPServer(ThreadedServer):
|
|||
self.transport_class = kwargs["transport"]
|
||||
self.transport_kwargs = kwargs["transport_kwargs"]
|
||||
|
||||
self.igd = None
|
||||
self.igd_mapping = False
|
||||
|
||||
del kwargs["stream"]
|
||||
del kwargs["transport"]
|
||||
del kwargs["transport_kwargs"]
|
||||
|
||||
ThreadedServer.__init__(self, *args, **kwargs)
|
||||
|
||||
try:
|
||||
self.igd = IGDClient()
|
||||
if self.igd.available:
|
||||
self.igd.AddPortMapping(self.port, 'TCP', self.port)
|
||||
self.igd_mapping = True
|
||||
|
||||
except UPNPError as e:
|
||||
self.logger.warn("Couldn't create IGD mapping: {}".format(e.description))
|
||||
|
||||
def _setup_connection(self, lock, sock, queue):
|
||||
'''Authenticate a client and if it succeeds, wraps the socket in a connection object.
|
||||
Note that this code is cut and paste from the rpyc internals and may have to be
|
||||
|
@ -124,6 +138,15 @@ class PupyTCPServer(ThreadedServer):
|
|||
|
||||
self.clients.discard(sock)
|
||||
|
||||
def close(self):
|
||||
ThreadedServer.close(self)
|
||||
if self.igd_mapping:
|
||||
try:
|
||||
self.igd.DeletePortMapping(self.port, 'TCP')
|
||||
except Exception as e:
|
||||
self.logger.info('IGD Exception: {}/{}'.format(type(e), e))
|
||||
|
||||
|
||||
class PupyUDPServer(object):
|
||||
def __init__(self, service, **kwargs):
|
||||
if not "stream" in kwargs:
|
||||
|
|
Loading…
Reference in New Issue