aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/jackpal/go-nat-pmp/network.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/jackpal/go-nat-pmp/network.go')
-rw-r--r--vendor/github.com/jackpal/go-nat-pmp/network.go89
1 files changed, 89 insertions, 0 deletions
diff --git a/vendor/github.com/jackpal/go-nat-pmp/network.go b/vendor/github.com/jackpal/go-nat-pmp/network.go
new file mode 100644
index 000000000..c42b4fee9
--- /dev/null
+++ b/vendor/github.com/jackpal/go-nat-pmp/network.go
@@ -0,0 +1,89 @@
+package natpmp
+
+import (
+ "fmt"
+ "net"
+ "time"
+)
+
+const nAT_PMP_PORT = 5351
+const nAT_TRIES = 9
+const nAT_INITIAL_MS = 250
+
+// A caller that implements the NAT-PMP RPC protocol.
+type network struct {
+ gateway net.IP
+}
+
+func (n *network) call(msg []byte, timeout time.Duration) (result []byte, err error) {
+ var server net.UDPAddr
+ server.IP = n.gateway
+ server.Port = nAT_PMP_PORT
+ conn, err := net.DialUDP("udp", nil, &server)
+ if err != nil {
+ return
+ }
+ defer conn.Close()
+
+ // 16 bytes is the maximum result size.
+ result = make([]byte, 16)
+
+ var finalTimeout time.Time
+ if timeout != 0 {
+ finalTimeout = time.Now().Add(timeout)
+ }
+
+ needNewDeadline := true
+
+ var tries uint
+ for tries = 0; (tries < nAT_TRIES && finalTimeout.IsZero()) || time.Now().Before(finalTimeout); {
+ if needNewDeadline {
+ nextDeadline := time.Now().Add((nAT_INITIAL_MS << tries) * time.Millisecond)
+ err = conn.SetDeadline(minTime(nextDeadline, finalTimeout))
+ if err != nil {
+ return
+ }
+ needNewDeadline = false
+ }
+ _, err = conn.Write(msg)
+ if err != nil {
+ return
+ }
+ var bytesRead int
+ var remoteAddr *net.UDPAddr
+ bytesRead, remoteAddr, err = conn.ReadFromUDP(result)
+ if err != nil {
+ if err.(net.Error).Timeout() {
+ tries++
+ needNewDeadline = true
+ continue
+ }
+ return
+ }
+ if !remoteAddr.IP.Equal(n.gateway) {
+ // Ignore this packet.
+ // Continue without increasing retransmission timeout or deadline.
+ continue
+ }
+ // Trim result to actual number of bytes received
+ if bytesRead < len(result) {
+ result = result[:bytesRead]
+ }
+ return
+ }
+ err = fmt.Errorf("Timed out trying to contact gateway")
+ return
+}
+
+func minTime(a, b time.Time) time.Time {
+ if a.IsZero() {
+ return b
+ }
+ if b.IsZero() {
+ return a
+ }
+ if a.Before(b) {
+ return a
+ }
+ return b
+}