123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- package upnp
- import (
- "errors"
- "log"
- "sync"
- "time"
- "gitlab.com/NebulousLabs/go-upnp"
- )
- /*
- uPNP Module
- This module handles uPNP Connections to the gateway router and create a port forward entry
- for the host system at the given port (set with -port paramter)
- */
- type UPnPClient struct {
- Connection *upnp.IGD //UPnP conenction object
- ExternalIP string //Storage of external IP address
- RequiredPorts []int //All the required ports will be recored
- PolicyNames sync.Map //Name for the required port nubmer
- RenewStopChan chan bool
- }
- func NewUPNPClient() (*UPnPClient, error) {
- //Create uPNP forwarding in the NAT router
- log.Println("Discovering UPnP router in Local Area Network...")
- d, err := upnp.Discover()
- if err != nil {
- return &UPnPClient{}, err
- }
- // discover external IP
- ip, err := d.ExternalIP()
- if err != nil {
- return &UPnPClient{}, err
- }
- //Create the final obejcts
- ticker := time.NewTicker(6 * time.Hour)
- stop := make(chan bool)
- newUPnPObject := &UPnPClient{
- Connection: d,
- ExternalIP: ip,
- RequiredPorts: []int{},
- RenewStopChan: stop,
- }
- go func() {
- defer ticker.Stop()
- for {
- select {
- case <-stop:
- return
- case <-ticker.C:
- newUPnPObject.RenewForwardRules()
- }
- }
- }()
- return newUPnPObject, nil
- }
- func (u *UPnPClient) ForwardPort(portNumber int, ruleName string) error {
- log.Println("UPnP forwarding new port: ", portNumber, "for "+ruleName+" service")
- //Check if port already forwarded
- _, ok := u.PolicyNames.Load(portNumber)
- if ok {
- //Port already forward. Ignore this request
- return errors.New("Port already forwarded")
- }
- // forward a port
- err := u.Connection.Forward(uint16(portNumber), ruleName)
- if err != nil {
- return err
- }
- u.RequiredPorts = append(u.RequiredPorts, portNumber)
- u.PolicyNames.Store(portNumber, ruleName)
- return nil
- }
- func (u *UPnPClient) ClosePort(portNumber int) error {
- //Check if port is opened
- portOpened := false
- newRequiredPort := []int{}
- for _, thisPort := range u.RequiredPorts {
- if thisPort != portNumber {
- newRequiredPort = append(newRequiredPort, thisPort)
- } else {
- portOpened = true
- }
- }
- if portOpened {
- //Update the port list
- u.RequiredPorts = newRequiredPort
- // Close the port
- log.Println("Closing UPnP Port Forward: ", portNumber)
- err := u.Connection.Clear(uint16(portNumber))
- //Delete the name registry
- u.PolicyNames.Delete(portNumber)
- if err != nil {
- log.Println(err)
- return err
- }
- }
- return nil
- }
- // Renew forward rules, prevent router lease time from flushing the Upnp config
- func (u *UPnPClient) RenewForwardRules() {
- portsToRenew := u.RequiredPorts
- for _, thisPort := range portsToRenew {
- ruleName, ok := u.PolicyNames.Load(thisPort)
- if !ok {
- continue
- }
- u.ClosePort(thisPort)
- time.Sleep(100 * time.Millisecond)
- u.ForwardPort(thisPort, ruleName.(string))
- }
- log.Println("UPnP Port Forward rule renew completed")
- }
- func (u *UPnPClient) Close() {
- //Shutdown the default UPnP Object
- if u != nil {
- if u.RenewStopChan != nil {
- u.RenewStopChan <- true
- }
- for _, portNumber := range u.RequiredPorts {
- err := u.Connection.Clear(uint16(portNumber))
- if err != nil {
- log.Println(err)
- }
- }
- }
- }
|