73 lines
1.5 KiB
Go
73 lines
1.5 KiB
Go
|
package natpmp
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"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) (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)
|
||
|
|
||
|
needNewDeadline := true
|
||
|
|
||
|
var tries uint
|
||
|
for tries = 0; tries < nAT_TRIES; {
|
||
|
if needNewDeadline {
|
||
|
err = conn.SetDeadline(time.Now().Add((nAT_INITIAL_MS << tries) * time.Millisecond))
|
||
|
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) {
|
||
|
log.Printf("Ignoring packet because IPs differ:", remoteAddr, 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
|
||
|
}
|