Ignore 'Invalid Args' error when refreshing UPnP forwarding
[re6stnet.git] / re6st / upnpigd.py
1 import miniupnpc
2 import logging
3 import time
4
5
6 class Forwarder:
7 def __init__(self):
8 self._u = miniupnpc.UPnP()
9 self._u.discoverdelay = 200
10 self._rules = []
11 self._u.discover()
12 self._u.selectigd()
13 self._external_ip = self._u.externalipaddress()
14 self.next_refresh = time.time()
15
16 def addRule(self, local_port, proto):
17 # Init parameters
18 external_port = 1023
19 desc = 're6stnet openvpn %s server' % proto
20 proto = proto.upper()
21 lanaddr = self._u.lanaddr
22 # Choose a free port
23 while True:
24 external_port += 1
25 if external_port > 65535:
26 raise Exception('Failed to redirect %u/%s via UPnP'
27 % (local_port, proto))
28 try:
29 if not self._u.getspecificportmapping(external_port, proto):
30 args = external_port, proto, lanaddr, local_port, desc, ''
31 self._u.addportmapping(*args)
32 break
33 except Exception, e:
34 if str(e) != 'ConflictInMappingEntry':
35 raise
36 logging.debug('Forwarding %s:%s to %s:%s', self._external_ip,
37 external_port, self._u.lanaddr, local_port)
38 self._rules.append(args)
39 return self._external_ip, external_port
40
41 def refresh(self):
42 logging.debug('Refreshing port forwarding')
43 for args in self._rules:
44 try:
45 self._u.addportmapping(*args)
46 except Exception, e:
47 if str(e) not in ('UnknownError', 'Invalid Args'):
48 raise
49 logging.warning("Failed to refresh port forwarding: %s", args)
50 self.next_refresh = time.time() + 500
51
52 def clear(self):
53 for args in self._rules:
54 self._u.deleteportmapping(args[0], args[1])
55 del self._rules[:]