From 345c246ab05b0a0c2895a01f7ce53a5f2fb6faa0 Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Thu, 13 Feb 2020 14:05:06 +0100 Subject: [PATCH] Add initial PCP support --- go.mod | 4 ++++ go.sum | 26 +++++++++++++++++++++ nat.go | 2 ++ pcp.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 go.sum create mode 100644 pcp.go diff --git a/go.mod b/go.mod index 1d9cf08..b447444 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,10 @@ require ( github.com/huin/goupnp v0.0.0-20180415215157-1395d1447324 github.com/jackpal/gateway v1.0.5 github.com/jackpal/go-nat-pmp v1.0.1 + github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d + github.com/sashahilton00/go-pcp v0.0.0-20200213123604-3235ca47ebbb golang.org/x/net v0.0.0-20180524181706-dfa909b99c79 golang.org/x/text v0.3.2 ) + +go 1.13 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..43b11bb --- /dev/null +++ b/go.sum @@ -0,0 +1,26 @@ +github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= +github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/huin/goupnp v0.0.0-20180415215157-1395d1447324 h1:PV190X5/DzQ/tbFFG5YpT5mH6q+cHlfgqI5JuRnH9oE= +github.com/huin/goupnp v0.0.0-20180415215157-1395d1447324/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= +github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= +github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA= +github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ= +github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sashahilton00/go-pcp v0.0.0-20200213123604-3235ca47ebbb h1:32gmQvpgZb84hgcpk0RGZ5dUBgcouaAIvFlAaS8ghx0= +github.com/sashahilton00/go-pcp v0.0.0-20200213123604-3235ca47ebbb/go.mod h1:jOXPqBJYObze/XYy6JOXTUx/kRmEPqFD+KYS/7E80IU= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/net v0.0.0-20180524181706-dfa909b99c79 h1:1FDlG4HI84rVePw1/0E/crL5tt2N+1blLJpY6UZ6krs= +golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/nat.go b/nat.go index eb574d6..b73c228 100644 --- a/nat.go +++ b/nat.go @@ -45,6 +45,8 @@ func DiscoverGateway() (NAT, error) { return nat, nil case nat := <-discoverNATPMP(): return nat, nil + case nat := <-discoverPCP(): + return nat, nil case <-time.After(10 * time.Second): return nil, ErrNoNATFound } diff --git a/pcp.go b/pcp.go new file mode 100644 index 0000000..3381fe6 --- /dev/null +++ b/pcp.go @@ -0,0 +1,73 @@ +package nat + +import( + "net" + "time" + + "github.com/sashahilton00/go-pcp" +) + +var ( + _ NAT = (*pcpClient)(nil) +) + +type pcpClient struct { + c *pcp.Client +} + +func discoverPCP() <- chan NAT { + res := make(chan NAT, 1) + var client *pcp.Client + client, err := pcp.NewClient() + if err == nil { + //Currently connection checking logic is missing upstream. Thus PCP is always returned as an option even where not present. + res <- &pcpClient{client} + } + return res +} + +func (p *pcpClient) GetDeviceAddress() (addr net.IP, err error) { + return p.c.GetGatewayAddress() +} + +func (p *pcpClient) GetExternalAddress() (addr net.IP, err error) { + return p.c.GetExternalAddress() +} + +func (p *pcpClient) GetInternalAddress() (addr net.IP, err error) { + return p.c.GetInternalAddress() +} + +func (p *pcpClient) AddPortMapping(protocol string, internalPort int, description string, timeout time.Duration) (mappedExternalPort int, err error) { + proto := stringToProtocol(protocol) + externalPort := randomPort() + timeoutInSeconds := int(timeout / time.Second) + lifetime := uint32(timeoutInSeconds) + err = p.c.AddPortMapping(proto, uint16(internalPort), uint16(externalPort), nil, lifetime) + if err != nil { + return 0, err + } + return externalPort, nil +} + +func (p *pcpClient) DeletePortMapping(protocol string, internalPort int) (err error) { + err = p.c.DeletePortMapping(uint16(internalPort)) + return +} + +func (p *pcpClient) Type() string { + return "PCP" +} + +func stringToProtocol(s string) pcp.Protocol { + var p pcp.Protocol + switch s { + case "tcp": + p = 6 + case "udp": + p = 17 + default: + p = 0 + } + return p +}