mirror of
				https://github.com/gortc/stun.git
				synced 2025-11-01 04:12:50 +08:00 
			
		
		
		
	all: more testing and attr refactor
This commit is contained in:
		| @@ -125,12 +125,6 @@ type RawAttribute struct { | ||||
| 	Value  []byte | ||||
| } | ||||
|  | ||||
| // AddTo adds RawAttribute to m. | ||||
| func (a *RawAttribute) AddTo(m *Message) error { | ||||
| 	m.Add(a.Type, m.Raw) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Equal returns true if a == b. | ||||
| func (a RawAttribute) Equal(b RawAttribute) bool { | ||||
| 	if a.Type != b.Type { | ||||
|   | ||||
| @@ -1,266 +1,36 @@ | ||||
| package stun | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"encoding/hex" | ||||
| 	"net" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestSoftware_GetFrom(t *testing.T) { | ||||
| 	m := New() | ||||
| 	v := "Client v0.0.1" | ||||
| 	m.Add(AttrSoftware, []byte(v)) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	m2 := &Message{ | ||||
| 		Raw: make([]byte, 0, 256), | ||||
| 	} | ||||
| 	software := new(Software) | ||||
| 	if _, err := m2.ReadFrom(m.reader()); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	if err := software.GetFrom(m); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if software.String() != v { | ||||
| 		t.Errorf("Expected %q, got %q.", v, software) | ||||
| 	} | ||||
|  | ||||
| 	sAttr, ok := m.Attributes.Get(AttrSoftware) | ||||
| 	if !ok { | ||||
| 		t.Error("sowfware attribute should be found") | ||||
| 	} | ||||
| 	s := sAttr.String() | ||||
| 	if !strings.HasPrefix(s, "SOFTWARE:") { | ||||
| 		t.Error("bad string representation", s) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkXORMappedAddress_AddTo(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	ip := net.ParseIP("192.168.1.32") | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		addr := &XORMappedAddress{IP: ip, Port: 3654} | ||||
| 		addr.AddTo(m) | ||||
| 		m.Reset() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkXORMappedAddress_GetFrom(b *testing.B) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		b.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	addrValue, err := hex.DecodeString("00019cd5f49f38ae") | ||||
| 	if err != nil { | ||||
| 		b.Error(err) | ||||
| 	} | ||||
| 	m.Add(AttrXORMappedAddress, addrValue) | ||||
| 	addr := new(XORMappedAddress) | ||||
| 	b.ReportAllocs() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		if err := addr.GetFrom(m); err != nil { | ||||
| 			b.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMessage_GetXORMappedAddress(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	addrValue, err := hex.DecodeString("00019cd5f49f38ae") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	m.Add(AttrXORMappedAddress, addrValue) | ||||
| 	addr := new(XORMappedAddress) | ||||
| 	if err = addr.GetFrom(m); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	if !addr.IP.Equal(net.ParseIP("213.141.156.236")) { | ||||
| 		t.Error("bad IP", addr.IP, "!=", "213.141.156.236") | ||||
| 	} | ||||
| 	if addr.Port != 48583 { | ||||
| 		t.Error("bad Port", addr.Port, "!=", 48583) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMessage_GetXORMappedAddressBad(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedIP := net.ParseIP("213.141.156.236") | ||||
| 	expectedPort := 21254 | ||||
| 	addr := new(XORMappedAddress) | ||||
|  | ||||
| 	if err = addr.GetFrom(m); err == nil { | ||||
| 		t.Fatal(err, "should be nil") | ||||
| 	} | ||||
|  | ||||
| 	addr.IP = expectedIP | ||||
| 	addr.Port = expectedPort | ||||
| 	addr.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	mRes := New() | ||||
| 	binary.BigEndian.PutUint16(m.Raw[20+4:20+4+2], 0x21) | ||||
| 	if _, err = mRes.ReadFrom(bytes.NewReader(m.Raw)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if err = addr.GetFrom(m); err == nil { | ||||
| 		t.Fatal(err, "should not be nil") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMessage_AddXORMappedAddress(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedIP := net.ParseIP("213.141.156.236") | ||||
| 	expectedPort := 21254 | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP:   net.ParseIP("213.141.156.236"), | ||||
| 		Port: expectedPort, | ||||
| 	} | ||||
| 	if err = addr.AddTo(m); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	m.WriteHeader() | ||||
| 	mRes := New() | ||||
| 	if _, err = mRes.Write(m.Raw); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if err = addr.GetFrom(mRes); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if !addr.IP.Equal(expectedIP) { | ||||
| 		t.Errorf("%s (got) != %s (expected)", addr.IP, expectedIP) | ||||
| 	} | ||||
| 	if addr.Port != expectedPort { | ||||
| 		t.Error("bad Port", addr.Port, "!=", expectedPort) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMessage_AddXORMappedAddressV6(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedIP := net.ParseIP("fe80::dc2b:44ff:fe20:6009") | ||||
| 	expectedPort := 21254 | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP:   net.ParseIP("fe80::dc2b:44ff:fe20:6009"), | ||||
| 		Port: 21254, | ||||
| 	} | ||||
| 	addr.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	mRes := New() | ||||
| 	if _, err = mRes.ReadFrom(m.reader()); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	gotAddr := new(XORMappedAddress) | ||||
| 	if err = gotAddr.GetFrom(m); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if !gotAddr.IP.Equal(expectedIP) { | ||||
| 		t.Error("bad IP", gotAddr.IP, "!=", expectedIP) | ||||
| 	} | ||||
| 	if gotAddr.Port != expectedPort { | ||||
| 		t.Error("bad Port", gotAddr.Port, "!=", expectedPort) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkErrorCode_AddTo(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		CodeStaleNonce.AddTo(m) | ||||
| 		m.Reset() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkErrorCodeAttribute_AddTo(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	a := &ErrorCodeAttribute{ | ||||
| 		Code:   404, | ||||
| 		Reason: []byte("not found!"), | ||||
| 	} | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		a.AddTo(m) | ||||
| 		m.Reset() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkErrorCodeAttribute_GetFrom(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	a := &ErrorCodeAttribute{ | ||||
| 		Code:   404, | ||||
| 		Reason: []byte("not found!"), | ||||
| 	} | ||||
| 	a.AddTo(m) | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		a.GetFrom(m) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMessage_AddErrorCode(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedCode := ErrorCode(428) | ||||
| 	expectedReason := "Stale Nonce" | ||||
| 	CodeStaleNonce.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	mRes := New() | ||||
| 	if _, err = mRes.ReadFrom(m.reader()); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	errCodeAttr := new(ErrorCodeAttribute) | ||||
| 	if err = errCodeAttr.GetFrom(mRes); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	code := errCodeAttr.Code | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	if code != expectedCode { | ||||
| 		t.Error("bad code", code) | ||||
| 	} | ||||
| 	if string(errCodeAttr.Reason) != expectedReason { | ||||
| 		t.Error("bad reason", string(errCodeAttr.Reason)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkMessage_GetNotFound(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		m.Get(AttrRealm) | ||||
| 	} | ||||
| } | ||||
| } | ||||
|  | ||||
| func TestMessage_GetNoAllocs(t *testing.T) { | ||||
| 	m := New() | ||||
| 	NewSoftware("c").AddTo(m) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	t.Run("Default", func(t *testing.T) { | ||||
| 		allocs := testing.AllocsPerRun(10, func() { | ||||
| 			m.Get(AttrSoftware) | ||||
| 		}) | ||||
| 		if allocs > 0 { | ||||
| 			t.Error("allocated memory, but should not") | ||||
| 		} | ||||
| 	}) | ||||
| 	t.Run("Not found", func(t *testing.T) { | ||||
| 		allocs := testing.AllocsPerRun(10, func() { | ||||
| 			m.Get(AttrOrigin) | ||||
| 		}) | ||||
| 		if allocs > 0 { | ||||
| 			t.Error("allocated memory, but should not") | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| package stun | ||||
|  | ||||
| import "errors" | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // ErrorCodeAttribute represents ERROR-CODE attribute. | ||||
| type ErrorCodeAttribute struct { | ||||
| @@ -8,6 +11,10 @@ type ErrorCodeAttribute struct { | ||||
| 	Reason []byte | ||||
| } | ||||
|  | ||||
| func (c ErrorCodeAttribute) String() string { | ||||
| 	return fmt.Sprintf("%d: %s", c.Code, c.Reason) | ||||
| } | ||||
|  | ||||
| // constants for ERROR-CODE encoding. | ||||
| const ( | ||||
| 	errorCodeReasonStart = 4 | ||||
|   | ||||
							
								
								
									
										73
									
								
								errorcode_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								errorcode_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| package stun | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func BenchmarkErrorCode_AddTo(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		CodeStaleNonce.AddTo(m) | ||||
| 		m.Reset() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkErrorCodeAttribute_AddTo(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	a := &ErrorCodeAttribute{ | ||||
| 		Code:   404, | ||||
| 		Reason: []byte("not found!"), | ||||
| 	} | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		a.AddTo(m) | ||||
| 		m.Reset() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkErrorCodeAttribute_GetFrom(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	a := &ErrorCodeAttribute{ | ||||
| 		Code:   404, | ||||
| 		Reason: []byte("not found!"), | ||||
| 	} | ||||
| 	a.AddTo(m) | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		a.GetFrom(m) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMessage_AddErrorCode(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedCode := ErrorCode(428) | ||||
| 	expectedReason := "Stale Nonce" | ||||
| 	CodeStaleNonce.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	mRes := New() | ||||
| 	if _, err = mRes.ReadFrom(m.reader()); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	errCodeAttr := new(ErrorCodeAttribute) | ||||
| 	if err = errCodeAttr.GetFrom(mRes); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	code := errCodeAttr.Code | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	if code != expectedCode { | ||||
| 		t.Error("bad code", code) | ||||
| 	} | ||||
| 	if string(errCodeAttr.Reason) != expectedReason { | ||||
| 		t.Error("bad reason", string(errCodeAttr.Reason)) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										56
									
								
								fingerprint_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								fingerprint_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| package stun | ||||
|  | ||||
| import ( | ||||
| 	"net" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func BenchmarkFingerprint_AddTo(b *testing.B) { | ||||
| 	b.ReportAllocs() | ||||
| 	m := new(Message) | ||||
| 	s := NewSoftware("software") | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP: net.IPv4(213, 1, 223, 5), | ||||
| 	} | ||||
| 	addAttr(b, m, addr) | ||||
| 	addAttr(b, m, s) | ||||
| 	b.SetBytes(int64(len(m.Raw))) | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		Fingerprint.AddTo(m) | ||||
| 		m.WriteLength() | ||||
| 		m.Length -= attributeHeaderSize + fingerprintSize | ||||
| 		m.Raw = m.Raw[:m.Length+messageHeaderSize] | ||||
| 		m.Attributes = m.Attributes[:len(m.Attributes)-1] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestFingerprint_Check(t *testing.T) { | ||||
| 	m := new(Message) | ||||
| 	addAttr(t, m, NewSoftware("software")) | ||||
| 	m.WriteHeader() | ||||
| 	Fingerprint.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
| 	if err := Fingerprint.Check(m); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkFingerprint_Check(b *testing.B) { | ||||
| 	b.ReportAllocs() | ||||
| 	m := new(Message) | ||||
| 	s := NewSoftware("software") | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP: net.IPv4(213, 1, 223, 5), | ||||
| 	} | ||||
| 	addAttr(b, m, addr) | ||||
| 	addAttr(b, m, s) | ||||
| 	m.WriteHeader() | ||||
| 	Fingerprint.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
| 	b.SetBytes(int64(len(m.Raw))) | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		if err := Fingerprint.Check(m); err != nil { | ||||
| 			b.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -17,6 +17,16 @@ import ( | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| type attributeEncoder interface { | ||||
| 	AddTo(m *Message) error | ||||
| } | ||||
| 
 | ||||
| func addAttr(t testing.TB, m *Message, a attributeEncoder) { | ||||
| 	if err := a.AddTo(m); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func bUint16(v uint16) string { | ||||
| 	return fmt.Sprintf("0b%016s", strconv.FormatUint(uint64(v), 2)) | ||||
| } | ||||
| @@ -504,31 +514,6 @@ func TestExampleChrome(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestPadding(t *testing.T) { | ||||
| 	tt := []struct { | ||||
| 		in, out int | ||||
| 	}{ | ||||
| 		{4, 4},   // 0 | ||||
| 		{2, 4},   // 1 | ||||
| 		{5, 8},   // 2 | ||||
| 		{8, 8},   // 3 | ||||
| 		{11, 12}, // 4 | ||||
| 		{1, 4},   // 5 | ||||
| 		{3, 4},   // 6 | ||||
| 		{6, 8},   // 7 | ||||
| 		{7, 8},   // 8 | ||||
| 		{0, 0},   // 9 | ||||
| 		{40, 40}, // 10 | ||||
| 	} | ||||
| 	for i, c := range tt { | ||||
| 		if got := nearestPaddedValueLength(c.in); got != c.out { | ||||
| 			t.Errorf("[%d]: padd(%d) %d (got) != %d (expected)", | ||||
| 				i, c.in, got, c.out, | ||||
| 			) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestMessageFromBrowsers(t *testing.T) { | ||||
| 	// file contains udp-packets captured from browsers (WebRTC) | ||||
| 	reader := csv.NewReader(bytes.NewReader(loadData(t, "frombrowsers.csv"))) | ||||
| @@ -568,15 +553,7 @@ func TestMessageFromBrowsers(t *testing.T) { | ||||
| func TestRFC5769(t *testing.T) { | ||||
| 	// Test Vectors for Session Traversal Utilities for NAT (STUN) | ||||
| 	// see https://tools.ietf.org/html/rfc5769 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func BenchmarkNewTransactionID(b *testing.B) { | ||||
| 	b.ReportAllocs() | ||||
| 	m := new(Message) | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		m.TransactionID = NewTransactionID() | ||||
| 	} | ||||
| 	t.Skip("RFC5769 test is not implemented") | ||||
| } | ||||
| 
 | ||||
| func BenchmarkMessage_NewTransactionID(b *testing.B) { | ||||
| @@ -626,66 +603,6 @@ func BenchmarkMessageFullHardcore(b *testing.B) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type attributeEncoder interface { | ||||
| 	AddTo(m *Message) error | ||||
| } | ||||
| 
 | ||||
| func addAttr(t testing.TB, m *Message, a attributeEncoder) { | ||||
| 	if err := a.AddTo(m); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func BenchmarkFingerprint_AddTo(b *testing.B) { | ||||
| 	b.ReportAllocs() | ||||
| 	m := new(Message) | ||||
| 	s := NewSoftware("software") | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP: net.IPv4(213, 1, 223, 5), | ||||
| 	} | ||||
| 	addAttr(b, m, addr) | ||||
| 	addAttr(b, m, s) | ||||
| 	b.SetBytes(int64(len(m.Raw))) | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		Fingerprint.AddTo(m) | ||||
| 		m.WriteLength() | ||||
| 		m.Length -= attributeHeaderSize + fingerprintSize | ||||
| 		m.Raw = m.Raw[:m.Length+messageHeaderSize] | ||||
| 		m.Attributes = m.Attributes[:len(m.Attributes)-1] | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestFingerprint_Check(t *testing.T) { | ||||
| 	m := new(Message) | ||||
| 	addAttr(t, m, NewSoftware("software")) | ||||
| 	m.WriteHeader() | ||||
| 	Fingerprint.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
| 	if err := Fingerprint.Check(m); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func BenchmarkFingerprint_Check(b *testing.B) { | ||||
| 	b.ReportAllocs() | ||||
| 	m := new(Message) | ||||
| 	s := NewSoftware("software") | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP: net.IPv4(213, 1, 223, 5), | ||||
| 	} | ||||
| 	addAttr(b, m, addr) | ||||
| 	addAttr(b, m, s) | ||||
| 	m.WriteHeader() | ||||
| 	Fingerprint.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
| 	b.SetBytes(int64(len(m.Raw))) | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		if err := Fingerprint.Check(m); err != nil { | ||||
| 			b.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func BenchmarkMessage_WriteHeader(b *testing.B) { | ||||
| 	m := &Message{ | ||||
| 		TransactionID: NewTransactionID(), | ||||
| @@ -1,8 +1,6 @@ | ||||
| package stun | ||||
|  | ||||
| const ( | ||||
| 	padding = 4 | ||||
| ) | ||||
| const padding = 4 | ||||
|  | ||||
| func nearestPaddedValueLength(l int) int { | ||||
| 	n := padding * (l / padding) | ||||
|   | ||||
							
								
								
									
										28
									
								
								padding_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								padding_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package stun | ||||
|  | ||||
| import "testing" | ||||
|  | ||||
| func TestPadding(t *testing.T) { | ||||
| 	tt := []struct { | ||||
| 		in, out int | ||||
| 	}{ | ||||
| 		{4, 4},   // 0 | ||||
| 		{2, 4},   // 1 | ||||
| 		{5, 8},   // 2 | ||||
| 		{8, 8},   // 3 | ||||
| 		{11, 12}, // 4 | ||||
| 		{1, 4},   // 5 | ||||
| 		{3, 4},   // 6 | ||||
| 		{6, 8},   // 7 | ||||
| 		{7, 8},   // 8 | ||||
| 		{0, 0},   // 9 | ||||
| 		{40, 40}, // 10 | ||||
| 	} | ||||
| 	for i, c := range tt { | ||||
| 		if got := nearestPaddedValueLength(c.in); got != c.out { | ||||
| 			t.Errorf("[%d]: padd(%d) %d (got) != %d (expected)", | ||||
| 				i, c.in, got, c.out, | ||||
| 			) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										13
									
								
								software.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								software.go
									
									
									
									
									
								
							| @@ -1,5 +1,15 @@ | ||||
| package stun | ||||
|  | ||||
| import "errors" | ||||
|  | ||||
| const softwareRawMaxB = 763 | ||||
|  | ||||
| // ErrSoftwareTooBig means that it is not less than 128 characters | ||||
| // (which can be as long as 763 bytes). | ||||
| var ErrSoftwareTooBig = errors.New( | ||||
| 	"SOFTWARE attribute bigger than 763 bytes or 128 characters", | ||||
| ) | ||||
|  | ||||
| // Software is SOFTWARE attribute. | ||||
| type Software struct { | ||||
| 	Raw []byte | ||||
| @@ -16,6 +26,9 @@ func NewSoftware(software string) *Software { | ||||
|  | ||||
| // AddTo adds Software attribute to m. | ||||
| func (s *Software) AddTo(m *Message) error { | ||||
| 	if len(s.Raw) > softwareRawMaxB { | ||||
| 		return ErrSoftwareTooBig | ||||
| 	} | ||||
| 	m.Add(AttrSoftware, m.Raw) | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										58
									
								
								software_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								software_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| package stun | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestSoftware_GetFrom(t *testing.T) { | ||||
| 	m := New() | ||||
| 	v := "Client v0.0.1" | ||||
| 	m.Add(AttrSoftware, []byte(v)) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	m2 := &Message{ | ||||
| 		Raw: make([]byte, 0, 256), | ||||
| 	} | ||||
| 	software := new(Software) | ||||
| 	if _, err := m2.ReadFrom(m.reader()); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	if err := software.GetFrom(m); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if software.String() != v { | ||||
| 		t.Errorf("Expected %q, got %q.", v, software) | ||||
| 	} | ||||
|  | ||||
| 	sAttr, ok := m.Attributes.Get(AttrSoftware) | ||||
| 	if !ok { | ||||
| 		t.Error("sowfware attribute should be found") | ||||
| 	} | ||||
| 	s := sAttr.String() | ||||
| 	if !strings.HasPrefix(s, "SOFTWARE:") { | ||||
| 		t.Error("bad string representation", s) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSoftware_AddTo_Invalid(t *testing.T) { | ||||
| 	m := New() | ||||
| 	s := &Software{ | ||||
| 		Raw: make([]byte, 1024), | ||||
| 	} | ||||
| 	if err := s.AddTo(m); err != ErrSoftwareTooBig { | ||||
| 		t.Errorf("AddTo should return %q, got: %v", ErrSoftwareTooBig, err) | ||||
| 	} | ||||
| 	if err := s.GetFrom(m); err != ErrAttributeNotFound { | ||||
| 		t.Errorf("GetFrom should return %q, got: %v", ErrAttributeNotFound, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSoftware_AddTo_Regression(t *testing.T) { | ||||
| 	// s.AddTo checked len(m.Raw) instead of len(s.Raw). | ||||
| 	m := &Message{Raw: make([]byte, 2048)} | ||||
| 	s := &Software{Raw: make([]byte, 100)} | ||||
| 	if err := s.AddTo(m); err != nil { | ||||
| 		t.Errorf("AddTo should return <nil>, got: %v", err) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										12
									
								
								xoraddr.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								xoraddr.go
									
									
									
									
									
								
							| @@ -4,6 +4,7 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -18,7 +19,13 @@ type XORMappedAddress struct { | ||||
| } | ||||
|  | ||||
| func (a XORMappedAddress) String() string { | ||||
| 	return fmt.Sprintf("%s:%d", a.IP, a.Port) | ||||
| 	return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port)) | ||||
| } | ||||
|  | ||||
| // isIPv4 returns true if ip with len of net.IPv6Len seems to be ipv4. | ||||
| func isIPv4(ip net.IP) bool { | ||||
| 	// Optimized for performance. Copied from net.IP.To4. | ||||
| 	return isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff | ||||
| } | ||||
|  | ||||
| // Is p all zeros? | ||||
| @@ -42,8 +49,7 @@ func (a *XORMappedAddress) AddTo(m *Message) error { | ||||
| 		ip     = a.IP | ||||
| 	) | ||||
| 	if len(a.IP) == net.IPv6len { | ||||
| 		// Optimized for performance. See net.IP.To4 method. | ||||
| 		if isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff { | ||||
| 		if isIPv4(ip) { | ||||
| 			ip = ip[12:16] // like in ip.To4() | ||||
| 		} else { | ||||
| 			family = familyIPv6 | ||||
|   | ||||
							
								
								
									
										202
									
								
								xoraddr_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								xoraddr_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| package stun | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"encoding/hex" | ||||
| 	"net" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func BenchmarkXORMappedAddress_AddTo(b *testing.B) { | ||||
| 	m := New() | ||||
| 	b.ReportAllocs() | ||||
| 	ip := net.ParseIP("192.168.1.32") | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		addr := &XORMappedAddress{IP: ip, Port: 3654} | ||||
| 		addr.AddTo(m) | ||||
| 		m.Reset() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkXORMappedAddress_GetFrom(b *testing.B) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		b.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	addrValue, err := hex.DecodeString("00019cd5f49f38ae") | ||||
| 	if err != nil { | ||||
| 		b.Error(err) | ||||
| 	} | ||||
| 	m.Add(AttrXORMappedAddress, addrValue) | ||||
| 	addr := new(XORMappedAddress) | ||||
| 	b.ReportAllocs() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		if err := addr.GetFrom(m); err != nil { | ||||
| 			b.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestXORMappedAddress_GetFrom(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	addrValue, err := hex.DecodeString("00019cd5f49f38ae") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	m.Add(AttrXORMappedAddress, addrValue) | ||||
| 	addr := new(XORMappedAddress) | ||||
| 	if err = addr.GetFrom(m); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	if !addr.IP.Equal(net.ParseIP("213.141.156.236")) { | ||||
| 		t.Error("bad IP", addr.IP, "!=", "213.141.156.236") | ||||
| 	} | ||||
| 	if addr.Port != 48583 { | ||||
| 		t.Error("bad Port", addr.Port, "!=", 48583) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestXORMappedAddress_GetFrom_Invalid(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedIP := net.ParseIP("213.141.156.236") | ||||
| 	expectedPort := 21254 | ||||
| 	addr := new(XORMappedAddress) | ||||
|  | ||||
| 	if err = addr.GetFrom(m); err == nil { | ||||
| 		t.Fatal(err, "should be nil") | ||||
| 	} | ||||
|  | ||||
| 	addr.IP = expectedIP | ||||
| 	addr.Port = expectedPort | ||||
| 	addr.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	mRes := New() | ||||
| 	binary.BigEndian.PutUint16(m.Raw[20+4:20+4+2], 0x21) | ||||
| 	if _, err = mRes.ReadFrom(bytes.NewReader(m.Raw)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if err = addr.GetFrom(m); err == nil { | ||||
| 		t.Fatal(err, "should not be nil") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestXORMappedAddress_AddTo(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedIP := net.ParseIP("213.141.156.236") | ||||
| 	expectedPort := 21254 | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP:   net.ParseIP("213.141.156.236"), | ||||
| 		Port: expectedPort, | ||||
| 	} | ||||
| 	if err = addr.AddTo(m); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	m.WriteHeader() | ||||
| 	mRes := New() | ||||
| 	if _, err = mRes.Write(m.Raw); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if err = addr.GetFrom(mRes); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if !addr.IP.Equal(expectedIP) { | ||||
| 		t.Errorf("%s (got) != %s (expected)", addr.IP, expectedIP) | ||||
| 	} | ||||
| 	if addr.Port != expectedPort { | ||||
| 		t.Error("bad Port", addr.Port, "!=", expectedPort) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestXORMappedAddress_AddTo_IPv6(t *testing.T) { | ||||
| 	m := New() | ||||
| 	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	copy(m.TransactionID[:], transactionID) | ||||
| 	expectedIP := net.ParseIP("fe80::dc2b:44ff:fe20:6009") | ||||
| 	expectedPort := 21254 | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP:   net.ParseIP("fe80::dc2b:44ff:fe20:6009"), | ||||
| 		Port: 21254, | ||||
| 	} | ||||
| 	addr.AddTo(m) | ||||
| 	m.WriteHeader() | ||||
|  | ||||
| 	mRes := New() | ||||
| 	if _, err = mRes.ReadFrom(m.reader()); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	gotAddr := new(XORMappedAddress) | ||||
| 	if err = gotAddr.GetFrom(m); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if !gotAddr.IP.Equal(expectedIP) { | ||||
| 		t.Error("bad IP", gotAddr.IP, "!=", expectedIP) | ||||
| 	} | ||||
| 	if gotAddr.Port != expectedPort { | ||||
| 		t.Error("bad Port", gotAddr.Port, "!=", expectedPort) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestXORMappedAddress_AddTo_Invalid(t *testing.T) { | ||||
| 	m := New() | ||||
| 	addr := &XORMappedAddress{ | ||||
| 		IP:   []byte{1, 2, 3, 4, 5, 6, 7, 8}, | ||||
| 		Port: 21254, | ||||
| 	} | ||||
| 	if err := addr.AddTo(m); err != ErrBadIPLength { | ||||
| 		t.Errorf("AddTo should return %q, got: %v", ErrBadIPLength, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestXORMappedAddress_String(t *testing.T) { | ||||
| 	tt := []struct { | ||||
| 		in  XORMappedAddress | ||||
| 		out string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			// 0 | ||||
| 			XORMappedAddress{ | ||||
| 				IP:   net.ParseIP("fe80::dc2b:44ff:fe20:6009"), | ||||
| 				Port: 124, | ||||
| 			}, "[fe80::dc2b:44ff:fe20:6009]:124", | ||||
| 		}, | ||||
| 		{ | ||||
| 			// 1 | ||||
| 			XORMappedAddress{ | ||||
| 				IP:   net.ParseIP("213.141.156.236"), | ||||
| 				Port: 8147, | ||||
| 			}, "213.141.156.236:8147", | ||||
| 		}, | ||||
| 	} | ||||
| 	for i, c := range tt { | ||||
| 		if got := c.in.String(); got != c.out { | ||||
| 			t.Errorf("[%d]: XORMappesAddres.String() %s (got) != %s (expected)", | ||||
| 				i, | ||||
| 				got, | ||||
| 				c.out, | ||||
| 			) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Aleksandr Razumov
					Aleksandr Razumov