1// Copyright 2016 Cong Ding 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package stun 16 17import ( 18 "errors" 19 "net" 20) 21 22// Follow RFC 3489 and RFC 5389. 23// Figure 2: Flow for type discovery process (from RFC 3489). 24// +--------+ 25// | Test | 26// | I | 27// +--------+ 28// | 29// | 30// V 31// /\ /\ 32// N / \ Y / \ Y +--------+ 33// UDP <-------/Resp\--------->/ IP \------------->| Test | 34// Blocked \ ? / \Same/ | II | 35// \ / \? / +--------+ 36// \/ \/ | 37// | N | 38// | V 39// V /\ 40// +--------+ Sym. N / \ 41// | Test | UDP <---/Resp\ 42// | II | Firewall \ ? / 43// +--------+ \ / 44// | \/ 45// V |Y 46// /\ /\ | 47// Symmetric N / \ +--------+ N / \ V 48// NAT <--- / IP \<-----| Test |<--- /Resp\ Open 49// \Same/ | I | \ ? / Internet 50// \? / +--------+ \ / 51// \/ \/ 52// |Y |Y 53// | | 54// | V 55// | Full 56// | Cone 57// V /\ 58// +--------+ / \ Y 59// | Test |------>/Resp\---->Restricted 60// | III | \ ? / 61// +--------+ \ / 62// \/ 63// |N 64// | Port 65// +------>Restricted 66func (c *Client) discover(conn net.PacketConn, addr *net.UDPAddr) (NATType, *Host, error) { 67 // Perform test1 to check if it is under NAT. 68 c.logger.Debugln("Do Test1") 69 c.logger.Debugln("Send To:", addr) 70 resp, err := c.test1(conn, addr) 71 if err != nil { 72 return NATError, nil, err 73 } 74 c.logger.Debugln("Received:", resp) 75 if resp == nil { 76 return NATBlocked, nil, nil 77 } 78 // identical used to check if it is open Internet or not. 79 identical := resp.identical 80 // changedAddr is used to perform second time test1 and test3. 81 changedAddr := resp.changedAddr 82 // mappedAddr is used as the return value, its IP is used for tests 83 mappedAddr := resp.mappedAddr 84 // Make sure IP and port are not changed. 85 if resp.serverAddr.IP() != addr.IP.String() || 86 resp.serverAddr.Port() != uint16(addr.Port) { 87 return NATError, mappedAddr, errors.New("Server error: response IP/port") 88 } 89 // if changedAddr is not available, use otherAddr as changedAddr, 90 // which is updated in RFC 5780 91 if changedAddr == nil { 92 changedAddr = resp.otherAddr 93 } 94 // changedAddr shall not be nil 95 if changedAddr == nil { 96 return NATError, mappedAddr, errors.New("Server error: no changed address.") 97 } 98 // Perform test2 to see if the client can receive packet sent from 99 // another IP and port. 100 c.logger.Debugln("Do Test2") 101 c.logger.Debugln("Send To:", addr) 102 resp, err = c.test2(conn, addr) 103 if err != nil { 104 return NATError, mappedAddr, err 105 } 106 c.logger.Debugln("Received:", resp) 107 // Make sure IP and port are changed. 108 if resp != nil && 109 (resp.serverAddr.IP() == addr.IP.String() || 110 resp.serverAddr.Port() == uint16(addr.Port)) { 111 return NATError, mappedAddr, errors.New("Server error: response IP/port") 112 } 113 if identical { 114 if resp == nil { 115 return SymmetricUDPFirewall, mappedAddr, nil 116 } 117 return NATNone, mappedAddr, nil 118 } 119 if resp != nil { 120 return NATFull, mappedAddr, nil 121 } 122 // Perform test1 to another IP and port to see if the NAT use the same 123 // external IP. 124 c.logger.Debugln("Do Test1") 125 c.logger.Debugln("Send To:", changedAddr) 126 caddr, err := net.ResolveUDPAddr("udp", changedAddr.String()) 127 if err != nil { 128 c.logger.Debugf("ResolveUDPAddr error: %v", err) 129 } 130 resp, err = c.test1(conn, caddr) 131 if err != nil { 132 return NATError, mappedAddr, err 133 } 134 c.logger.Debugln("Received:", resp) 135 if resp == nil { 136 // It should be NAT_BLOCKED, but will be detected in the first 137 // step. So this will never happen. 138 return NATUnknown, mappedAddr, nil 139 } 140 // Make sure IP/port is not changed. 141 if resp.serverAddr.IP() != caddr.IP.String() || 142 resp.serverAddr.Port() != uint16(caddr.Port) { 143 return NATError, mappedAddr, errors.New("Server error: response IP/port") 144 } 145 if mappedAddr.IP() == resp.mappedAddr.IP() && mappedAddr.Port() == resp.mappedAddr.Port() { 146 // Perform test3 to see if the client can receive packet sent 147 // from another port. 148 c.logger.Debugln("Do Test3") 149 c.logger.Debugln("Send To:", caddr) 150 resp, err = c.test3(conn, caddr) 151 if err != nil { 152 return NATError, mappedAddr, err 153 } 154 c.logger.Debugln("Received:", resp) 155 if resp == nil { 156 return NATPortRestricted, mappedAddr, nil 157 } 158 // Make sure IP is not changed, and port is changed. 159 if resp.serverAddr.IP() != caddr.IP.String() || 160 resp.serverAddr.Port() == uint16(caddr.Port) { 161 return NATError, mappedAddr, errors.New("Server error: response IP/port") 162 } 163 return NATRestricted, mappedAddr, nil 164 } 165 return NATSymmetric, mappedAddr, nil 166} 167