upnpigd.py 1.88 KB
Newer Older
Guillaume Bury's avatar
Guillaume Bury committed
1
import miniupnpc
2
import logging
3
import time
Guillaume Bury's avatar
Guillaume Bury committed
4

5

6 7
class Forwarder:
    def __init__(self):
8 9
        self._u = miniupnpc.UPnP()
        self._u.discoverdelay = 200
10
        self._rules = []
11
        self._u.discover()
Julien Muchembled's avatar
Julien Muchembled committed
12
        self._u.selectigd()
13 14 15
        self._external_ip = self._u.externalipaddress()
        self.next_refresh = time.time()

Julien Muchembled's avatar
Julien Muchembled committed
16
    def addRule(self, local_port, proto):
17
        # Init parameters
Julien Muchembled's avatar
Julien Muchembled committed
18 19 20 21
        external_port = 1023
        desc = 're6stnet openvpn %s server' % proto
        proto = proto.upper()
        lanaddr = self._u.lanaddr
22 23
        # Choose a free port
        while True:
Julien Muchembled's avatar
Julien Muchembled committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
            external_port += 1
            if external_port > 65535:
                raise Exception('Failed to redirect %u/%s via UPnP'
                                % (local_port, proto))
            try:
                if not self._u.getspecificportmapping(external_port, proto):
                    args = external_port, proto, lanaddr, local_port, desc, ''
                    self._u.addportmapping(*args)
                    break
            except Exception, e:
                if str(e) != 'ConflictInMappingEntry':
                    raise
        logging.debug('Forwarding %s:%s to %s:%s', self._external_ip,
                      external_port, self._u.lanaddr, local_port)
        self._rules.append(args)
        return self._external_ip, external_port
Guillaume Bury's avatar
Guillaume Bury committed
40

41
    def refresh(self):
42
        logging.debug('Refreshing port forwarding')
Julien Muchembled's avatar
Julien Muchembled committed
43
        for args in self._rules:
44 45 46
            try:
                self._u.addportmapping(*args)
            except Exception, e:
47
                if str(e) not in ('UnknownError', 'Invalid Args'):
48 49
                    raise
                logging.warning("Failed to refresh port forwarding: %s", args)
50
        self.next_refresh = time.time() + 500
Julien Muchembled's avatar
Julien Muchembled committed
51 52 53 54

    def clear(self):
        for args in self._rules:
            self._u.deleteportmapping(args[0], args[1])
55
        del self._rules[:]