Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add behavior test mode. #42

Merged
merged 2 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

func main() {
var serverAddr = flag.String("s", stun.DefaultServerAddr, "STUN server address")
var b = flag.Bool("b", false, "NAT behavior test mode")
var v = flag.Bool("v", false, "verbose mode")
var vv = flag.Bool("vv", false, "double verbose mode (includes -v)")
var vvv = flag.Bool("vvv", false, "triple verbose mode (includes -v and -vv)")
Expand All @@ -35,6 +36,12 @@ func main() {
// Non verbose mode will be used by default unless we call SetVerbose(true) or SetVVerbose(true).
client.SetVerbose(*v || *vv || *vvv)
client.SetVVerbose(*vv || *vvv)

if *b {
behaviorTest(client)
return
}

// Discover the NAT and return the result.
nat, host, err := client.Discover()
if err != nil {
Expand All @@ -49,3 +56,17 @@ func main() {
fmt.Println("External Port:", host.Port())
}
}

func behaviorTest(c *stun.Client) {
natBehavior, err := c.BehaviorTest()
if err != nil {
fmt.Println(err)
}

if natBehavior != nil {
fmt.Println(" Mapping Behavior:", natBehavior.MappingType)
fmt.Println("Filtering Behavior:", natBehavior.FilteringType)
fmt.Println(" Normal NAT Type:", natBehavior.NormalType())
}
return
}
21 changes: 21 additions & 0 deletions stun/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ func (c *Client) Discover() (NATType, *Host, error) {
return c.discover(conn, serverUDPAddr)
}

func (c *Client) BehaviorTest() (*NATBehavior, error) {
if c.serverAddr == "" {
c.SetServerAddr(DefaultServerAddr)
}
serverUDPAddr, err := net.ResolveUDPAddr("udp", c.serverAddr)
if err != nil {
return nil, err
}
// Use the connection passed to the client if it is not nil, otherwise
// create a connection and close it at the end.
conn := c.conn
if conn == nil {
conn, err = net.ListenUDP("udp", nil)
if err != nil {
return nil, err
}
defer conn.Close()
}
return c.behaviorTest(conn, serverUDPAddr)
}

// Keepalive sends and receives a bind request, which ensures the mapping stays open
// Only applicable when client was created with a connection.
func (c *Client) Keepalive() (*Host, error) {
Expand Down
45 changes: 45 additions & 0 deletions stun/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ const (
// NATType is the type of NAT described by int.
type NATType int

// NAT behavior type
type BehaviorType int

type NATBehavior struct {
MappingType BehaviorType
FilteringType BehaviorType
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats up with these cryptic names? Are you paying per character?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for response. It's just some ridiculous thoughts: don't want it too long. Is it better use MType and FType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I move these to a test branch? There may need much more adjust.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type is already called NATBehaviour, why can't you just call them FilteringType and BehaviourType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good advice.


// NAT types.
const (
NATError NATType = iota
Expand All @@ -46,7 +54,16 @@ const (
NATSymmetricUDPFirewall = SymmetricUDPFirewall
)

const (
BehaviorTypeUnknown BehaviorType = iota
BehaviorTypeEndpoint
BehaviorTypeAddr
BehaviorTypeAddrAndPort
)

var natStr map[NATType]string
var natBehaviorTypeStr map[BehaviorType]string
var natNormalTypeStr map[NATBehavior]string

func init() {
natStr = map[NATType]string{
Expand All @@ -60,6 +77,20 @@ func init() {
NATNone: "Not behind a NAT",
SymmetricUDPFirewall: "Symmetric UDP firewall",
}

natBehaviorTypeStr = map[BehaviorType]string{
BehaviorTypeEndpoint: "EndpointIndependent",
BehaviorTypeAddr: "AddressDependent",
BehaviorTypeAddrAndPort: "AddressAndPortDependent",
}

// Defined in RFC 3489
natNormalTypeStr = map[NATBehavior]string{
NATBehavior{BehaviorTypeEndpoint, BehaviorTypeEndpoint}: "Full cone NAT",
NATBehavior{BehaviorTypeEndpoint, BehaviorTypeAddr}: "Restricted cone NAT",
NATBehavior{BehaviorTypeEndpoint, BehaviorTypeAddrAndPort}: "Port Restricted cone NAT",
NATBehavior{BehaviorTypeAddrAndPort, BehaviorTypeAddrAndPort}: "Symmetric NAT",
}
}

func (nat NATType) String() string {
Expand All @@ -69,6 +100,20 @@ func (nat NATType) String() string {
return "Unknown"
}

func (natBhType BehaviorType) String() string {
if s, ok := natBehaviorTypeStr[natBhType]; ok {
return s
}
return "Unknown"
}

func (natBehavior NATBehavior) NormalType() string {
if s, ok := natNormalTypeStr[natBehavior]; ok {
return s
}
return "Undefined"
}

const (
errorTryAlternate = 300
errorBadRequest = 400
Expand Down
87 changes: 87 additions & 0 deletions stun/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,90 @@ func (c *Client) discover(conn net.PacketConn, addr *net.UDPAddr) (NATType, *Hos
}
return NATSymmetric, mappedAddr, nil
}

func (c *Client) behaviorTest(conn net.PacketConn, addr *net.UDPAddr) (*NATBehavior, error) {
natBehavior := &NATBehavior{}

// Test1 ->(IP1,port1)
// Perform test to check if it is under NAT.
c.logger.Debugln("Do Test1")
resp1, err := c.test(conn, addr)
if err != nil {
return nil, err
}
// identical used to check if it is open Internet or not.
if resp1.identical {
return nil, errors.New("Not behind a NAT.")
}
// use otherAddr or changedAddr
otherAddr := resp1.otherAddr
if otherAddr == nil {
if resp1.changedAddr != nil {
otherAddr = resp1.changedAddr
} else {
return nil, errors.New("Server error: no other address and changed address.")
}
}

// Test2 ->(IP2,port1)
// Perform test to see if mapping to the same IP and port when
// send to another IP.
c.logger.Debugln("Do Test2")
tmpAddr := &net.UDPAddr{IP: net.ParseIP(otherAddr.IP()), Port: addr.Port}
resp2, err := c.test(conn, tmpAddr)
if err != nil {
return nil, err
}
if resp2.mappedAddr.IP() == resp1.mappedAddr.IP() &&
Copy link
Owner

@ccding ccding Jan 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got the following error here

➜  go-stun git:(test) ./go-stun -b -vvv
2022/01/23 20:59:42 Do Test1
2022/01/23 20:59:42 Send To: 216.93.246.18:3478
2022/01/23 20:59:42
00000000  00 01 00 18 21 12 a4 42  b3 2c 4d cc e4 65 eb 60  |....!..B.,M..e.`|
00000010  ed 9b ca 89 80 22 00 0c  53 74 75 6e 43 6c 69 65  |....."..StunClie|
00000020  6e 74 00 00 80 28 00 04  65 f9 e8 54              |nt...(..e..T|
2022/01/23 20:59:42
00000000  01 01 00 48 21 12 a4 42  b3 2c 4d cc e4 65 eb 60  |...H!..B.,M..e.`|
00000010  ed 9b ca 89 00 01 00 08  00 01 b9 88 63 40 4c 62  |............c@Lb|
00000020  00 04 00 08 00 01 b9 84  7d e7 90 f9 00 05 00 08  |........}.......|
00000030  00 01 c4 24 6b cd bb 39  80 20 00 08 00 01 24 00  |...$k..9. ....$.|
00000040  1f 0a 61 f7 80 22 00 14  56 6f 76 69 64 61 2e 6f  |..a.."..Vovida.o|
00000050  72 67 20 30 2e 39 38 2d  43 50 43 00              |rg 0.98-CPC.|
2022/01/23 20:59:42 Received: {packet nil: false, local: 62.24.197.181:1298, remote: 216.93.246.18:3478, changed: 107.205.187.57:50212, other: <nil>, identical: false}
2022/01/23 20:59:42 Do Test2
2022/01/23 20:59:42 Send To: 107.205.187.57:3478
2022/01/23 20:59:42
00000000  00 01 00 18 21 12 a4 42  4f 8d 2f a4 3c db f5 5e  |....!..BO./.<..^|
00000010  69 60 0f 09 80 22 00 0c  53 74 75 6e 43 6c 69 65  |i`..."..StunClie|
00000020  6e 74 00 00 80 28 00 04  a8 8f 35 b7              |nt...(....5.|
2022/01/23 20:59:51 Received: Nil
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x110ead2]

goroutine 1 [running]:
github.com/ccding/go-stun/stun.(*Client).behaviorTest(0xc00011bef8, 0x117b480, 0xc000010038, 0xc0000127e0, 0x0, 0x0, 0x0)
        /Users/cong/go/src/github.com/ccding/go-stun/stun/discover.go:204 +0x232
github.com/ccding/go-stun/stun.(*Client).BehaviorTest(0xc000045ef8, 0x0, 0x0, 0x0)
        /Users/cong/go/src/github.com/ccding/go-stun/stun/client.go:120 +0xe5
main.behaviorTest(0xc000045ef8)
        /Users/cong/go/src/github.com/ccding/go-stun/main.go:61 +0x45
main.main()
        /Users/cong/go/src/github.com/ccding/go-stun/main.go:41 +0x6a5

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also local: 62.24.197.181:1298 is not my local IP address

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be nat blocked , but will be detected in the first step.Maybe the network is not stable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also local: 62.24.197.181:1298 is not my local IP address

Is it better to use mapped instead of local?

resp2.mappedAddr.Port() == resp1.mappedAddr.Port() {
natBehavior.MappingType = BehaviorTypeEndpoint
}

// Test3 ->(IP2,port2)
// Perform test to see if mapping to the same IP and port when
// send to another port.
if natBehavior.MappingType == BehaviorTypeUnknown {
c.logger.Debugln("Do Test3")
tmpAddr.Port = int(otherAddr.Port())
resp3, err := c.test(conn, tmpAddr)
if err != nil {
return nil, err
}
if resp3.mappedAddr.IP() == resp2.mappedAddr.IP() &&
resp3.mappedAddr.Port() == resp2.mappedAddr.Port() {
natBehavior.MappingType = BehaviorTypeAddr
} else {
natBehavior.MappingType = BehaviorTypeAddrAndPort
}
}

// Test4 ->(IP1,port1) (IP2,port2)->
// Perform test to see if the client can receive packet sent from
// another IP and port.
c.logger.Debugln("Do Test4")
resp4, err := c.testChangeBoth(conn, addr)
if err != nil {
return natBehavior, err
}
if resp4 != nil {
natBehavior.FilteringType = BehaviorTypeEndpoint
}

// Test5 ->(IP1,port1) (IP1,port2)->
// Perform test to see if the client can receive packet sent from
// another port.
if natBehavior.FilteringType == BehaviorTypeUnknown {
c.logger.Debugln("Do Test5")
resp5, err := c.testChangePort(conn, addr)
if err != nil {
return natBehavior, err
}
if resp5 != nil {
natBehavior.FilteringType = BehaviorTypeAddr
} else {
natBehavior.FilteringType = BehaviorTypeAddrAndPort
}
}

return natBehavior, nil
}
36 changes: 36 additions & 0 deletions stun/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,45 @@
package stun

import (
"errors"
"net"
)

func (c *Client) sendWithLog(conn net.PacketConn, addr *net.UDPAddr, changeIP bool, changePort bool) (*response, error) {
c.logger.Debugln("Send To:", addr)
resp, err := c.sendBindingReq(conn, addr, changeIP, changePort)
if err != nil {
return nil, err
}
c.logger.Debugln("Received:", resp)
if resp == nil && changeIP == false && changePort == false {
return nil, errors.New("NAT blocked.")
}
if resp != nil && !addrCompare(resp.serverAddr, addr, changeIP, changePort) {
return nil, errors.New("Server error: response IP/port")
}
return resp, err
}

// Make sure IP and port have or haven't change
func addrCompare(host *Host, addr *net.UDPAddr, IPChange, portChange bool) bool {
isIPChange := host.IP() != addr.IP.String()
isPortChange := host.Port() != uint16(addr.Port)
return isIPChange == IPChange && isPortChange == portChange
}

func (c *Client) test(conn net.PacketConn, addr *net.UDPAddr) (*response, error) {
return c.sendWithLog(conn, addr, false, false)
}

func (c *Client) testChangePort(conn net.PacketConn, addr *net.UDPAddr) (*response, error) {
return c.sendWithLog(conn, addr, false, true)
}

func (c *Client) testChangeBoth(conn net.PacketConn, addr *net.UDPAddr) (*response, error) {
return c.sendWithLog(conn, addr, true, true)
}

func (c *Client) test1(conn net.PacketConn, addr net.Addr) (*response, error) {
return c.sendBindingReq(conn, addr, false, false)
}
Expand Down