natpmp.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package natpmp
  2. import (
  3. "fmt"
  4. "net"
  5. "time"
  6. )
  7. // Implement the NAT-PMP protocol, typically supported by Apple routers and open source
  8. // routers such as DD-WRT and Tomato.
  9. //
  10. // See http://tools.ietf.org/html/draft-cheshire-nat-pmp-03
  11. //
  12. // Usage:
  13. //
  14. // client := natpmp.NewClient(gatewayIP)
  15. // response, err := client.GetExternalAddress()
  16. // The recommended mapping lifetime for AddPortMapping
  17. const RECOMMENDED_MAPPING_LIFETIME_SECONDS = 3600
  18. // Interface used to make remote procedure calls.
  19. type caller interface {
  20. call(msg []byte, timeout time.Duration) (result []byte, err error)
  21. }
  22. // Client is a NAT-PMP protocol client.
  23. type Client struct {
  24. caller caller
  25. timeout time.Duration
  26. }
  27. // Create a NAT-PMP client for the NAT-PMP server at the gateway.
  28. // Uses default timeout which is around 128 seconds.
  29. func NewClient(gateway net.IP) (nat *Client) {
  30. return &Client{&network{gateway}, 0}
  31. }
  32. // Create a NAT-PMP client for the NAT-PMP server at the gateway, with a timeout.
  33. // Timeout defines the total amount of time we will keep retrying before giving up.
  34. func NewClientWithTimeout(gateway net.IP, timeout time.Duration) (nat *Client) {
  35. return &Client{&network{gateway}, timeout}
  36. }
  37. // Results of the NAT-PMP GetExternalAddress operation.
  38. type GetExternalAddressResult struct {
  39. SecondsSinceStartOfEpoc uint32
  40. ExternalIPAddress [4]byte
  41. }
  42. // Get the external address of the router.
  43. func (n *Client) GetExternalAddress() (result *GetExternalAddressResult, err error) {
  44. msg := make([]byte, 2)
  45. msg[0] = 0 // Version 0
  46. msg[1] = 0 // OP Code 0
  47. response, err := n.rpc(msg, 12)
  48. if err != nil {
  49. return
  50. }
  51. result = &GetExternalAddressResult{}
  52. result.SecondsSinceStartOfEpoc = readNetworkOrderUint32(response[4:8])
  53. copy(result.ExternalIPAddress[:], response[8:12])
  54. return
  55. }
  56. // Results of the NAT-PMP AddPortMapping operation
  57. type AddPortMappingResult struct {
  58. SecondsSinceStartOfEpoc uint32
  59. InternalPort uint16
  60. MappedExternalPort uint16
  61. PortMappingLifetimeInSeconds uint32
  62. }
  63. // Add (or delete) a port mapping. To delete a mapping, set the requestedExternalPort and lifetime to 0
  64. func (n *Client) AddPortMapping(protocol string, internalPort, requestedExternalPort int, lifetime int) (result *AddPortMappingResult, err error) {
  65. var opcode byte
  66. if protocol == "udp" {
  67. opcode = 1
  68. } else if protocol == "tcp" {
  69. opcode = 2
  70. } else {
  71. err = fmt.Errorf("unknown protocol %v", protocol)
  72. return
  73. }
  74. msg := make([]byte, 12)
  75. msg[0] = 0 // Version 0
  76. msg[1] = opcode
  77. writeNetworkOrderUint16(msg[4:6], uint16(internalPort))
  78. writeNetworkOrderUint16(msg[6:8], uint16(requestedExternalPort))
  79. writeNetworkOrderUint32(msg[8:12], uint32(lifetime))
  80. response, err := n.rpc(msg, 16)
  81. if err != nil {
  82. return
  83. }
  84. result = &AddPortMappingResult{}
  85. result.SecondsSinceStartOfEpoc = readNetworkOrderUint32(response[4:8])
  86. result.InternalPort = readNetworkOrderUint16(response[8:10])
  87. result.MappedExternalPort = readNetworkOrderUint16(response[10:12])
  88. result.PortMappingLifetimeInSeconds = readNetworkOrderUint32(response[12:16])
  89. return
  90. }
  91. func (n *Client) rpc(msg []byte, resultSize int) (result []byte, err error) {
  92. result, err = n.caller.call(msg, n.timeout)
  93. if err != nil {
  94. return
  95. }
  96. err = protocolChecks(msg, resultSize, result)
  97. return
  98. }
  99. func protocolChecks(msg []byte, resultSize int, result []byte) (err error) {
  100. if len(result) != resultSize {
  101. err = fmt.Errorf("unexpected result size %d, expected %d", len(result), resultSize)
  102. return
  103. }
  104. if result[0] != 0 {
  105. err = fmt.Errorf("unknown protocol version %d", result[0])
  106. return
  107. }
  108. expectedOp := msg[1] | 0x80
  109. if result[1] != expectedOp {
  110. err = fmt.Errorf("Unexpected opcode %d. Expected %d", result[1], expectedOp)
  111. return
  112. }
  113. resultCode := readNetworkOrderUint16(result[2:4])
  114. if resultCode != 0 {
  115. err = fmt.Errorf("Non-zero result code %d", resultCode)
  116. return
  117. }
  118. // If we got here the RPC is good.
  119. return
  120. }
  121. func writeNetworkOrderUint16(buf []byte, d uint16) {
  122. buf[0] = byte(d >> 8)
  123. buf[1] = byte(d)
  124. }
  125. func writeNetworkOrderUint32(buf []byte, d uint32) {
  126. buf[0] = byte(d >> 24)
  127. buf[1] = byte(d >> 16)
  128. buf[2] = byte(d >> 8)
  129. buf[3] = byte(d)
  130. }
  131. func readNetworkOrderUint16(buf []byte) uint16 {
  132. return (uint16(buf[0]) << 8) | uint16(buf[1])
  133. }
  134. func readNetworkOrderUint32(buf []byte) uint32 {
  135. return (uint32(buf[0]) << 24) | (uint32(buf[1]) << 16) | (uint32(buf[2]) << 8) | uint32(buf[3])
  136. }