all: more testing and attr refactor

This commit is contained in:
Aleksandr Razumov
2017-02-11 08:17:00 +03:00
parent 587a112e6b
commit 1d5427b2ea
12 changed files with 483 additions and 361 deletions

View File

@@ -125,12 +125,6 @@ type RawAttribute struct {
Value []byte 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. // Equal returns true if a == b.
func (a RawAttribute) Equal(b RawAttribute) bool { func (a RawAttribute) Equal(b RawAttribute) bool {
if a.Type != b.Type { if a.Type != b.Type {

View File

@@ -1,266 +1,36 @@
package stun package stun
import ( import (
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"net"
"strings"
"testing" "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) { func BenchmarkMessage_GetNotFound(b *testing.B) {
m := New() m := New()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
m.Get(AttrRealm) 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")
}
})
}

View File

@@ -1,6 +1,9 @@
package stun package stun
import "errors" import (
"errors"
"fmt"
)
// ErrorCodeAttribute represents ERROR-CODE attribute. // ErrorCodeAttribute represents ERROR-CODE attribute.
type ErrorCodeAttribute struct { type ErrorCodeAttribute struct {
@@ -8,6 +11,10 @@ type ErrorCodeAttribute struct {
Reason []byte Reason []byte
} }
func (c ErrorCodeAttribute) String() string {
return fmt.Sprintf("%d: %s", c.Code, c.Reason)
}
// constants for ERROR-CODE encoding. // constants for ERROR-CODE encoding.
const ( const (
errorCodeReasonStart = 4 errorCodeReasonStart = 4

73
errorcode_test.go Normal file
View 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
View 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)
}
}
}

View File

@@ -17,6 +17,16 @@ import (
"testing" "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 { func bUint16(v uint16) string {
return fmt.Sprintf("0b%016s", strconv.FormatUint(uint64(v), 2)) 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) { func TestMessageFromBrowsers(t *testing.T) {
// file contains udp-packets captured from browsers (WebRTC) // file contains udp-packets captured from browsers (WebRTC)
reader := csv.NewReader(bytes.NewReader(loadData(t, "frombrowsers.csv"))) reader := csv.NewReader(bytes.NewReader(loadData(t, "frombrowsers.csv")))
@@ -568,15 +553,7 @@ func TestMessageFromBrowsers(t *testing.T) {
func TestRFC5769(t *testing.T) { func TestRFC5769(t *testing.T) {
// Test Vectors for Session Traversal Utilities for NAT (STUN) // Test Vectors for Session Traversal Utilities for NAT (STUN)
// see https://tools.ietf.org/html/rfc5769 // see https://tools.ietf.org/html/rfc5769
t.Skip("RFC5769 test is not implemented")
}
func BenchmarkNewTransactionID(b *testing.B) {
b.ReportAllocs()
m := new(Message)
for i := 0; i < b.N; i++ {
m.TransactionID = NewTransactionID()
}
} }
func BenchmarkMessage_NewTransactionID(b *testing.B) { 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) { func BenchmarkMessage_WriteHeader(b *testing.B) {
m := &Message{ m := &Message{
TransactionID: NewTransactionID(), TransactionID: NewTransactionID(),

View File

@@ -1,8 +1,6 @@
package stun package stun
const ( const padding = 4
padding = 4
)
func nearestPaddedValueLength(l int) int { func nearestPaddedValueLength(l int) int {
n := padding * (l / padding) n := padding * (l / padding)

28
padding_test.go Normal file
View 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,
)
}
}
}

View File

@@ -1,5 +1,15 @@
package stun 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. // Software is SOFTWARE attribute.
type Software struct { type Software struct {
Raw []byte Raw []byte
@@ -16,6 +26,9 @@ func NewSoftware(software string) *Software {
// AddTo adds Software attribute to m. // AddTo adds Software attribute to m.
func (s *Software) AddTo(m *Message) error { func (s *Software) AddTo(m *Message) error {
if len(s.Raw) > softwareRawMaxB {
return ErrSoftwareTooBig
}
m.Add(AttrSoftware, m.Raw) m.Add(AttrSoftware, m.Raw)
return nil return nil
} }

58
software_test.go Normal file
View 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)
}
}

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"strconv"
) )
const ( const (
@@ -18,7 +19,13 @@ type XORMappedAddress struct {
} }
func (a XORMappedAddress) String() string { 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? // Is p all zeros?
@@ -42,8 +49,7 @@ func (a *XORMappedAddress) AddTo(m *Message) error {
ip = a.IP ip = a.IP
) )
if len(a.IP) == net.IPv6len { if len(a.IP) == net.IPv6len {
// Optimized for performance. See net.IP.To4 method. if isIPv4(ip) {
if isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff {
ip = ip[12:16] // like in ip.To4() ip = ip[12:16] // like in ip.To4()
} else { } else {
family = familyIPv6 family = familyIPv6

202
xoraddr_test.go Normal file
View 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,
)
}
}
}