hydrus/include/HydrusNATPunch.py

218 lines
7.6 KiB
Python

import HydrusConstants as HC
import HydrusExceptions
import os
import socket
import subprocess
import threading
import traceback
from twisted.internet import reactor, defer
from twisted.internet.threads import deferToThread
from twisted.python import log
# new stuff starts here
if HC.PLATFORM_LINUX: upnpc_path = '"' + HC.BIN_DIR + os.path.sep + 'upnpc_linux"'
elif HC.PLATFORM_OSX: upnpc_path = '"' + HC.BIN_DIR + os.path.sep + 'upnpc_osx"'
elif HC.PLATFORM_WINDOWS: upnpc_path = '"' + HC.BIN_DIR + os.path.sep + 'upnpc_win32.exe"'
EXTERNAL_IP = {}
EXTERNAL_IP[ 'ip' ] = None
EXTERNAL_IP[ 'time' ] = 0
def GetExternalIP():
if HC.GetNow() - EXTERNAL_IP[ 'time' ] > 3600 * 24:
EXTERNAL_IP[ 'time' ] = HC.GetNow()
command = upnpc_path + ' -l'
p = subprocess.Popen( command, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
p.wait()
( output, error ) = p.communicate()
if error is not None and len( error ) > 0: raise Exception( 'Problem while trying to fetch External IP:' + os.linesep * 2 + HC.u( error ) )
else:
try:
lines = output.split( os.linesep )
i = lines.index( ' i protocol exPort->inAddr:inPort description remoteHost leaseTime' )
'''ExternalIPAddress = ip'''
( gumpf, EXTERNAL_IP[ 'ip' ] ) = lines[ i - 1 ].split( ' = ' )
except: raise Exception( 'Problem while trying to fetch External IP.' )
return EXTERNAL_IP[ 'ip' ]
def GetLocalIP(): return socket.gethostbyname( socket.gethostname() )
# old, win32 only stuff
'''
if HC.PLATFORM_WINDOWS: import win32com.client
def GetStaticPortMappingCollection():
try:
dispatcher = win32com.client.Dispatch( 'HNetCfg.NATUPnP' )
static_port_mappings = dispatcher.StaticPortMappingCollection
except: raise Exception( 'Could not fetch UPnP Manager!' )
if static_port_mappings is None: raise Exception( 'Could not fetch UPnP info!' + os.linesep + 'Make sure UPnP is enabled for your computer and router, or try restarting your router.' )
return static_port_mappings
def GetUPnPMappings():
static_port_mappings = GetStaticPortMappingCollection()
mappings = []
for i in range( len( static_port_mappings ) ):
static_port_mapping = static_port_mappings[i]
description = static_port_mapping.Description
internal_client = static_port_mapping.InternalClient
internal_port = static_port_mapping.InternalPort
external_ip_address = static_port_mapping.ExternalIPAddress
external_port = static_port_mapping.ExternalPort
protocol = static_port_mapping.Protocol
enabled = static_port_mapping.Enabled
mappings.append( ( description, internal_client, internal_port, external_ip_address, external_port, protocol, enabled ) )
return mappings
def AddUPnPMapping( external_port, protocol, internal_port, description ):
internal_client = GetLocalIP()
enabled = True
static_port_mappings = GetStaticPortMappingCollection()
try: static_port_mappings.Add( external_port, protocol, internal_port, internal_client, enabled, description )
except: raise Exception( 'Attempt to add a UPnP mapping failed; that mapping probably already exists as a UPnP mapping or static port forward already.' )
def RemoveUPnPMapping( external_port, protocol ):
static_port_mappings = GetStaticPortMappingCollection()
try: static_port_mappings.Remove( external_port, protocol )
except: raise Exception( 'Attempt to remove UPnP mapping failed.' )
'''
def AddUPnPMapping( internal_client, internal_port, external_port, protocol, description, duration = 3600 ):
command = upnpc_path + ' -e "' + description + '" -a ' + internal_client + ' ' + str( internal_port ) + ' ' + str( external_port ) + ' ' + protocol + ' ' + str( duration )
p = subprocess.Popen( command, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
p.wait()
( output, error ) = p.communicate()
if error is not None and len( error ) > 0: raise Exception( 'Problem while trying to add UPnP mapping:' + os.linesep * 2 + HC.u( error ) )
def GetUPnPMappings():
command = upnpc_path + ' -l'
p = subprocess.Popen( command, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
p.wait()
( output, error ) = p.communicate()
if error is not None and len( error ) > 0: raise Exception( 'Problem while trying to fetch UPnP mappings:' + os.linesep * 2 + HC.u( error ) )
else:
try:
lines = output.split( os.linesep )
i = lines.index( ' i protocol exPort->inAddr:inPort description remoteHost leaseTime' )
'''ExternalIPAddress = ip'''
( gumpf, external_ip_address ) = lines[ i - 1 ].split( ' = ' )
data_lines = []
i += 1
while i < len( lines ):
if not lines[ i ][0] in ( ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ): break
data_lines.append( lines[ i ] )
i += 1
processed_data = []
for line in data_lines:
''' 0 UDP 65533->192.168.0.197:65533 'Skype UDP at 192.168.0.197:65533 (2665)' '' 0'''
while ' ' in line: line = line.replace( ' ', ' ' )
if line.startswith( ' ' ): ( empty, number, protocol, mapping_data, rest_of_line ) = line.split( ' ', 4 )
else: ( number, protocol, mapping_data, rest_of_line ) = line.split( ' ', 3 )
( external_port, rest_of_mapping_data ) = mapping_data.split( '->' )
external_port = int( external_port )
( internal_client, internal_port ) = rest_of_mapping_data.split( ':' )
internal_port = int( internal_port )
( empty, description, space, remote_host, rest_of_line ) = rest_of_line.split( '\'', 4 )
lease_time = int( rest_of_line[1:] )
processed_data.append( ( description, internal_client, internal_port, external_ip_address, external_port, protocol, lease_time ) )
return processed_data
except Exception as e:
print( traceback.format_exc() )
raise Exception( 'Problem while trying to parse UPnP mappings:' + os.linesep * 2 + HC.u( e ) )
def RemoveUPnPMapping( external_port, protocol ):
command = upnpc_path + ' -d ' + str( external_port ) + ' ' + protocol
p = subprocess.Popen( command, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
p.wait()
( output, error ) = p.communicate()
if error is not None and len( error ) > 0: raise Exception( 'Problem while trying to remove UPnP mapping:' + os.linesep * 2 + HC.u( error ) )