mirror of
https://github.com/pion/stun.git
synced 2025-10-14 12:03:49 +08:00
all: more testing and attr refactor
This commit is contained in:
@@ -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 {
|
||||||
|
@@ -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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -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
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"
|
"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(),
|
@@ -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
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
|
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
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"
|
"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
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