1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 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
}
|