├── README.md ├── README_zh.MD ├── ice ├── AUTHORS ├── README.md ├── attr │ ├── icecontrolled.go │ ├── icecontrolling.go │ ├── priority.go │ └── usercandidate.go ├── candidate.go ├── candidate_test.go ├── checklist.go ├── constant.go ├── error.go ├── example │ └── example.go ├── gather.go ├── hostonlysock.go ├── ice-gather │ └── main.go ├── icesession.go ├── icestreamtransport.go ├── icestreamtransport_test.go ├── interface.go ├── stunserversock.go ├── stunserversock_test.go ├── stunsock.go ├── stunsock_test.go ├── test.txt ├── testdata │ └── candidates_ex1.sdp ├── turnserversock.go ├── turnserversock_test.go ├── turnsock.go ├── turnsock_test.go └── util.go ├── sdp ├── .travis.yml ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── Makefile ├── README.md ├── appveyor.yml ├── bench ├── decoder.go ├── decoder_test.go ├── encoder.go ├── encoder_test.go ├── examples │ ├── sdp-decode │ │ ├── example.sdp │ │ └── sdp-decode.go │ ├── sdp-encode-low │ │ └── main.go │ └── sdp-encode │ │ └── sdp-encode.go ├── fields.go ├── fields_test.go ├── sdp.go ├── sdp_test.go └── testdata │ ├── sdp_session_ex1.txt │ ├── sdp_session_ex_err1.txt │ ├── sdp_session_ex_err2.txt │ ├── sdp_session_ex_err3.txt │ ├── sdp_session_ex_err4.txt │ ├── sdp_session_ex_err5.txt │ ├── sdp_session_ex_full.txt │ ├── spd_session_ex2.txt │ ├── spd_session_ex3.txt │ ├── spd_session_ex_attributes.txt │ ├── spd_session_ex_bandwidth.txt │ ├── spd_session_ex_info.txt │ ├── spd_session_ex_ip.txt │ ├── spd_session_ex_keys.txt │ ├── spd_session_ex_media.txt │ ├── spd_session_ex_name.txt │ ├── spd_session_ex_origin.txt │ ├── spd_session_ex_repeat.txt │ ├── spd_session_ex_timing.txt │ ├── spd_session_ex_uri.txt │ ├── spd_session_ex_webrtc1.txt │ └── spd_session_ex_zones.txt ├── stun ├── .travis.yml ├── AUTHORS ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── addr.go ├── addr_test.go ├── agent.go ├── agent_test.go ├── appveyor.yml ├── attributes.go ├── attributes_test.go ├── client.go ├── client_test.go ├── cmd │ └── stun-client │ │ ├── stun-client │ │ └── stun-client.go ├── errorcode.go ├── errorcode_test.go ├── errors.go ├── errors_test.go ├── examples │ ├── README.md │ ├── stun-msg │ │ ├── corpus │ │ │ ├── 07d9d07df5b341d90ce313f890431fe22673e88e-2 │ │ │ ├── 0f06d0415ec78e5a0670024180429a0d6622e90c-4 │ │ │ ├── 25f2f9fdee7f38b2ce075a03be4c487da027e0c9-1 │ │ │ ├── 323cc3d2eb5115119562afa212f6c124fb4c2f43-6 │ │ │ ├── 325742b8349899bd75473819334034de786d8173-3 │ │ │ ├── 387d69f1e8239e36d3298b9e6459fcfe28aa60fe-3 │ │ │ ├── 45921a3525e058441c0ffd7be81d6b2ac8fc72c7-4 │ │ │ ├── 85745364c54dd7e3a7fe0019b98ce8af454ae963-7 │ │ │ ├── 895d16bc50ce45b6b3684175b57216876084bd81-8 │ │ │ ├── a26e2093b2d3212cd9ff57dbb8d4f62972077971-9 │ │ │ ├── a456f433f4c433a00b723e645759877bbccdd969-4 │ │ │ ├── a788ca1e84797136fec6e61f8bf1206622aec141-1 │ │ │ ├── ac92f2f215a192e18307fb229c3e31d1d36ea72b-9 │ │ │ ├── b1ad18e20a8d34c86cec4db711408a71b16ed27d-5 │ │ │ ├── bdc18abc9f9705cc46de5537fbf88bc16af9ea26-2 │ │ │ ├── c9e72ef3998f3e522d3c8e47b231a879f2372220-4 │ │ │ ├── ceafbc9130e5e938b7c76df54e1a2898e734cbdd-4 │ │ │ └── da39a3ee5e6b4b0d3255bfef95601890afd80709 │ │ ├── crashers │ │ │ ├── 1cc1e3902aa8cce338eb7b6e71d9552f4c6a79f7 │ │ │ ├── 1cc1e3902aa8cce338eb7b6e71d9552f4c6a79f7.output │ │ │ └── 1cc1e3902aa8cce338eb7b6e71d9552f4c6a79f7.quoted │ │ └── suppressions │ │ │ └── 682e7dcec53e1feb7a449dd7aa573cf2a06c3e92 │ ├── stun-setters │ │ ├── corpus │ │ │ ├── 0faf54d4bd09022d40749a9a2607c517cc756cab-4 │ │ │ ├── 12a8b892d03fca186b2f182ebbbd27ac591c5e1d-4 │ │ │ ├── 2aa1442c4a0c09e5a52ad31d325e74a1b63d6541-4 │ │ │ ├── 2d0134ed3b9de132c720fe697b532b4c232ff9fe-6 │ │ │ ├── 31dfbd93e8e15ed443151ba2f3117fb0457929e6-2 │ │ │ ├── 320355ced694aa69924f6bb82e7b74f420303fd9-6 │ │ │ ├── 403db8fb298e330d8d10b4df6ce302b168be125c-9 │ │ │ ├── 4a0a19218e082a343a1b17e5333409af9d98f0f5-5 │ │ │ ├── 5184eb8960542005471ed1e60fd57f3617ca81fa-3 │ │ │ ├── 5c2dd944dde9e08881bef0894fe7b22a5c9c4b06-2 │ │ │ ├── 69dbd1b5c36797f81437a3ebf28409da9912905c-6 │ │ │ ├── 6b0d31c0d563223024da45691584643ac78c96e8-1 │ │ │ ├── 889ab7aaf54aa9410297826cc8cb91119938cedb-7 │ │ │ ├── 9be845a433ab87917f70fe5c8ed0c81d099ffa33-5 │ │ │ ├── a277d65fbc50cfd312f8323fc4778af4acd80631-10 │ │ │ ├── a7c309ed0e4ab135a14951675c982dd148fbc523-5 │ │ │ ├── ac9231da4082430afe8f4d40127814c613648d8e-1 │ │ │ ├── ad4d9292d016d8d7c4ab7a4a43e7a8f91a177591-2 │ │ │ ├── bc26964852021aa26278b88156153fe4b53820cd-8 │ │ │ ├── c3156e00d3c2588c639e0d3cf6821258b05761c7-4 │ │ │ ├── c9ee5681d3c59f7541c27a38b67edf46259e187b-3 │ │ │ ├── ca4c203b3dd9f53dfff08f2ab1ed3801fedf480b-3 │ │ │ ├── d1854cae891ec7b29161ccaf79a24b00c274bdaa │ │ │ ├── da39a3ee5e6b4b0d3255bfef95601890afd80709 │ │ │ ├── ea080fac5ccfc34e1cf7c5e2ffa5dacd377b701c-5 │ │ │ └── ee9791faca6cc73cb213f104dc9b32f202f53612-5 │ │ ├── crashers │ │ │ ├── 43eb31a6590debc599d5839bb8a55afa80e49201 │ │ │ ├── 43eb31a6590debc599d5839bb8a55afa80e49201.output │ │ │ ├── 43eb31a6590debc599d5839bb8a55afa80e49201.quoted │ │ │ ├── 4dd09422696288b4f97acd0b7f51ffb3f07dec28 │ │ │ ├── 4dd09422696288b4f97acd0b7f51ffb3f07dec28.output │ │ │ ├── 4dd09422696288b4f97acd0b7f51ffb3f07dec28.quoted │ │ │ ├── 5c0137314c031cc920b1ac6497600cc1ad234a9e │ │ │ ├── 5c0137314c031cc920b1ac6497600cc1ad234a9e.output │ │ │ ├── 5c0137314c031cc920b1ac6497600cc1ad234a9e.quoted │ │ │ ├── 6934105ad50010b814c933314b1da6841431bc8b │ │ │ ├── 6934105ad50010b814c933314b1da6841431bc8b.output │ │ │ ├── 6934105ad50010b814c933314b1da6841431bc8b.quoted │ │ │ ├── 72d92b32ce3d5c8239c45e57f47eff0e6e8abf05 │ │ │ ├── 72d92b32ce3d5c8239c45e57f47eff0e6e8abf05.output │ │ │ ├── 72d92b32ce3d5c8239c45e57f47eff0e6e8abf05.quoted │ │ │ ├── 8dc00598417d4eb788a77ac6ccef3cb484905d8b │ │ │ ├── 8dc00598417d4eb788a77ac6ccef3cb484905d8b.output │ │ │ ├── 8dc00598417d4eb788a77ac6ccef3cb484905d8b.quoted │ │ │ ├── 90c77b8e1c800456b5a4a46693d0ff806aa94fa5 │ │ │ ├── 90c77b8e1c800456b5a4a46693d0ff806aa94fa5.output │ │ │ ├── 90c77b8e1c800456b5a4a46693d0ff806aa94fa5.quoted │ │ │ ├── a6de690b04292410e3c88d2e3cd967e6cb0e51ec │ │ │ ├── a6de690b04292410e3c88d2e3cd967e6cb0e51ec.output │ │ │ ├── a6de690b04292410e3c88d2e3cd967e6cb0e51ec.quoted │ │ │ ├── f9309435560bc14774700f46f7ba898bfb982441 │ │ │ ├── f9309435560bc14774700f46f7ba898bfb982441.output │ │ │ └── f9309435560bc14774700f46f7ba898bfb982441.quoted │ │ └── suppressions │ │ │ ├── 141f41aee117afb4798f3cba5fc9f470beed94a1 │ │ │ ├── 29b71161b1355c93a882a7fa6b8ff098113ac76b │ │ │ ├── 5607cf1f2a7846dd0f8f775daf7a350f5d1c0734 │ │ │ ├── 58915993609c5d48730aecf40b3b3cede8c1a9af │ │ │ ├── 76d8cd83b54ce64d6d1b9aaaf16f3f383f28b804 │ │ │ ├── b61c92191d726cb462f0fd36cdab989880235bdf │ │ │ ├── d8b9c1359784aa7a593c0b8141e4468ad6a50bf8 │ │ │ ├── e2f854b5a3caf1482e7314722f9c3b5d2edc3e93 │ │ │ └── f5d7ef9e130b5d534dee3a3da3878e610003855c │ └── stun-typ │ │ ├── corpus │ │ ├── 02af615c9595bc675b21b30ff2de725877e47207-3 │ │ ├── 0de292b04c19b5aef164070475a47f94be80a615-1 │ │ ├── 2a89163095f14efe95934fc14a29c10d5a4d9ddf-1 │ │ ├── 2f3662c1d727be578d15cd6512dc714da0092020-1 │ │ ├── 9bdb77276c1852e1fb067820472812fcf6084024-1 │ │ ├── cd8fd7e44f6096d78161657475088cd3fb87398d-2 │ │ └── da39a3ee5e6b4b0d3255bfef95601890afd80709 │ │ ├── crashers │ │ ├── da39a3ee5e6b4b0d3255bfef95601890afd80709 │ │ ├── da39a3ee5e6b4b0d3255bfef95601890afd80709.output │ │ └── da39a3ee5e6b4b0d3255bfef95601890afd80709.quoted │ │ └── suppressions │ │ ├── 1df2ba61c49ca26ff8124345d40bca8057f55e7c │ │ └── 6580320c92b36eca1e0a55e10b062ca67868a28e ├── fingerprint.go ├── fingerprint_test.go ├── fuzz.go ├── fuzz_test.go ├── helpers.go ├── helpers_test.go ├── integrity.go ├── integrity_test.go ├── message.go ├── message_test.go ├── rfc5769_test.go ├── stun.go ├── stun_test.go ├── testdata │ ├── README.md │ ├── ex1_chrome.stun │ └── frombrowsers.csv ├── textattrs.go ├── textattrs_test.go ├── uattrs.go ├── uattrs_test.go ├── xor.go ├── xor_test.go ├── xoraddr.go └── xoraddr_test.go ├── turn ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── appveyor.yml ├── chann.go ├── chann_test.go ├── channeldata.go ├── chrome_test.go ├── cmd │ └── turn-client │ │ └── client.go ├── data.go ├── data_test.go ├── dontfrag.go ├── dontfrag_test.go ├── evenport.go ├── evenport_test.go ├── examples │ └── turn-setters │ │ └── corpus │ │ ├── 1b6453892473a467d07372d45eb05abc2031647a-1 │ │ ├── 55df2a59ed6a888ee2f0cdfdcc8582696702de7a-1 │ │ ├── a15226820da790455c2899eb3d3a0c26ae48f18e-1 │ │ ├── aff3f3449225ed816660ce1e2fe227531915a659-2 │ │ └── da39a3ee5e6b4b0d3255bfef95601890afd80709 ├── fuzz.go ├── fuzz_test.go ├── lifetime.go ├── lifetime_test.go ├── peeraddr.go ├── peeraddr_test.go ├── relayedaddr.go ├── relayedaddr_test.go ├── reqtrans.go ├── reqtrans_test.go ├── rsrvtoken.go ├── rsrvtoken_test.go ├── testdata │ └── 01_chromeallocreq.hex ├── turn.go └── turn_test.go └── utils └── util.go /README.md: -------------------------------------------------------------------------------- 1 | # goice 2 | go implementation of ICE( RFC 5245),it doesn't depends any c library,so it can runs on all the platforms which go support. it only supports one component now. 3 | 4 | ## how to use 5 | please reference ice/exmaple/example.go, this a simple example to present how to setup a p2p connection between two nodes without any signal server. 6 | 7 | ## how to setup a turnserver on ubutu 8 | 9 | ```bash 10 | apt install turnserver 11 | turnserver -u bai:bai 12 | ``` 13 | turnserver's default port is 3478,make sure your firewall doesn't block it. 14 | -------------------------------------------------------------------------------- /README_zh.MD: -------------------------------------------------------------------------------- 1 | #goice 2 | goice 是一个 纯 go 实现的ICE(RFC5245)P2P 通信库,不依赖任何 c 语言库,这样可以保证 goice 可以运行在所有支持 go 的平台上. 3 | 4 | ## 如何使用 5 | 请参考ice/example/example.go, 这是一个简单的不依赖信令服务器,演示两个节点如何建立 P2P 连接. 6 | 他依赖一个 turnserver. 我不能保证这个 turnserver 一直都能正常运行.如果turnserver 不能正常工作,请搭建自己的 turnserver. 7 | 8 | ## 在 ubuntu上搭建自己的 turnserver 9 | ```bash 10 | apt install turnserver 11 | turnserver -u bai:bai 12 | ``` 13 | turnserver 默认端口3478,请保证外部可以使用. 14 | -------------------------------------------------------------------------------- /ice/AUTHORS: -------------------------------------------------------------------------------- 1 | steven bai 2 | Aleksandr Razumov 3 | Aliaksandr Valialkin 4 | 5 | -------------------------------------------------------------------------------- /ice/README.md: -------------------------------------------------------------------------------- 1 | # ice 2 | Package ice implements [RFC 5245](https://tools.ietf.org/html/rfc5245) 3 | Interactive Connectivity Establishment (ICE): 4 | A Protocol for Network Address Translator (NAT) Traversal for Offer/Answer Protocols. 5 | 6 | Currently in active development. 7 | 8 | Needs go 1.7 or better. 9 | 10 | it only supports one component now, and when it's role is controlling, it must use aggressive mode. 11 | -------------------------------------------------------------------------------- /ice/attr/icecontrolled.go: -------------------------------------------------------------------------------- 1 | package attr 2 | 3 | import ( 4 | "encoding/binary" 5 | "strconv" 6 | 7 | "github.com/nkbai/goice/stun" 8 | "github.com/nkbai/goice/turn" 9 | ) 10 | 11 | // IceControlled role of ice 12 | type IceControlled uint64 13 | 14 | func (n IceControlled) String() string { return strconv.Itoa(int(n)) } 15 | 16 | const iceControlledSize = 8 17 | 18 | // AddTo adds ICE-CONTROLLED to message. 19 | func (n IceControlled) AddTo(m *stun.Message) error { 20 | v := make([]byte, iceControlledSize) 21 | binary.BigEndian.PutUint64(v, uint64(n)) 22 | m.Add(stun.AttrICEControlled, v) 23 | return nil 24 | } 25 | 26 | // GetFrom decodes ICE-CONTROLLED from message. 27 | func (n *IceControlled) GetFrom(m *stun.Message) error { 28 | v, err := m.Get(stun.AttrICEControlled) 29 | if err != nil { 30 | return err 31 | } 32 | if len(v) != iceControlledSize { 33 | return &turn.BadAttrLength{ 34 | Attr: stun.AttrICEControlled, 35 | Got: len(v), 36 | Expected: iceControlledSize, 37 | } 38 | } 39 | *n = IceControlled(binary.BigEndian.Uint64(v)) 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /ice/attr/icecontrolling.go: -------------------------------------------------------------------------------- 1 | package attr 2 | 3 | import ( 4 | "encoding/binary" 5 | "math/rand" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/nkbai/goice/stun" 10 | "github.com/nkbai/goice/turn" 11 | ) 12 | 13 | //IceControlling role of ice 14 | type IceControlling uint64 15 | 16 | func (n IceControlling) String() string { return strconv.Itoa(int(n)) } 17 | 18 | const iceControllingSize = 8 19 | 20 | // AddTo adds ICE-CONTROLLING to message. 21 | func (n IceControlling) AddTo(m *stun.Message) error { 22 | v := make([]byte, iceControllingSize) 23 | binary.BigEndian.PutUint64(v, uint64(n)) 24 | m.Add(stun.AttrICEControlling, v) 25 | return nil 26 | } 27 | 28 | // GetFrom decodes ICE-CONTROLLING from message. 29 | func (n *IceControlling) GetFrom(m *stun.Message) error { 30 | v, err := m.Get(stun.AttrICEControlling) 31 | if err != nil { 32 | return err 33 | } 34 | if len(v) != iceControllingSize { 35 | return &turn.BadAttrLength{ 36 | Attr: stun.AttrICEControlling, 37 | Got: len(v), 38 | Expected: iceControllingSize, 39 | } 40 | } 41 | *n = IceControlling(binary.BigEndian.Uint64(v)) 42 | return nil 43 | } 44 | 45 | //RandUint64 random uint64 46 | func RandUint64() uint64 { 47 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 48 | return r.Uint64() 49 | } 50 | -------------------------------------------------------------------------------- /ice/attr/priority.go: -------------------------------------------------------------------------------- 1 | package attr 2 | 3 | import ( 4 | "strconv" 5 | 6 | "encoding/binary" 7 | 8 | "github.com/nkbai/goice/stun" 9 | "github.com/nkbai/goice/turn" 10 | ) 11 | 12 | //Priority https://trac.tools.ietf.org/html/rfc5245#section-19.1 13 | type Priority uint32 // encoded as uint16 14 | 15 | func (n Priority) String() string { return strconv.Itoa(int(n)) } 16 | 17 | /* 18 | It is a 32-bit unsigned integer, and has an attribute 19 | value of 0x0024. 20 | */ 21 | const prioritySize = 4 22 | 23 | // AddTo adds PRIORITY to message. 24 | func (n Priority) AddTo(m *stun.Message) error { 25 | v := make([]byte, prioritySize) 26 | binary.BigEndian.PutUint32(v, uint32(n)) 27 | // v[2:4] are zeroes (RFFU = 0) 28 | m.Add(stun.AttrPriority, v) 29 | return nil 30 | } 31 | 32 | // GetFrom decodes PRIORITY from message. 33 | func (n *Priority) GetFrom(m *stun.Message) error { 34 | v, err := m.Get(stun.AttrPriority) 35 | if err != nil { 36 | return err 37 | } 38 | if len(v) != prioritySize { 39 | return &turn.BadAttrLength{ 40 | Attr: stun.AttrPriority, 41 | Got: len(v), 42 | Expected: prioritySize, 43 | } 44 | } 45 | *n = Priority(binary.BigEndian.Uint32(v)) 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /ice/attr/usercandidate.go: -------------------------------------------------------------------------------- 1 | package attr 2 | 3 | import "github.com/nkbai/goice/stun" 4 | 5 | type useCandidateSetter struct{} 6 | 7 | func (useCandidateSetter) AddTo(m *stun.Message) error { 8 | m.Add(stun.AttrUseCandidate, nil) 9 | return nil 10 | } 11 | 12 | // UseCandidate is Setter for m.UseCandidate. 13 | var UseCandidate stun.Setter = useCandidateSetter{} 14 | -------------------------------------------------------------------------------- /ice/candidate_test.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "fmt" 10 | 11 | "github.com/nkbai/goice/sdp" 12 | "github.com/nkbai/goice/utils" 13 | "github.com/nkbai/log" 14 | ) 15 | 16 | func loadData(tb testing.TB, name string) []byte { 17 | name = filepath.Join("testdata", name) 18 | f, err := os.Open(name) 19 | if err != nil { 20 | tb.Fatal(err) 21 | } 22 | defer func() { 23 | if errClose := f.Close(); errClose != nil { 24 | tb.Fatal(errClose) 25 | } 26 | }() 27 | v, err := ioutil.ReadAll(f) 28 | if err != nil { 29 | tb.Fatal(err) 30 | } 31 | return v 32 | } 33 | 34 | func TestConnectionAddress(t *testing.T) { 35 | data := loadData(t, "candidates_ex1.sdp") 36 | s, err := sdp.DecodeSession(data, nil) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | for _, c := range s { 41 | p := candidateParser{ 42 | c: new(Candidate), 43 | buf: c.Value, 44 | } 45 | if err = p.parse(); err != nil { 46 | t.Fatal(err) 47 | } 48 | log.Trace(fmt.Sprintf("c= %s", utils.StringInterface(p.c, 3))) 49 | } 50 | 51 | // a=candidate:3862931549 1 udp 2113937151 192.168.220.128 56032 52 | // foundation ---┘ | | | | | 53 | // component id --------┘ | | | | 54 | // transport -----------┘ | | | 55 | // priority ------------------┘ | | 56 | // conn. address -------------------------------┘ | 57 | // port ------------------------------------------┘ 58 | } 59 | 60 | func TestParse(t *testing.T) { 61 | //data := loadData(t, "candidates_ex1.sdp") 62 | //s, err := sdp.decodeSession(data, nil) 63 | //if err != nil { 64 | // t.Fatal(err) 65 | //} 66 | //expected := []Candidate{} 67 | tCases := []struct { 68 | input []byte 69 | expected Candidate 70 | }{ 71 | { 72 | input: []byte("candidate:3862931549 1 udp 2113937151 192.168.220.128 56032 typ host generation 0 network-cost 50 alpha beta ??"), 73 | expected: Candidate{ 74 | Foundation: 3862931549, 75 | ComponentID: 1, 76 | Priority: 2113937151, 77 | addr: "192.168.220.128:56032", 78 | Type: CandidateHost, 79 | transport: TransportUDP, 80 | NetworkCost: 50, 81 | Attributes: Attributes{ 82 | Attribute{ 83 | Key: []byte("alpha"), 84 | Value: []byte("beta"), 85 | }, 86 | }, 87 | }}, // 0 88 | { 89 | input: []byte("candidate:842163049 1 udp 1677729535 213.141.156.236 55726 typ srflx raddr"), 90 | expected: Candidate{ 91 | Foundation: 842163049, 92 | ComponentID: 1, 93 | Priority: 1677729535, 94 | addr: "213.141.156.236:55726", 95 | Type: CandidateServerReflexive, 96 | transport: TransportUDP, 97 | }, 98 | }, { 99 | input: []byte("candidate:842163049 1 udp 1677729535 b2.cydev.ru 56024 typ srflx raddr 10.1.22.220 rport 56024 generation 0 ufrag eM2ytqY8D5Q07RAn"), 100 | expected: Candidate{ 101 | Foundation: 842163049, 102 | ComponentID: 1, 103 | Priority: 1677729535, 104 | addr: "b2.cydev.ru:56024", 105 | Type: CandidateServerReflexive, 106 | relatedAddr: "10.1.22.220:56024", 107 | Generation: 0, 108 | transport: TransportUDP, 109 | Attributes: Attributes{ 110 | Attribute{ 111 | Key: []byte("ufrag"), 112 | Value: []byte("eM2ytqY8D5Q07RAn"), 113 | }, 114 | }, 115 | }, 116 | }, 117 | } 118 | 119 | for i, c := range tCases { 120 | parser := candidateParser{ 121 | buf: c.input, 122 | c: new(Candidate), 123 | } 124 | if err := parser.parse(); err != nil { 125 | t.Errorf("[%d]: unexpected error %s", 126 | i, err, 127 | ) 128 | } 129 | if !c.expected.Equal(parser.c) { 130 | t.Errorf("[%d]: %#v != %#v (exp)", 131 | i, parser.c, c.expected, 132 | ) 133 | } 134 | } 135 | } 136 | 137 | func BenchmarkParse(b *testing.B) { 138 | data := loadData(b, "candidates_ex1.sdp") 139 | s, err := sdp.DecodeSession(data, nil) 140 | if err != nil { 141 | b.Fatal(err) 142 | } 143 | b.ReportAllocs() 144 | value := s[0].Value 145 | p := candidateParser{ 146 | c: new(Candidate), 147 | } 148 | for i := 0; i < b.N; i++ { 149 | p.buf = value 150 | if err = p.parse(); err != nil { 151 | b.Fatal(err) 152 | } 153 | p.c.reset() 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /ice/checklist.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | /* 9 | SessionCheckState describes the state of ICE check. 10 | */ 11 | type SessionCheckState int 12 | 13 | const ( 14 | /** 15 | * A check for this pair hasn't been performed, and it can't 16 | * yet be performed until some other check succeeds, allowing this 17 | * pair to unfreeze and move into the Waiting state. 18 | */ 19 | checkStateFrozen SessionCheckState = iota 20 | /** 21 | * A check has not been performed for this pair, and can be 22 | * performed as soon as it is the highest priority Waiting pair on 23 | * the check list. 24 | */ 25 | checkStateWaiting 26 | /** 27 | * A check has not been performed for this pair, and can be 28 | * performed as soon as it is the highest priority Waiting pair on 29 | * the check list. 30 | */ 31 | checkStateInProgress 32 | /** 33 | * A check has not been performed for this pair, and can be 34 | * performed as soon as it is the highest priority Waiting pair on 35 | * the check list. 36 | */ 37 | checkStateSucced 38 | /** 39 | * A check for this pair was already done and failed, either 40 | * never producing any response or producing an unrecoverable failure 41 | * response. 42 | */ 43 | checkStateFailed 44 | ) 45 | 46 | func (s SessionCheckState) String() string { 47 | switch s { 48 | case checkStateFrozen: 49 | return "frozen" 50 | case checkStateWaiting: 51 | return "waiting" 52 | case checkStateInProgress: 53 | return "inprogress" 54 | case checkStateSucced: 55 | return "success" 56 | case checkStateFailed: 57 | return "failed" 58 | } 59 | return "unknown" 60 | } 61 | 62 | /** 63 | * This structure describes an ICE connectivity check. An ICE check 64 | * contains a candidate pair, and will involve sending STUN Binding 65 | * Request transaction for the purposes of verifying connectivity. 66 | * A check is sent from the local candidate to the remote candidate 67 | * of a candidate pair. 68 | */ 69 | type sessionCheck struct { 70 | localCandidate *Candidate 71 | remoteCandidate *Candidate 72 | key string //简单与其他 check 区分,更多用于调试. 73 | priority uint64 74 | state SessionCheckState 75 | /** 76 | * Flag to indicate whether this check is nominated. A nominated check 77 | * contains USE-CANDIDATE attribute in its STUN Binding request. 78 | */ 79 | nominated bool 80 | /* 81 | what error 82 | */ 83 | err error 84 | } 85 | 86 | func (s *sessionCheck) String() string { 87 | return fmt.Sprintf("{l=%s,r=%s,priorit=%x,state=%s,nominated=%v,err=%s}", 88 | s.localCandidate.addr, s.remoteCandidate.addr, s.priority, s.state, s.nominated, s.err) 89 | } 90 | 91 | type sessionCheckList struct { 92 | checks []*sessionCheck 93 | } 94 | 95 | func (sc *sessionCheckList) String() string { 96 | w := new(bytes.Buffer) 97 | for i, v := range sc.checks { 98 | fmt.Fprintf(w, "\t [%d]=%s\n", i, v) 99 | } 100 | fmt.Fprintf(w, "}") 101 | return w.String() 102 | } 103 | func (sc *sessionCheckList) Len() int { 104 | return len(sc.checks) 105 | } 106 | func (sc *sessionCheckList) Less(i, j int) bool { 107 | return sc.checks[i].priority > sc.checks[j].priority 108 | } 109 | 110 | func (sc *sessionCheckList) Swap(i, j int) { 111 | var t *sessionCheck 112 | t = sc.checks[i] 113 | sc.checks[i] = sc.checks[j] 114 | sc.checks[j] = t 115 | } 116 | -------------------------------------------------------------------------------- /ice/constant.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import "time" 4 | 5 | /** 6 | * Maximum number of STUN transmission count. 7 | * 8 | * Default: 7 (as per RFC 3489-bis) 9 | */ 10 | const maxRetryBindingRequest = 7 11 | 12 | /** 13 | * The STUN transaction timeout value, in miliseconds. 14 | * After the last retransmission is sent and if no response is received 15 | * after this time, the STUN transaction will be considered to have failed. 16 | * 17 | * The default value is 16x RTO (as per RFC 3489-bis). 18 | */ 19 | const stunTimeoutValue = 1600 * time.Millisecond 20 | 21 | /** 22 | * The default initial STUN round-trip time estimation (the RTO value 23 | * in RFC 3489-bis), in miliseconds. 24 | * This value is used to control the STUN request 25 | * retransmit time. The initial value of retransmission interval 26 | * would be set to this value, and will be doubled after each 27 | * retransmission. 28 | */ 29 | const defaultRTOValue = time.Millisecond * 100 30 | 31 | /** 32 | * The TURN permission lifetime setting. This value should be taken from the 33 | * TURN protocol specification. 34 | */ 35 | const turnPermissionTimeout = time.Second * 300 36 | 37 | /** 38 | * The TURN channel binding lifetime. This value should be taken from the 39 | * TURN protocol specification. 40 | */ 41 | const turnChannelTimeout = time.Second * 600 42 | 43 | /** 44 | * Number of seconds to refresh the permission/channel binding before the 45 | * permission/channel binding expires. This value should be greater than 46 | * PJ_TURN_PERM_TIMEOUT setting. 47 | */ 48 | const turnRefreshSecondsBefore = time.Second * 60 49 | 50 | /** 51 | * The TURN session timer heart beat interval. When this timer occurs, the 52 | * TURN session will scan all the permissions/channel bindings to see which 53 | * need to be refreshed. 54 | */ 55 | const turnKeepAliveSecond = time.Second * 15 56 | 57 | /** 58 | * Duration to keep response in the cache, in msec. 59 | * 60 | * Default: 10000 (as per RFC 3489-bis) 61 | */ 62 | const stunResponseCacheDuration = time.Second * 10 63 | -------------------------------------------------------------------------------- /ice/error.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import "errors" 4 | 5 | var ( 6 | errCheckRetry = errors.New("retry again") 7 | errTriedTooManyTimes = errors.New("have tried too many times") 8 | errInvalidStunMessage = errors.New("Invalid STUN message") 9 | errStunInvalidLength = errors.New("Invalid STUN message length") 10 | errStunUnknownType = errors.New("Invalid or unexpected STUN message type") 11 | errStunTimeout = errors.New("STUN transaction has timed out") 12 | 13 | errStunTooManyAttributes = errors.New("Too many STUN attributes") 14 | errStunAttributeLength = errors.New("Invalid STUN attribute length") 15 | ) 16 | -------------------------------------------------------------------------------- /ice/example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | "time" 7 | 8 | "fmt" 9 | 10 | "github.com/nkbai/goice/ice" 11 | "github.com/nkbai/log" 12 | ) 13 | 14 | const ( 15 | typHost = 1 16 | typStun = 2 17 | typTurn = 3 18 | ) 19 | 20 | type icecb struct { 21 | data chan []byte 22 | iceresult chan error 23 | name string 24 | } 25 | 26 | func newicecb(name string) *icecb { 27 | return &icecb{ 28 | name: name, 29 | data: make(chan []byte, 1), 30 | iceresult: make(chan error, 1), 31 | } 32 | } 33 | func (c *icecb) OnReceiveData(data []byte, from net.Addr) { 34 | c.data <- data 35 | } 36 | 37 | /* 38 | Callback to report status of various ICE operations. 39 | */ 40 | func (c *icecb) OnIceComplete(result error) { 41 | c.iceresult <- result 42 | log.Trace(fmt.Sprintf("%s negotiation complete", c.name)) 43 | } 44 | func setupIcePair(typ int) (s1, s2 *ice.StreamTransport, err error) { 45 | var cfg *ice.TransportConfig 46 | switch typ { 47 | case typHost: 48 | cfg = ice.NewTransportConfigHostonly() 49 | case typStun: 50 | cfg = ice.NewTransportConfigWithStun("182.254.155.208:3478") 51 | case typTurn: 52 | cfg = ice.NewTransportConfigWithTurn("182.254.155.208:3478", "bai", "bai") 53 | } 54 | s1, err = ice.NewIceStreamTransport(cfg, "s1") 55 | if err != nil { 56 | return 57 | } 58 | s2, err = ice.NewIceStreamTransport(cfg, "s2") 59 | log.Trace("-----------------------------------------") 60 | return 61 | } 62 | func main() { 63 | s1, s2, err := setupIcePair(typTurn) 64 | if err != nil { 65 | log.Crit(err.Error()) 66 | return 67 | } 68 | cb1 := newicecb("s1") 69 | cb2 := newicecb("s2") 70 | s1.SetCallBack(cb1) 71 | s2.SetCallBack(cb2) 72 | err = s1.InitIce(ice.SessionRoleControlling) 73 | if err != nil { 74 | log.Crit(err.Error()) 75 | return 76 | } 77 | err = s2.InitIce(ice.SessionRoleControlled) 78 | if err != nil { 79 | log.Crit(err.Error()) 80 | return 81 | } 82 | rsdp, err := s2.EncodeSession() 83 | if err != nil { 84 | log.Crit(err.Error()) 85 | return 86 | } 87 | err = s1.StartNegotiation(rsdp) 88 | if err != nil { 89 | log.Crit(err.Error()) 90 | return 91 | } 92 | lsdp, err := s1.EncodeSession() 93 | if err != nil { 94 | log.Crit(err.Error()) 95 | return 96 | } 97 | err = s2.StartNegotiation(lsdp) 98 | if err != nil { 99 | log.Crit(err.Error()) 100 | return 101 | } 102 | select { 103 | case <-time.After(10 * time.Second): 104 | log.Error("s1 negotiation timeout") 105 | return 106 | case err = <-cb1.iceresult: 107 | if err != nil { 108 | log.Error(fmt.Sprintf("s1 negotiation failed %s", err)) 109 | return 110 | } 111 | } 112 | select { 113 | case <-time.After(10 * time.Second): 114 | log.Error("s2 negotiation timeout") 115 | return 116 | case err = <-cb2.iceresult: 117 | if err != nil { 118 | log.Error(fmt.Sprintf("s2 negotiation failed %s", err)) 119 | return 120 | } 121 | } 122 | s1data := []byte("hello,s2") 123 | s2data := []byte("hello,s1") 124 | err = s1.SendData(s1data) 125 | if err != nil { 126 | log.Crit(err.Error()) 127 | return 128 | } 129 | err = s2.SendData(s2data) 130 | if err != nil { 131 | log.Crit(err.Error()) 132 | return 133 | } 134 | select { 135 | case <-time.After(10 * time.Second): 136 | log.Error("s2 recevied timeout") 137 | return 138 | case data := <-cb2.data: 139 | if !bytes.Equal(data, s1data) { 140 | log.Error(fmt.Sprintf("s2 recevied error ,got %s", string(data))) 141 | return 142 | } 143 | } 144 | select { 145 | case <-time.After(10 * time.Second): 146 | log.Error("s1 recevied timeout") 147 | return 148 | case data := <-cb1.data: 149 | if !bytes.Equal(data, s2data) { 150 | log.Error(fmt.Sprintf("s1 recevied error ,got %s", string(data))) 151 | return 152 | } 153 | } 154 | log.Info("ice complete...") 155 | } 156 | -------------------------------------------------------------------------------- /ice/hostonlysock.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | /* 11 | HostOnlySock 没有指定 turn /stun server 也是允许的, 12 | */ 13 | type HostOnlySock struct { 14 | localCandidates []string 15 | } 16 | 17 | //GetCandidates Gather interface 18 | func (h *HostOnlySock) GetCandidates() (candidates []*Candidate, err error) { 19 | addrs, err := DefaultGatherer.Gather() 20 | if err != nil { 21 | return 22 | } 23 | if len(addrs) < 0 { 24 | //no ip 25 | err = errors.New("no network") 26 | } 27 | port := rand.NewSource(time.Now().UnixNano()).Int63() % 50000 28 | primaryAddress := fmt.Sprintf("%s:%d", addrs[0].IP.String(), port) 29 | candidates, err = getLocalCandidates(primaryAddress) 30 | if err != nil { 31 | return 32 | } 33 | for _, c := range candidates { 34 | h.localCandidates = append(h.localCandidates, c.addr) 35 | } 36 | return 37 | } 38 | 39 | //Close implements io.Closer 40 | func (h *HostOnlySock) Close() { 41 | 42 | } 43 | 44 | /* 45 | address need to listen for input stun binding request... 46 | */ 47 | func (h *HostOnlySock) getListenCandidiates() []string { 48 | return h.localCandidates 49 | } 50 | -------------------------------------------------------------------------------- /ice/ice-gather/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/nkbai/goice/ice" 8 | ) 9 | 10 | func main() { 11 | addrs, err := ice.DefaultGatherer.Gather() 12 | if err != nil { 13 | log.Fatal(fmt.Sprintf("failed to gather: %s", err)) 14 | } 15 | for _, a := range addrs { 16 | fmt.Printf("%s\n", a) 17 | //laddr, err := net.ResolveUDPAddr("udp", 18 | // a.ZeroPortAddr(), 19 | //) 20 | //if err != nil { 21 | // log.Fatal(err) 22 | //} 23 | //c, err := net.ListenUDP("udp", laddr) 24 | //if err != nil { 25 | // fmt.Println(" ", "failed:", err) 26 | // continue 27 | //} 28 | //fmt.Println(" ", "bind ok", c.LocalAddr()) 29 | //c.Close() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ice/interface.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import "github.com/nkbai/goice/stun" 4 | 5 | type candidateGetter interface { 6 | /* 7 | 获取有一部分信息的candidiate.第一个是本机主要地址,最后一个是缺省 Candidate 8 | */ 9 | GetCandidates() (candidates []*Candidate, err error) 10 | } 11 | 12 | //treat stun and turn as the same ... 13 | type stunTranporter interface { 14 | candidateGetter 15 | Close() 16 | getListenCandidiates() []string 17 | /* 18 | transporter is using turn? 19 | */ 20 | //IsTurn() bool 21 | } 22 | 23 | /* 24 | 建立连接需要的本地的 简易stun 服务器. 25 | */ 26 | type serverSocker interface { 27 | /* 28 | 指定 从 from 到 to 发送一个消息 29 | from 有可能是本地地址,也有可能是 turn server relay 的地址 30 | */ 31 | sendStunMessageSync(msg *stun.Message, fromaddr, toaddr string) (res *stun.Message, err error) 32 | /* 33 | 暂时没用,先留着 34 | */ 35 | sendStunMessageWithResult(msg *stun.Message, fromaddr, toaddr string) (key stun.TransactionID, ch chan *serverSockResponse, err error) 36 | /* 37 | 参数含义和 sync 是一致的,不用等待结果. 38 | */ 39 | sendStunMessageAsync(msg *stun.Message, fromaddr, toaddr string) error 40 | /* 41 | 从 from 到 to 发送一个数据包, 42 | 如果 from 是本机地址,则直接发送, 43 | 如果是 turn server relay address, 那么需要经由 turn server 中转. 44 | 也就是会把 data 封装到 SendIndication 或者 ChannelDataRequest中 45 | */ 46 | sendData(data []byte, fromaddr, toaddr string) error 47 | /* 48 | 关闭连接 49 | */ 50 | Close() 51 | /* 52 | ice check 真正完毕以后, 53 | 需要开启刷新权限以及 keep alive 等操作. 54 | todo 这里的 mode 定义并不清晰,需要梳理. 55 | */ 56 | FinishNegotiation(mode serverSockMode) 57 | //StartRefresh() 58 | } 59 | -------------------------------------------------------------------------------- /ice/stunserversock_test.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "testing" 5 | 6 | "fmt" 7 | 8 | "github.com/nkbai/log" 9 | "github.com/nkbai/goice/stun" 10 | ) 11 | 12 | type mockcb struct { 13 | s serverSocker 14 | } 15 | 16 | /* 17 | 收到一个 stun.Message, 可能是 Bind Request/Bind Response 等等. 18 | */ 19 | func (m *mockcb) RecieveStunMessage(localAddr, remoteAddr string, req *stun.Message) { 20 | if req.Type != stun.BindingRequest { 21 | return 22 | } 23 | log.Info(fmt.Sprintf("recevied binding request %s<----%s", localAddr, remoteAddr)) 24 | var res = new(stun.Message) 25 | from := addrToUDPAddr(remoteAddr) 26 | 27 | err := res.Build( 28 | stun.NewTransactionIDSetter(req.TransactionID), 29 | stun.NewType(stun.MethodBinding, stun.ClassSuccessResponse), 30 | software, 31 | &stun.XORMappedAddress{ 32 | IP: from.IP, 33 | Port: from.Port, 34 | }, 35 | stun.Fingerprint, 36 | ) 37 | if err != nil { 38 | panic(fmt.Sprintf("build res message error %s", err)) 39 | } 40 | m.s.sendStunMessageAsync(res, localAddr, remoteAddr) 41 | return 42 | } 43 | 44 | /* 45 | ICE 协商建立连接以后,收到了对方发过来的数据,可能是经过 turn server 中转的 channel data( 不接受 sendData data request),也可能直接是数据. 46 | 如果是经过 turn server 中转的, channelNumber 一定介于0x4000-0x7fff 之间.否则一定为0 47 | */ 48 | func (m *mockcb) ReceiveData(localAddr, peerAddr string, data []byte) { 49 | 50 | } 51 | 52 | //binding request 和普通的 stun message 一样处理. 53 | //func (s *stunServerSock) processBindingRequest(from net.Addr, req *stun.Message) { 54 | 55 | //notauthrized: 56 | // res.Build(stun.NewTransactionIDSetter(req.TransactionID), stun.BindingError, 57 | // stun.CodeUnauthorised, software, stun.Fingerprint) 58 | // s.sendStunMessageAsync(res, from) 59 | //} 60 | func setupTestServerSock() (s1, s2 *stunServerSock) { 61 | var err error 62 | mybindaddr := "127.0.0.1:8700" 63 | peerbindaddr := "127.0.0.1:8800" 64 | m1 := new(mockcb) 65 | m2 := new(mockcb) 66 | s1, err = newStunServerSock(mybindaddr, m1, "s1") 67 | if err != nil { 68 | log.Crit(fmt.Sprintf("create new sock error %s %s", mybindaddr, err)) 69 | } 70 | s2, err = newStunServerSock(peerbindaddr, m2, "s2") 71 | if err != nil { 72 | log.Crit(fmt.Sprintf("creat new sock error %s %s", peerbindaddr, err)) 73 | } 74 | m1.s = s1 75 | m2.s = s2 76 | return s1, s2 77 | } 78 | func TestNewServerSock(t *testing.T) { 79 | s1, s2 := setupTestServerSock() 80 | req, _ := stun.Build(stun.TransactionIDSetter, stun.BindingRequest, software, stun.Fingerprint) 81 | res, err := s1.sendStunMessageSync(req, s1.Addr, s2.Addr) 82 | if err != nil { 83 | t.Error(err) 84 | return 85 | } 86 | if res.Type != stun.BindingSuccess { 87 | t.Error("should success") 88 | return 89 | } 90 | log.Trace(fmt.Sprintf("s1 received :%s", res.String())) 91 | 92 | } 93 | -------------------------------------------------------------------------------- /ice/stunsock.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | "time" 7 | 8 | "fmt" 9 | 10 | "github.com/nkbai/log" 11 | "github.com/nkbai/goice/stun" 12 | ) 13 | 14 | const defaultReadDeadLine = time.Second * 10 15 | 16 | /* 17 | 用于在没有 turn server 而只有 stun server 的情形下,收集本机候选地址. 18 | */ 19 | type stunSocket struct { 20 | ServerAddr string 21 | MappedAddr net.UDPAddr 22 | LocalAddr string // local addr used to connect server 23 | Client *stun.Client 24 | ReadDeadline time.Duration 25 | localAddrs []string //for listen 26 | } 27 | 28 | func newStunSocket(serverAddr string) (s *stunSocket, err error) { 29 | s = &stunSocket{ 30 | ServerAddr: serverAddr, 31 | ReadDeadline: defaultReadDeadLine, 32 | } 33 | conn, err := net.Dial("udp", serverAddr) 34 | if err != nil { 35 | log.Crit(fmt.Sprintf("failed to dial:%s", err)) 36 | } 37 | client, err := stun.NewClient(stun.ClientOptions{ 38 | Connection: conn, 39 | }) 40 | if err != nil { 41 | return 42 | } 43 | s.Client = client 44 | s.LocalAddr = conn.(*net.UDPConn).LocalAddr().String() 45 | return 46 | } 47 | 48 | //get mapped address from server 49 | func (s *stunSocket) mapAddress() error { 50 | deadline := time.Now().Add(s.ReadDeadline) 51 | var err error 52 | wg := sync.WaitGroup{} 53 | wg.Add(1) 54 | err = s.Client.Do(stun.MustBuild(stun.TransactionIDSetter, stun.BindingRequest), deadline, func(res stun.Event) { 55 | defer wg.Done() 56 | if res.Error != nil { 57 | err = res.Error 58 | return 59 | } 60 | var xorAddr stun.XORMappedAddress 61 | if err = xorAddr.GetFrom(res.Message); err != nil { 62 | var addr stun.MappedAddress 63 | err = addr.GetFrom(res.Message) 64 | if err != nil { 65 | return 66 | } 67 | s.MappedAddr = net.UDPAddr{IP: addr.IP, Port: addr.Port} 68 | } else { 69 | s.MappedAddr = net.UDPAddr{IP: xorAddr.IP, Port: xorAddr.Port} 70 | } 71 | }) 72 | wg.Wait() 73 | //keep alive todo 74 | return err 75 | } 76 | 77 | /* 78 | 获取有一部分信息的candidiate.第一个是本机主要地址,最后一个是缺省 Candidate 79 | */ 80 | func (s *stunSocket) GetCandidates() (candidates []*Candidate, err error) { 81 | err = s.mapAddress() 82 | if err != nil { 83 | return 84 | } 85 | c := new(Candidate) 86 | c.baseAddr = s.LocalAddr 87 | c.Type = CandidateServerReflexive 88 | c.addr = s.MappedAddr.String() 89 | c.Foundation = calcFoundation(c.baseAddr) 90 | candidates, err = getLocalCandidates(c.baseAddr) 91 | if err != nil { 92 | return 93 | } 94 | for _, c := range candidates { 95 | s.localAddrs = append(s.localAddrs, c.addr) 96 | } 97 | if c.addr != c.baseAddr { //we have a public ip 98 | candidates = append(candidates, c) 99 | } 100 | return 101 | } 102 | 103 | func (s *stunSocket) Close() { 104 | if s.Client != nil { 105 | s.Client.Close() 106 | } 107 | } 108 | 109 | /* 110 | address need to listen for input stun binding request... 111 | */ 112 | func (s *stunSocket) getListenCandidiates() []string { 113 | return s.localAddrs 114 | } 115 | -------------------------------------------------------------------------------- /ice/stunsock_test.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/davecgh/go-spew/spew" 7 | ) 8 | 9 | func TestNewStunSocket(t *testing.T) { 10 | stun, err := newStunSocket("182.254.155.208:3478") 11 | if err != nil { 12 | t.Error(err) 13 | return 14 | } 15 | cands, err := stun.GetCandidates() 16 | if err != nil { 17 | t.Error(err) 18 | return 19 | } 20 | spew.Dump("cands", cands) 21 | } 22 | -------------------------------------------------------------------------------- /ice/test.txt: -------------------------------------------------------------------------------- 1 | 2 | v=0 3 | o=- 3414953978 3414953978 IN IP4 localhost 4 | s=ice 5 | t=0 0 6 | a=ice-ufrag:77188b05 7 | a=ice-pwd:6c4f3258 8 | m=audio 57941 RTP/AVP 0 9 | c=IN IP4 182.254.155.208 10 | a=candidate:Sc0a80071 1 UDP 1694498815 113.206.156.21 45825 typ srflx 11 | a=candidate:Hc0a80071 1 UDP 2130706431 192.168.0.113 58451 typ host 12 | a=candidate:Ha000011 1 UDP 2130706431 10.0.0.17 58451 typ host 13 | a=candidate:Rb6fe9bd0 1 UDP 16777215 182.254.155.208 57941 typ relay 14 | 15 | -------------------------------------------------------------------------------- /ice/testdata/candidates_ex1.sdp: -------------------------------------------------------------------------------- 1 | a=candidate:3862931549 1 udp 2113937151 192.168.220.128 56032 typ host generation 0 network-cost 50 alpha beta ?? 2 | a=candidate:842163049 1 udp 1677729535 213.141.156.236 55726 typ srflx raddr 3 | a=candidate:2983135859 1 udp 2113937151 10.1.22.220 56024 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 4 | a=candidate:4294175796 1 udp 2113939711 2001:67c:56c:100::3 36737 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 5 | a=candidate:2983135859 2 udp 2113937150 10.1.22.220 51941 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 6 | a=candidate:4294175796 2 udp 2113939710 2001:67c:56c:100::3 42279 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 7 | a=candidate:842163049 2 udp 1677729534 91.225.236.99 51941 typ srflx raddr 10.1.22.220 rport 51941 generation 0 ufrag eM2ytqY8D5Q07RAn 8 | a=candidate:842163049 1 udp 1677729535 91.225.236.99 56024 typ srflx raddr 10.1.22.220 rport 56024 generation 0 ufrag eM2ytqY8D5Q07RAn 9 | a=candidate:842163049 1 udp 1677729535 b2.cydev.ru 56024 typ srflx raddr 10.1.22.220 rport 56024 generation 0 ufrag eM2ytqY8D5Q07RAn -------------------------------------------------------------------------------- /ice/turnserversock_test.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nkbai/log" 7 | "github.com/nkbai/goice/stun" 8 | ) 9 | 10 | func setupTurnServerSock() (s1, s2 *turnServerSock) { 11 | t1 := newTestTurnSock() 12 | t2 := newTestTurnSock() 13 | err := t1.allocateAddress() 14 | if err != nil { 15 | panic(err) 16 | } 17 | err = t2.allocateAddress() 18 | if err != nil { 19 | panic(err) 20 | } 21 | t1.Close() 22 | t2.Close() 23 | cfg1 := &turnServerSockConfig{ 24 | user: t1.user, 25 | password: t1.password, 26 | nonce: t1.nonce, 27 | realm: t1.realm, 28 | credentials: t1.credentials, 29 | lifetime: t1.lifetime, 30 | serverAddr: t1.serverAddr, 31 | relayAddress: t1.relayAddress, 32 | } 33 | cfg2 := &turnServerSockConfig{ 34 | user: t2.user, 35 | password: t2.password, 36 | nonce: t2.nonce, 37 | realm: t2.realm, 38 | credentials: t2.credentials, 39 | lifetime: t2.lifetime, 40 | serverAddr: t2.serverAddr, 41 | relayAddress: t2.relayAddress, 42 | } 43 | m1 := new(mockcb) 44 | m2 := new(mockcb) 45 | s1, err = newTurnServerSockWrapper(t1.s.LocalAddr, "s1", m1, cfg1) 46 | if err != nil { 47 | panic(err) 48 | } 49 | s2, err = newTurnServerSockWrapper(t2.s.LocalAddr, "s2", m2, cfg2) 50 | if err != nil { 51 | panic(err) 52 | } 53 | candidates1, err := t1.GetCandidates() 54 | if err != nil { 55 | panic(err) 56 | } 57 | candidates2, err := t2.GetCandidates() 58 | if err != nil { 59 | panic(err) 60 | } 61 | m1.s = s1 62 | m2.s = s2 63 | _, err = s1.createPermission(candidates2) 64 | if err != nil { 65 | panic(err) 66 | } 67 | _, err = s2.createPermission(candidates1) 68 | if err != nil { 69 | panic(err) 70 | } 71 | log.Trace("------------------------------") 72 | return 73 | } 74 | func TestNewTurnServerSockWrapper(t *testing.T) { 75 | s1, s2 := setupTurnServerSock() 76 | req, _ := stun.Build(stun.TransactionIDSetter, stun.BindingRequest, software, stun.Fingerprint) 77 | res, err := s1.sendStunMessageSync(req, s1.cfg.relayAddress, s2.cfg.relayAddress) 78 | if err != nil { 79 | t.Error(err) 80 | return 81 | } 82 | t.Log(res) 83 | } 84 | -------------------------------------------------------------------------------- /ice/turnsock_test.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func newTestTurnSock() (turn *turnSock) { 9 | turn, err := newTurnSock("182.254.155.208:3478", "bai", "bai") 10 | if err != nil { 11 | panic(err) 12 | } 13 | return turn 14 | } 15 | func TestNewTurnSock(t *testing.T) { 16 | turn, err := newTurnSock("182.254.155.208:3478", "bai", "bai") 17 | if err != nil { 18 | t.Error(err) 19 | return 20 | } 21 | cands, err := turn.GetCandidates() 22 | if err != nil { 23 | t.Error(err) 24 | return 25 | } 26 | for i, c := range cands { 27 | t.Log(fmt.Sprintf("cands[%d]=%s", i, c)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ice/util.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | 8 | "github.com/nkbai/log" 9 | ) 10 | 11 | func addrToUDPAddr(addr string) *net.UDPAddr { 12 | host, port, err := net.SplitHostPort(addr) 13 | if err != nil { 14 | log.Error(fmt.Sprintf("SplitHostPort %s err %s", addr, err)) 15 | } 16 | porti, err := strconv.Atoi(port) 17 | if err != nil { 18 | log.Error(fmt.Sprintf("port %s not int ,err %s", port, err)) 19 | } 20 | return &net.UDPAddr{ 21 | IP: net.ParseIP(host), 22 | Port: porti, 23 | } 24 | } 25 | func udpAddrToAddr(udpAddr net.Addr) string { 26 | addr := udpAddr.(*net.UDPAddr) 27 | return fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port) 28 | } 29 | -------------------------------------------------------------------------------- /sdp/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.10.x 5 | 6 | before_install: 7 | - go get github.com/mattn/goveralls 8 | - go get golang.org/x/tools/cmd/cover 9 | 10 | install: 11 | - go get -v -t . 12 | 13 | script: 14 | - $HOME/gopath/bin/goveralls -service=travis-ci -v 15 | -------------------------------------------------------------------------------- /sdp/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/pkg/errors" 6 | packages = ["."] 7 | revision = "645ef00459ed84a119197bfb8d8205042c6df63d" 8 | version = "v0.8.0" 9 | 10 | [solve-meta] 11 | analyzer-name = "dep" 12 | analyzer-version = 1 13 | inputs-digest = "be52a27ec302b8a6c01d9cceda92c94ebffda1207e5c8ec8bee6579b794eaa8e" 14 | solver-name = "gps-cdcl" 15 | solver-version = 1 16 | -------------------------------------------------------------------------------- /sdp/Gopkg.toml: -------------------------------------------------------------------------------- 1 | 2 | # Gopkg.toml example 3 | # 4 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 5 | # for detailed Gopkg.toml documentation. 6 | # 7 | # required = ["github.com/user/thing/cmd/thing"] 8 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 9 | # 10 | # [[constraint]] 11 | # name = "github.com/user/project" 12 | # version = "1.0.0" 13 | # 14 | # [[constraint]] 15 | # name = "github.com/user/project2" 16 | # branch = "dev" 17 | # source = "github.com/myfork/project2" 18 | # 19 | # [[override]] 20 | # name = "github.com/x/y" 21 | # version = "2.4.0" 22 | 23 | 24 | [[constraint]] 25 | name = "github.com/pkg/errors" 26 | version = "0.8.0" 27 | -------------------------------------------------------------------------------- /sdp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Aleksandr Razumov, Cydev. All Rigths Reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Cydev nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /sdp/Makefile: -------------------------------------------------------------------------------- 1 | PROCS := $(shell nproc) 2 | lint: 3 | @echo "linting on $(PROCS) cores" 4 | @gometalinter -e "\.String\(\).+gocyclo" \ 5 | -e "_test.go.+(gocyclo|errcheck|dupl)" \ 6 | --enable="lll" --line-length=80 \ 7 | --enable="gofmt" \ 8 | --disable=gocyclo \ 9 | --deadline=300s \ 10 | --dupl-threshold=70 \ 11 | -j $(PROCS) 12 | @echo "ok" 13 | install: 14 | go get -u sourcegraph.com/sqs/goreturns 15 | go get -u github.com/alecthomas/gometalinter 16 | gometalinter --install --update 17 | go get -u github.com/cydev/go-fuzz/go-fuzz-build 18 | go get -u github.com/dvyukov/go-fuzz/go-fuzz 19 | format: 20 | goimports -w . 21 | profile: 22 | go tool pprof -alloc_space sdp.test mem.out 23 | profile-cpu: 24 | go tool pprof sdp.test cpu.out 25 | -------------------------------------------------------------------------------- /sdp/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: build-{build}.{branch} 2 | 3 | clone_folder: C:\gopath\src\github.com\gortc\sdp 4 | shallow_clone: true # for startup speed 5 | 6 | environment: 7 | GOPATH: C:\gopath 8 | 9 | platform: 10 | - x64 11 | 12 | # http://www.appveyor.com/docs/installed-software 13 | install: 14 | # some helpful output for debugging builds 15 | - go version 16 | - go env 17 | # pre-installed MinGW at C:\MinGW is 32bit only 18 | # but MSYS2 at C:\msys64 has mingw64 19 | - set PATH=C:\msys64\mingw64\bin;%PATH% 20 | - gcc --version 21 | - g++ --version 22 | 23 | build_script: 24 | - go install -v ./... 25 | 26 | test_script: 27 | - set PATH=C:\gopath\bin;%PATH% 28 | - go test -v ./... 29 | 30 | #artifacts: 31 | # - path: '%GOPATH%\bin\*.exe' 32 | deploy: off -------------------------------------------------------------------------------- /sdp/bench: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go test -bench $1 -o sdp.test -memprofile=mem.out -cpuprofile cpu.out 3 | -------------------------------------------------------------------------------- /sdp/encoder.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | func (s Session) appendAttributes(attrs Attributes) Session { 4 | for k, v := range attrs { 5 | for _, a := range v { 6 | if len(a) == 0 { 7 | s = s.AddFlag(k) 8 | } else { 9 | s = s.AddAttribute(k, a) 10 | } 11 | } 12 | } 13 | return s 14 | } 15 | 16 | // Append encodes message to Session and returns result. 17 | func (m *Message) Append(s Session) Session { 18 | // see https://tools.ietf.org/html/rfc4566#section-5 19 | s = s.AddVersion(m.Version) 20 | s = s.AddOrigin(m.Origin) 21 | s = s.AddSessionName(m.Name) 22 | if len(m.Info) > 0 { 23 | s = s.AddSessionInfo(m.Info) 24 | } 25 | if len(m.URI) > 0 { 26 | s = s.AddURI(m.URI) 27 | } 28 | if len(m.Email) > 0 { 29 | s = s.AddEmail(m.Email) 30 | } 31 | if len(m.Phone) > 0 { 32 | s = s.AddPhone(m.Phone) 33 | } 34 | if !m.Connection.Blank() { 35 | s = s.AddConnectionData(m.Connection) 36 | } 37 | for t, v := range m.Bandwidths { 38 | s = s.AddBandwidth(t, v) 39 | } 40 | // One or more time descriptions ("t=" and "r=" lines) 41 | for _, t := range m.Timing { 42 | s = s.AddTiming(t.Start, t.End) 43 | if len(t.Offsets) > 0 { 44 | s = s.AddRepeatTimesCompact(t.Repeat, t.Active, t.Offsets...) 45 | } 46 | } 47 | if len(m.TZAdjustments) > 0 { 48 | s = s.AddTimeZones(m.TZAdjustments...) 49 | } 50 | if !m.Encryption.Blank() { 51 | s = s.AddEncryption(m.Encryption) 52 | } 53 | s = s.appendAttributes(m.Attributes) 54 | 55 | for _, mm := range m.Medias { 56 | s = s.AddMediaDescription(mm.Description) 57 | if len(mm.Title) > 0 { 58 | s = s.AddSessionInfo(mm.Title) 59 | } 60 | if !mm.Connection.Blank() { 61 | s = s.AddConnectionData(mm.Connection) 62 | } 63 | for t, v := range mm.Bandwidths { 64 | s = s.AddBandwidth(t, v) 65 | } 66 | if !mm.Encryption.Blank() { 67 | s = s.AddEncryption(mm.Encryption) 68 | } 69 | s = s.appendAttributes(mm.Attributes) 70 | } 71 | return s 72 | } 73 | -------------------------------------------------------------------------------- /sdp/encoder_test.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestMessage_Append(t *testing.T) { 11 | audio := Media{ 12 | Description: MediaDescription{ 13 | Type: "audio", 14 | Port: 49170, 15 | Format: "0", 16 | Protocol: "RTP/AVP", 17 | }, 18 | } 19 | video := Media{ 20 | Description: MediaDescription{ 21 | Type: "video", 22 | Port: 51372, 23 | Format: "99", 24 | Protocol: "RTP/AVP", 25 | }, 26 | Bandwidths: Bandwidths{ 27 | BandwidthApplicationSpecific: 66781, 28 | }, 29 | Encryption: Encryption{ 30 | Method: "prompt", 31 | }, 32 | } 33 | video.AddAttribute("rtpmap", "99", "h263-1998/90000") 34 | 35 | m := &Message{ 36 | Origin: Origin{ 37 | Username: "jdoe", 38 | SessionID: 2890844526, 39 | SessionVersion: 2890842807, 40 | Address: "10.47.16.5", 41 | }, 42 | Name: "SDP Seminar", 43 | Info: "A Seminar on the session description protocol", 44 | URI: "http://www.example.com/seminars/sdp.pdf", 45 | Email: "j.doe@example.com (Jane Doe)", 46 | Phone: "12345", 47 | Connection: ConnectionData{ 48 | IP: net.ParseIP("224.2.17.12"), 49 | TTL: 127, 50 | }, 51 | Bandwidths: Bandwidths{ 52 | BandwidthConferenceTotal: 154798, 53 | }, 54 | Timing: []Timing{ 55 | { 56 | Start: NTPToTime(2873397496), 57 | End: NTPToTime(2873404696), 58 | Repeat: 7 * time.Hour * 24, 59 | Active: 3600 * time.Second, 60 | Offsets: []time.Duration{ 61 | 0, 62 | 25 * time.Hour, 63 | }, 64 | }, 65 | }, 66 | Encryption: Encryption{ 67 | Method: "clear", 68 | Key: "ab8c4df8b8f4as8v8iuy8re", 69 | }, 70 | Medias: []Media{audio, video}, 71 | } 72 | m.AddFlag("recvonly") 73 | result := `v=0 74 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 75 | s=SDP Seminar 76 | i=A Seminar on the session description protocol 77 | u=http://www.example.com/seminars/sdp.pdf 78 | e=j.doe@example.com (Jane Doe) 79 | p=12345 80 | c=IN IP4 224.2.17.12/127 81 | b=CT:154798 82 | t=2873397496 2873404696 83 | r=7d 1h 0 25h 84 | k=clear:ab8c4df8b8f4as8v8iuy8re 85 | a=recvonly 86 | m=audio 49170 RTP/AVP 0 87 | m=video 51372 RTP/AVP 99 88 | b=AS:66781 89 | k=prompt 90 | a=rtpmap:99 h263-1998/90000` 91 | s := make(Session, 0, 100) 92 | s = m.Append(s) 93 | buf := make([]byte, 0, 1024) 94 | buf = s.AppendTo(buf) 95 | if result != string(buf) { 96 | for k, v := range m.Attributes { 97 | fmt.Println(k, v) 98 | } 99 | for i, v := range s { 100 | fmt.Println(i, v) 101 | } 102 | t.Error(string(buf)) 103 | } 104 | } 105 | 106 | func BenchmarkEncode(b *testing.B) { 107 | b.ReportAllocs() 108 | audio := Media{ 109 | Description: MediaDescription{ 110 | Type: "audio", 111 | Port: 49170, 112 | Format: "0", 113 | Protocol: "RTP/AVP", 114 | }, 115 | } 116 | video := Media{ 117 | Description: MediaDescription{ 118 | Type: "video", 119 | Port: 51372, 120 | Format: "99", 121 | Protocol: "RTP/AVP", 122 | }, 123 | Bandwidths: Bandwidths{ 124 | BandwidthApplicationSpecific: 66781, 125 | }, 126 | Encryption: Encryption{ 127 | Method: "prompt", 128 | }, 129 | } 130 | video.AddAttribute("rtpmap", "99", "h263-1998/90000") 131 | 132 | m := &Message{ 133 | Origin: Origin{ 134 | Username: "jdoe", 135 | SessionID: 2890844526, 136 | SessionVersion: 2890842807, 137 | Address: "10.47.16.5", 138 | }, 139 | Name: "SDP Seminar", 140 | Info: "A Seminar on the session description protocol", 141 | URI: "http://www.example.com/seminars/sdp.pdf", 142 | Email: "j.doe@example.com (Jane Doe)", 143 | Phone: "12345", 144 | Connection: ConnectionData{ 145 | IP: net.ParseIP("224.2.17.12"), 146 | TTL: 127, 147 | }, 148 | Bandwidths: Bandwidths{ 149 | BandwidthConferenceTotal: 154798, 150 | }, 151 | Timing: []Timing{ 152 | { 153 | Start: NTPToTime(2873397496), 154 | End: NTPToTime(2873404696), 155 | Repeat: 7 * time.Hour * 24, 156 | Active: 3600 * time.Second, 157 | Offsets: []time.Duration{ 158 | 0, 159 | 25 * time.Hour, 160 | }, 161 | }, 162 | }, 163 | Encryption: Encryption{ 164 | Method: "clear", 165 | Key: "ab8c4df8b8f4as8v8iuy8re", 166 | }, 167 | Medias: []Media{audio, video}, 168 | } 169 | m.AddFlag("recvonly") 170 | s := make(Session, 0, 100) 171 | buf := make([]byte, 0, 1024) 172 | for i := 0; i < b.N; i++ { 173 | s = m.Append(s) 174 | buf = s.AppendTo(buf) 175 | s = s.reset() 176 | buf = buf[:0] 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /sdp/examples/sdp-decode/example.sdp: -------------------------------------------------------------------------------- 1 | v=0 2 | o=- 3414953978 3414953978 IN IP4 localhost 3 | s=ice 4 | t=0 0 5 | a=ice-ufrag:7e87390c 6 | a=ice-pwd:0f3e0099 7 | m=audio 53866 RTP/AVP 0 8 | c=IN IP4 182.254.155.208 9 | a=candidate:Sc0a82b28 1 UDP 1694498815 14.108.27.205 55744 typ srflx 10 | a=candidate:Hc0a82b28 1 UDP 2130706431 192.168.43.40 64199 typ host 11 | a=candidate:Rb6fe9bd0 1 UDP 16777215 182.254.155.208 53866 typ relay -------------------------------------------------------------------------------- /sdp/examples/sdp-decode/sdp-decode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | 10 | "github.com/nkbai/goice/sdp" 11 | ) 12 | 13 | func main() { 14 | name := "example.sdp" 15 | if len(os.Args) > 1 { 16 | name = os.Args[1] 17 | } 18 | var ( 19 | s sdp.Session 20 | b []byte 21 | err error 22 | f io.ReadCloser 23 | ) 24 | fmt.Println("sdp file:", name) 25 | if f, err = os.Open(name); err != nil { 26 | log.Fatal("err:", err) 27 | } 28 | defer f.Close() 29 | if b, err = ioutil.ReadAll(f); err != nil { 30 | log.Fatal("err:", err) 31 | } 32 | if s, err = sdp.DecodeSession(b, s); err != nil { 33 | log.Fatal("err:", err) 34 | } 35 | for k, v := range s { 36 | fmt.Println(k, v) 37 | } 38 | d := sdp.NewDecoder(s) 39 | m := new(sdp.Message) 40 | if err = d.Decode(m); err != nil { 41 | log.Fatal("err:", err) 42 | } 43 | fmt.Println("Decoded session", m.Name) 44 | fmt.Println("Info:", m.Info) 45 | fmt.Println("Origin:", m.Origin) 46 | } 47 | -------------------------------------------------------------------------------- /sdp/examples/sdp-encode-low/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nkbai/goice/sdp" 7 | ) 8 | 9 | func main() { 10 | var ( 11 | s sdp.Session 12 | b []byte 13 | ) 14 | b = s.AddVersion(0). 15 | AddMediaDescription(sdp.MediaDescription{ 16 | Type: "video", 17 | Port: 51372, 18 | Format: "99", 19 | Protocol: "RTP/AVP", 20 | }). 21 | AddAttribute("rtpmap", "99", "h263-1998/90000"). 22 | AddLine(sdp.TypeEmail, "test@test.com"). 23 | AddRaw('ф', "ОПАСНО"). 24 | AppendTo(b) 25 | // and so on 26 | fmt.Println(string(b)) 27 | // Output: 28 | // v=0 29 | // m=video 51372 RTP/AVP 99 30 | // a=rtpmap:99 h263-1998/90000 31 | // e=test@test.com 32 | // ф=ОПАСНО 33 | } 34 | -------------------------------------------------------------------------------- /sdp/examples/sdp-encode/sdp-encode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | 8 | "github.com/nkbai/goice/sdp" 9 | ) 10 | 11 | func main() { 12 | var ( 13 | s sdp.Session 14 | b []byte 15 | ) 16 | // defining medias 17 | audio := sdp.Media{ 18 | Description: sdp.MediaDescription{ 19 | Type: "audio", 20 | Port: 49170, 21 | Format: "0", 22 | Protocol: "RTP/AVP", 23 | }, 24 | } 25 | video := sdp.Media{ 26 | Description: sdp.MediaDescription{ 27 | Type: "video", 28 | Port: 51372, 29 | Format: "99", 30 | Protocol: "RTP/AVP", 31 | }, 32 | Bandwidths: sdp.Bandwidths{ 33 | sdp.BandwidthApplicationSpecific: 66781, 34 | }, 35 | Encryption: sdp.Encryption{ 36 | Method: "prompt", 37 | }, 38 | } 39 | video.AddAttribute("rtpmap", "99", "h263-1998/90000") 40 | 41 | // defining message 42 | m := &sdp.Message{ 43 | Origin: sdp.Origin{ 44 | Username: "jdoe", 45 | SessionID: 2890844526, 46 | SessionVersion: 2890842807, 47 | Address: "10.47.16.5", 48 | }, 49 | Name: "SDP Seminar", 50 | Info: "A Seminar on the session description protocol", 51 | URI: "http://www.example.com/seminars/sdp.pdf", 52 | Email: "j.doe@example.com (Jane Doe)", 53 | Phone: "12345", 54 | Connection: sdp.ConnectionData{ 55 | IP: net.ParseIP("224.2.17.12"), 56 | TTL: 127, 57 | }, 58 | Bandwidths: sdp.Bandwidths{ 59 | sdp.BandwidthConferenceTotal: 154798, 60 | }, 61 | Timing: []sdp.Timing{ 62 | { 63 | Start: sdp.NTPToTime(2873397496), 64 | End: sdp.NTPToTime(2873404696), 65 | Repeat: 7 * time.Hour * 24, 66 | Active: 3600 * time.Second, 67 | Offsets: []time.Duration{ 68 | 0, 69 | 25 * time.Hour, 70 | }, 71 | }, 72 | }, 73 | Encryption: sdp.Encryption{ 74 | Method: "clear", 75 | Key: "ab8c4df8b8f4as8v8iuy8re", 76 | }, 77 | Medias: []sdp.Media{audio, video}, 78 | } 79 | m.AddFlag("recvonly") 80 | 81 | // appending message to session 82 | s = m.Append(s) 83 | 84 | // appending session to byte buffer 85 | b = s.AppendTo(b) 86 | fmt.Println(string(b)) 87 | } 88 | -------------------------------------------------------------------------------- /sdp/testdata/sdp_session_ex1.txt: -------------------------------------------------------------------------------- 1 | v=0 2 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 3 | s=SDP Seminar 4 | i=A Seminar on the session description protocol 5 | u=http://www.example.com/seminars/sdp.pdf 6 | e=j.doe@example.com (Jane Doe) 7 | c=IN IP4 224.2.17.12/127 8 | t=2873397496 2873404696 9 | a=recvonly 10 | m=audio 49170 RTP/AVP 0 11 | m=video 51372 RTP/AVP 99 12 | a=rtpmap:99 h263-1998/90000 -------------------------------------------------------------------------------- /sdp/testdata/sdp_session_ex_err1.txt: -------------------------------------------------------------------------------- 1 | v=0 2 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 3 | s=SDP Seminar 4 | i=A Seminar on the session description protocol 5 | u=http://www.example.com/seminars/sdp.pdf 6 | e=j.doe@example.com (Jane Doe) 7 | p=12345 8 | c=IN IP4 224.2.17.12/127 9 | b=CT:154798 10 | z=2882844526 -1h 2898848070 0 11 | t=2873397496 2873404696 12 | b=CT:154798 13 | r=7d 3600 0 25h 14 | k=clear:ab8c4df8b8f4as8v8iuy8re 15 | a=recvonly 16 | m=audio 49170 RTP/AVP 0 17 | m=video 51372 RTP/AVP 99 18 | b=AS:66781 19 | k=prompt 20 | a=rtpmap:99 h263-1998/90000 -------------------------------------------------------------------------------- /sdp/testdata/sdp_session_ex_err2.txt: -------------------------------------------------------------------------------- 1 | v=0 2 | o=jdoe 2890844526 noop noop 2890842807 IN IP4 10.47.16.5 3 | s=SDP Seminar 4 | i=A Seminar on the session description protocol 5 | u=http://www.example.com/seminars/sdp.pdf 6 | e=j.doe@example.com (Jane Doe) 7 | p=12345 8 | c=IN IP4 224.2.17.12/127 9 | b=CT:154798 10 | z=2882844526 -1h 2898848070 0 11 | t=2873397496 2873404696 12 | r=7d 3600 0 25h 13 | k=clear:ab8c4df8b8f4as8v8iuy8re 14 | a=recvonly 15 | m=audio 49170 RTP/AVP 0 16 | m=video 51372 RTP/AVP 99 17 | b=AS:66781 18 | k=prompt 19 | a=rtpmap:99 h263-1998/90000 -------------------------------------------------------------------------------- /sdp/testdata/sdp_session_ex_err3.txt: -------------------------------------------------------------------------------- 1 | sdp_session_ex_err1.txtv=0 2 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 3 | s=SDP Seminar 4 | i=A Seminar on the session description protocol 5 | u=http://www.example.com/seminars/sdp.pdf 6 | e=j.doe@example.com (Jane Doe) 7 | p=12345 8 | c=IN IP4 224.2.17.12/127 9 | b=CT:154798 10 | z=288KEK2844526 -1h 2898848070 0 11 | t=2873397496 2873404696 12 | b=CT:154798 13 | r=7d 3600 0 25h 14 | k=clear:ab8c4df8b8f4as8v8iuy8re 15 | a=recvonly 16 | m=audio 49170 RTP/AVP 0 17 | m=video 51372 RTP/AVP 99 18 | b=AS:66781 19 | k=prompt 20 | a=rtpmap:99 h263-1998/90000 -------------------------------------------------------------------------------- /sdp/testdata/sdp_session_ex_err4.txt: -------------------------------------------------------------------------------- 1 | v=0 2 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 3 | s=SDP Seminar 4 | i=A Seminar on the session description protocol 5 | u=http://www.example.com/seminars/sdp.pdf 6 | e=j.doe@example.com (Jane Doe) 7 | p=12345 8 | c=IN IP4 224.2.17.12/127 9 | b=CT:154798 10 | z=2882844526 -1h 2898848070 0 11 | t=2873397496 2873404696 12 | b=CT:154798 13 | r=7d 360KEK0 0 25h 14 | k=clear:ab8c4df8b8f4as8v8iuy8re 15 | a=recvonly 16 | m=audio 49170 RTP/AVP 0 17 | m=video 51372 RTP/AVP 99 18 | b=AS:66781 19 | k=prompt 20 | a=rtpmap:99 h263-1998/90000 -------------------------------------------------------------------------------- /sdp/testdata/sdp_session_ex_err5.txt: -------------------------------------------------------------------------------- 1 | v=0 2 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 3 | s=SDP Seminar 4 | i=A Seminar on the session description protocol 5 | u=http://www.example.com/seminars/sdp.pdf 6 | e=j.doe@example.com (Jane Doe) 7 | p=12345 8 | c=IN IP4 9 | b=CT:154798 10 | z=2882844526 -1h 2898848070 0 11 | t=2873397496 2873404696 12 | r=7d 3600 0 25h 13 | k=clear:ab8c4df8b8f4as8v8iuy8re 14 | a=recvonly 15 | m=audio 49170 RTP/AVP 0 16 | m=video 51372 RTP/AVP 99 17 | b=AS:66781 18 | k=prompt 19 | a=rtpmap:99 h263-1998/90000 -------------------------------------------------------------------------------- /sdp/testdata/sdp_session_ex_full.txt: -------------------------------------------------------------------------------- 1 | v=0 2 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 3 | s=SDP Seminar 4 | i=A Seminar on the session description protocol 5 | u=http://www.example.com/seminars/sdp.pdf 6 | e=j.doe@example.com (Jane Doe) 7 | p=12345 8 | c=IN IP4 224.2.17.12/127 9 | b=CT:154798 10 | z=2882844526 -1h 2898848070 0 11 | t=2873397496 2873404696 12 | r=7d 3600 0 25h 13 | k=clear:ab8c4df8b8f4as8v8iuy8re 14 | a=recvonly 15 | m=audio 49170 RTP/AVP 0 16 | m=video 51372 RTP/AVP 99 17 | b=AS:66781 18 | k=prompt 19 | a=rtpmap:99 h263-1998/90000 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex2.txt: -------------------------------------------------------------------------------- 1 | v=1337 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex3.txt: -------------------------------------------------------------------------------- 1 | p=+1 617 555-6011 2 | e=j.doe@example.com (Jane Doe) -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_attributes.txt: -------------------------------------------------------------------------------- 1 | a=recvonly 2 | a=anotherflag 3 | a=orient:landscape 4 | a=rtpmap:96 L8/8000 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_bandwidth.txt: -------------------------------------------------------------------------------- 1 | b=CT:154798 2 | b=AS:66781 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_info.txt: -------------------------------------------------------------------------------- 1 | i=Info goes here -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_ip.txt: -------------------------------------------------------------------------------- 1 | c=IN IP6 FF15::103 2 | c=IN IP4 224.2.36.42/127 3 | c=IN IP4 214.6.36.42/95/4 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_keys.txt: -------------------------------------------------------------------------------- 1 | k=clear:ab8c4df8b8f4as8v8iuy8re 2 | k=prompt -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_media.txt: -------------------------------------------------------------------------------- 1 | m=video 49170/2 RTP/AVP 31 2 | m=audio 49170 RTP/AVP 555 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_name.txt: -------------------------------------------------------------------------------- 1 | s=CyConf -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_origin.txt: -------------------------------------------------------------------------------- 1 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 2 | o=jdoe 2890844527 2890842807 IN IP6 FF15::103 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_repeat.txt: -------------------------------------------------------------------------------- 1 | r=604800 3600 0 90000 2 | r=7d 1h 0 25h 3 | r=604810 1h 0 25h -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_timing.txt: -------------------------------------------------------------------------------- 1 | t=0 0 2 | t=0 3042462419 -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_uri.txt: -------------------------------------------------------------------------------- 1 | u=http://cydev.ru -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_webrtc1.txt: -------------------------------------------------------------------------------- 1 | v=0 2 | o=- 663298250504093404 2 IN IP4 127.0.0.1 3 | s=- 4 | t=0 0 5 | a=group:BUNDLE data 6 | a=msid-semantic: WMS 7 | m=application 9 UDP/TLS/RTP/SAVPF 127 8 | c=IN IP4 0.0.0.0 9 | b=AS:30 10 | a=rtcp:9 IN IP4 0.0.0.0 11 | a=ice-ufrag:eM2ytqY8D5Q07RAn 12 | a=ice-pwd:jv5zOTXkL2+Xp0bYH4EWKbTT 13 | a=fingerprint:sha-256 A2:4E:42:B1:42:BD:69:FB:F4:60:94:0A:AD:FA:26:BA:32:DA:28:33:2A:20:C4:F6:AA:6E:8A:F6:23:65:BC:A6 14 | a=setup:actpass 15 | a=mid:data 16 | a=sendrecv 17 | a=rtcp-mux 18 | a=rtpmap:127 google-data/90000 19 | a=ssrc:3129309024 cname:IBzJsTdWzLObARFm 20 | a=ssrc:3129309024 msid:kekikus kekikus 21 | a=ssrc:3129309024 mslabel:kekikus 22 | a=ssrc:3129309024 label:kekikus 23 | a=candidate:2983135859 1 udp 2113937151 10.1.22.220 56024 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 24 | a=candidate:4294175796 1 udp 2113939711 2001:67c:56c:100::3 36737 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 25 | a=candidate:2983135859 2 udp 2113937150 10.1.22.220 51941 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 26 | a=candidate:4294175796 2 udp 2113939710 2001:67c:56c:100::3 42279 typ host generation 0 ufrag eM2ytqY8D5Q07RAn 27 | a=candidate:842163049 2 udp 1677729534 91.225.236.99 51941 typ srflx raddr 10.1.22.220 rport 51941 generation 0 ufrag eM2ytqY8D5Q07RAn 28 | a=candidate:842163049 1 udp 1677729535 91.225.236.99 56024 typ srflx raddr 10.1.22.220 rport 56024 generation 0 ufrag eM2ytqY8D5Q07RAn -------------------------------------------------------------------------------- /sdp/testdata/spd_session_ex_zones.txt: -------------------------------------------------------------------------------- 1 | z=2882844526 -1h 2898848070 0 2 | z=2898848070 90m 2898848070 0 -------------------------------------------------------------------------------- /stun/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | go: 6 | - master 7 | - 1.9.x 8 | - 1.10.x 9 | 10 | env: 11 | - TEST_EXTERNAL=1 12 | 13 | matrix: 14 | allow_failures: 15 | - go: 1.9.x 16 | - go: master 17 | fast_finish: true 18 | 19 | before_install: 20 | - go get github.com/mattn/goveralls 21 | - go get golang.org/x/tools/cmd/cover 22 | 23 | install: 24 | - go get -v -t . 25 | 26 | script: 27 | - go test -tags gofuzz -run TestFuzz -v . 28 | - $HOME/gopath/bin/goveralls -service=travis-ci -v -package . 29 | -------------------------------------------------------------------------------- /stun/AUTHORS: -------------------------------------------------------------------------------- 1 | Aleksandr Razumov 2 | Aliaksandr Valialkin 3 | Cydev 4 | The gortc project 5 | The IETF Trust 6 | The Go Authors 7 | -------------------------------------------------------------------------------- /stun/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.9.1 2 | 3 | COPY . /go/src/github.com/gortc/stun 4 | 5 | RUN go test github.com/gortc/stun 6 | 7 | -------------------------------------------------------------------------------- /stun/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2017 Aleksandr Razumov, gortc. All Rights Reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /stun/Makefile: -------------------------------------------------------------------------------- 1 | VERSION := $(shell git describe --tags | sed -e 's/^v//g' | awk -F "-" '{print $$1}') 2 | ITERATION := $(shell git describe --tags --long | awk -F "-" '{print $$2}') 3 | GO_VERSION=$(shell gobuild -v) 4 | GO := $(or $(GOROOT),/usr/lib/go)/bin/go 5 | PROCS := $(shell nproc) 6 | cores: 7 | @echo "cores: $(PROCS)" 8 | test: 9 | go test -v 10 | bench: 11 | go test -bench . 12 | bench-record: 13 | $(GO) test -bench . > "benchmarks/stun-go-$(GO_VERSION).txt" 14 | fuzz-prepare-msg: 15 | go-fuzz-build -func FuzzMessage -o stun-msg-fuzz.zip github.com/gortc/stun 16 | fuzz-prepare-typ: 17 | go-fuzz-build -func FuzzType -o stun-typ-fuzz.zip github.com/gortc/stun 18 | fuzz-prepare-setters: 19 | go-fuzz-build -func FuzzSetters -o stun-setters-fuzz.zip github.com/gortc/stun 20 | fuzz-msg: 21 | go-fuzz -bin=./stun-msg-fuzz.zip -workdir=examples/stun-msg 22 | fuzz-typ: 23 | go-fuzz -bin=./stun-typ-fuzz.zip -workdir=examples/stun-typ 24 | fuzz-setters: 25 | go-fuzz -bin=./stun-setters-fuzz.zip -workdir=examples/stun-setters 26 | fuzz-test: 27 | go test -tags gofuzz -run TestFuzz -v . 28 | lint: 29 | @echo "linting on $(PROCS) cores" 30 | @gometalinter \ 31 | -e "_test.go.+(gocyclo|errcheck|dupl)" \ 32 | -e "attributes\.go.+credentials,.+,LOW.+\(gas\)" \ 33 | -e "Message.+\(aligncheck\)" \ 34 | --enable="lll" --line-length=100 \ 35 | --enable="gofmt" \ 36 | --disable="gotype" \ 37 | --enable="goimports" \ 38 | --enable="misspell" \ 39 | --enable="unused" \ 40 | --deadline=300s \ 41 | -j $(PROCS) 42 | @echo "ok" 43 | escape: 44 | @echo "Not escapes, except autogenerated:" 45 | @go build -gcflags '-m -l' 2>&1 \ 46 | | grep -v "" \ 47 | | grep escapes 48 | format: 49 | goimports -w . 50 | bench-compare: 51 | go test -bench . > bench.go-16 52 | go-tip test -bench . > bench.go-tip 53 | @benchcmp bench.go-16 bench.go-tip 54 | install: 55 | go get -u sourcegraph.com/sqs/goreturns 56 | go get -u github.com/alecthomas/gometalinter 57 | gometalinter --install --update 58 | go get -u github.com/dvyukov/go-fuzz/go-fuzz-build 59 | go get github.com/dvyukov/go-fuzz/go-fuzz 60 | docker-build: 61 | docker build -t gortc/stun . 62 | test-integration: 63 | cd integration-test && bash ./test.sh 64 | prepush: test lint test-integration 65 | -------------------------------------------------------------------------------- /stun/addr.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "strconv" 8 | ) 9 | 10 | // MappedAddress represents MAPPED-ADDRESS attribute. 11 | // 12 | // This attribute is used only by servers for achieving backwards 13 | // compatibility with RFC 3489 clients. 14 | // 15 | // https://tools.ietf.org/html/rfc5389#section-15.1 16 | type MappedAddress struct { 17 | IP net.IP 18 | Port int 19 | } 20 | 21 | // AlternateServer represents ALTERNATE-SERVER attribute. 22 | // 23 | // https://tools.ietf.org/html/rfc5389#section-15.11 24 | type AlternateServer struct { 25 | IP net.IP 26 | Port int 27 | } 28 | 29 | // AddTo adds ALTERNATE-SERVER attribute to message. 30 | func (s *AlternateServer) AddTo(m *Message) error { 31 | a := (*MappedAddress)(s) 32 | return a.addAs(m, AttrAlternateServer) 33 | } 34 | 35 | // GetFrom decodes ALTERNATE-SERVER from message. 36 | func (s *AlternateServer) GetFrom(m *Message) error { 37 | a := (*MappedAddress)(s) 38 | return a.GetFromAs(m, AttrAlternateServer) 39 | } 40 | 41 | func (a MappedAddress) String() string { 42 | return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port)) 43 | } 44 | 45 | //GetFromAs decodes attribute t from message 46 | func (a *MappedAddress) GetFromAs(m *Message, t AttrType) error { 47 | v, err := m.Get(t) 48 | if err != nil { 49 | return err 50 | } 51 | if len(v) <= 4 { 52 | return io.ErrUnexpectedEOF 53 | } 54 | family := bin.Uint16(v[0:2]) 55 | if family != familyIPv6 && family != familyIPv4 { 56 | return newDecodeErr("xor-mapped address", "family", 57 | fmt.Sprintf("bad value %d", family), 58 | ) 59 | } 60 | ipLen := net.IPv4len 61 | if family == familyIPv6 { 62 | ipLen = net.IPv6len 63 | } 64 | // Ensuring len(a.IP) == ipLen and reusing a.IP. 65 | if len(a.IP) < ipLen { 66 | a.IP = a.IP[:cap(a.IP)] 67 | for len(a.IP) < ipLen { 68 | a.IP = append(a.IP, 0) 69 | } 70 | } 71 | a.IP = a.IP[:ipLen] 72 | for i := range a.IP { 73 | a.IP[i] = 0 74 | } 75 | a.Port = int(bin.Uint16(v[2:4])) 76 | copy(a.IP, v[4:]) 77 | return nil 78 | } 79 | 80 | func (a *MappedAddress) addAs(m *Message, t AttrType) error { 81 | var ( 82 | family = familyIPv4 83 | ip = a.IP 84 | ) 85 | if len(a.IP) == net.IPv6len { 86 | if isIPv4(ip) { 87 | ip = ip[12:16] // like in ip.To4() 88 | } else { 89 | family = familyIPv6 90 | } 91 | } else if len(ip) != net.IPv4len { 92 | return ErrBadIPLength 93 | } 94 | value := make([]byte, 128) 95 | value[0] = 0 // first 8 bits are zeroes 96 | bin.PutUint16(value[0:2], family) 97 | bin.PutUint16(value[2:4], uint16(a.Port)) 98 | copy(value[4:], ip) 99 | m.Add(t, value[:4+len(ip)]) 100 | return nil 101 | } 102 | 103 | // AddTo adds MAPPED-ADDRESS to message. 104 | func (a *MappedAddress) AddTo(m *Message) error { 105 | return a.addAs(m, AttrMappedAddress) 106 | } 107 | 108 | // GetFrom decodes MAPPED-ADDRESS from message. 109 | func (a *MappedAddress) GetFrom(m *Message) error { 110 | return a.GetFromAs(m, AttrMappedAddress) 111 | } 112 | -------------------------------------------------------------------------------- /stun/addr_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "testing" 7 | ) 8 | 9 | func TestMappedAddress(t *testing.T) { 10 | m := new(Message) 11 | addr := &MappedAddress{ 12 | IP: net.ParseIP("122.12.34.5"), 13 | Port: 5412, 14 | } 15 | if addr.String() != "122.12.34.5:5412" { 16 | t.Error("bad string", addr) 17 | } 18 | t.Run("Bad length", func(t *testing.T) { 19 | badAddr := &MappedAddress{ 20 | IP: net.IP{1, 2, 3}, 21 | } 22 | if err := badAddr.AddTo(m); err == nil { 23 | t.Error("should error") 24 | } 25 | }) 26 | t.Run("AddTo", func(t *testing.T) { 27 | if err := addr.AddTo(m); err != nil { 28 | t.Error(err) 29 | } 30 | t.Run("GetFrom", func(t *testing.T) { 31 | got := new(MappedAddress) 32 | if err := got.GetFrom(m); err != nil { 33 | t.Error(err) 34 | } 35 | if !got.IP.Equal(addr.IP) { 36 | t.Error("got bad IP: ", got.IP) 37 | } 38 | t.Run("Not found", func(t *testing.T) { 39 | message := new(Message) 40 | if err := got.GetFrom(message); err != ErrAttributeNotFound { 41 | t.Error("should be not found: ", err) 42 | } 43 | }) 44 | t.Run("Bad family", func(t *testing.T) { 45 | v, _ := m.Attributes.Get(AttrMappedAddress) 46 | v.Value[0] = 32 47 | if err := got.GetFrom(m); err == nil { 48 | t.Error("should error") 49 | } 50 | }) 51 | t.Run("Bad length", func(t *testing.T) { 52 | message := new(Message) 53 | message.Add(AttrMappedAddress, []byte{1, 2, 3}) 54 | if err := got.GetFrom(message); err != io.ErrUnexpectedEOF { 55 | t.Errorf("<%s> should be <%s>", err, io.ErrUnexpectedEOF) 56 | } 57 | }) 58 | }) 59 | }) 60 | } 61 | 62 | func TestMappedAddressV6(t *testing.T) { 63 | m := new(Message) 64 | addr := &MappedAddress{ 65 | IP: net.ParseIP("::"), 66 | Port: 5412, 67 | } 68 | t.Run("AddTo", func(t *testing.T) { 69 | if err := addr.AddTo(m); err != nil { 70 | t.Error(err) 71 | } 72 | t.Run("GetFrom", func(t *testing.T) { 73 | got := new(MappedAddress) 74 | if err := got.GetFrom(m); err != nil { 75 | t.Error(err) 76 | } 77 | if !got.IP.Equal(addr.IP) { 78 | t.Error("got bad IP: ", got.IP) 79 | } 80 | t.Run("Not found", func(t *testing.T) { 81 | message := new(Message) 82 | if err := got.GetFrom(message); err != ErrAttributeNotFound { 83 | t.Error("should be not found: ", err) 84 | } 85 | }) 86 | }) 87 | }) 88 | } 89 | 90 | func TestAlternateServer(t *testing.T) { 91 | m := new(Message) 92 | addr := &AlternateServer{ 93 | IP: net.ParseIP("122.12.34.5"), 94 | Port: 5412, 95 | } 96 | t.Run("AddTo", func(t *testing.T) { 97 | if err := addr.AddTo(m); err != nil { 98 | t.Error(err) 99 | } 100 | t.Run("GetFrom", func(t *testing.T) { 101 | got := new(AlternateServer) 102 | if err := got.GetFrom(m); err != nil { 103 | t.Error(err) 104 | } 105 | if !got.IP.Equal(addr.IP) { 106 | t.Error("got bad IP: ", got.IP) 107 | } 108 | t.Run("Not found", func(t *testing.T) { 109 | message := new(Message) 110 | if err := got.GetFrom(message); err != ErrAttributeNotFound { 111 | t.Error("should be not found: ", err) 112 | } 113 | }) 114 | }) 115 | }) 116 | } 117 | 118 | func BenchmarkMappedAddress_AddTo(b *testing.B) { 119 | m := new(Message) 120 | b.ReportAllocs() 121 | addr := &MappedAddress{ 122 | IP: net.ParseIP("122.12.34.5"), 123 | Port: 5412, 124 | } 125 | for i := 0; i < b.N; i++ { 126 | if err := addr.AddTo(m); err != nil { 127 | b.Fatal(err) 128 | } 129 | m.Reset() 130 | } 131 | } 132 | 133 | func BenchmarkAlternateServer_AddTo(b *testing.B) { 134 | m := new(Message) 135 | b.ReportAllocs() 136 | addr := &AlternateServer{ 137 | IP: net.ParseIP("122.12.34.5"), 138 | Port: 5412, 139 | } 140 | for i := 0; i < b.N; i++ { 141 | if err := addr.AddTo(m); err != nil { 142 | b.Fatal(err) 143 | } 144 | m.Reset() 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /stun/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | platform: x64 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | skip_tags: true 10 | 11 | clone_folder: c:\gopath\src\github.com\gortc\stun 12 | 13 | environment: 14 | GOPATH: c:\gopath 15 | GOVERSION: 1.10 16 | TEST_EXTERNAL: 1 17 | 18 | install: 19 | - go version 20 | - go get -v -t . 21 | 22 | build_script: 23 | - go test -v . 24 | -------------------------------------------------------------------------------- /stun/attributes_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func BenchmarkMessage_GetNotFound(b *testing.B) { 8 | m := New() 9 | b.ReportAllocs() 10 | for i := 0; i < b.N; i++ { 11 | m.Get(AttrRealm) 12 | } 13 | } 14 | 15 | func TestMessage_GetNoAllocs(t *testing.T) { 16 | m := New() 17 | NewSoftware("c").AddTo(m) 18 | m.WriteHeader() 19 | 20 | t.Run("Default", func(t *testing.T) { 21 | allocs := testing.AllocsPerRun(10, func() { 22 | m.Get(AttrSoftware) 23 | }) 24 | if allocs > 0 { 25 | t.Error("allocated memory, but should not") 26 | } 27 | }) 28 | t.Run("Not found", func(t *testing.T) { 29 | allocs := testing.AllocsPerRun(10, func() { 30 | m.Get(AttrOrigin) 31 | }) 32 | if allocs > 0 { 33 | t.Error("allocated memory, but should not") 34 | } 35 | }) 36 | } 37 | 38 | func TestPadding(t *testing.T) { 39 | tt := []struct { 40 | in, out int 41 | }{ 42 | {4, 4}, // 0 43 | {2, 4}, // 1 44 | {5, 8}, // 2 45 | {8, 8}, // 3 46 | {11, 12}, // 4 47 | {1, 4}, // 5 48 | {3, 4}, // 6 49 | {6, 8}, // 7 50 | {7, 8}, // 8 51 | {0, 0}, // 9 52 | {40, 40}, // 10 53 | } 54 | for i, c := range tt { 55 | if got := nearestPaddedValueLength(c.in); got != c.out { 56 | t.Errorf("[%d]: padd(%d) %d (got) != %d (expected)", 57 | i, c.in, got, c.out, 58 | ) 59 | } 60 | } 61 | } 62 | 63 | func TestAttrLengthError_Error(t *testing.T) { 64 | err := AttrOverflowErr{ 65 | Got: 100, 66 | Max: 50, 67 | Type: AttrLifetime, 68 | } 69 | if err.Error() != "incorrect length of LIFETIME attribute: 100 exceeds maximum 50" { 70 | t.Error("bad error string", err) 71 | } 72 | } 73 | 74 | func TestAttrLengthErr_Error(t *testing.T) { 75 | err := AttrLengthErr{ 76 | Attr: AttrErrorCode, 77 | Expected: 15, 78 | Got: 99, 79 | } 80 | if err.Error() != "incorrect length of ERROR-CODE attribute: got 99, expected 15" { 81 | t.Errorf("bad error string: %s", err) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /stun/cmd/stun-client/stun-client: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/cmd/stun-client/stun-client -------------------------------------------------------------------------------- /stun/cmd/stun-client/stun-client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "time" 8 | 9 | "github.com/nkbai/goice/stun" 10 | "github.com/nkbai/goice/utils" 11 | "github.com/nkbai/log" 12 | ) 13 | 14 | func main() { 15 | log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, utils.MyStreamHandler(os.Stderr))) 16 | flag.Usage = func() { 17 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 18 | fmt.Fprintln(os.Stderr, os.Args[0], "stun.l.google.com:19302") 19 | } 20 | flag.Parse() 21 | addr := flag.Arg(0) 22 | if len(addr) == 0 { 23 | //addr = "stun.l.google.com:19302" 24 | addr = "193.112.248.133:3478" 25 | } 26 | c, err := stun.Dial("udp", addr) 27 | if err != nil { 28 | log.Crit(fmt.Sprintf("dial: %s", err)) 29 | } 30 | deadline := time.Now().Add(time.Second * 25) 31 | if err := c.Do(stun.MustBuild(stun.TransactionIDSetter, stun.BindingRequest), deadline, func(res stun.Event) { 32 | if res.Error != nil { 33 | log.Crit(fmt.Sprintf("res %s", res)) 34 | } 35 | var xorAddr stun.XORMappedAddress 36 | if err := xorAddr.GetFrom(res.Message); err != nil { 37 | var addr stun.MappedAddress 38 | err = addr.GetFrom(res.Message) 39 | if err != nil { 40 | log.Crit(err.Error()) 41 | } 42 | log.Info(fmt.Sprintf("addr=%s", addr)) 43 | } else { 44 | log.Info(fmt.Sprintf("xoraddr=%s", xorAddr)) 45 | } 46 | }); err != nil { 47 | log.Crit(fmt.Sprintf("do: %s", err)) 48 | } 49 | if err := c.Close(); err != nil { 50 | log.Crit(err.Error()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stun/errorcode.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | // ErrorCodeAttribute represents ERROR-CODE attribute. 10 | // 11 | // https://tools.ietf.org/html/rfc5389#section-15.6 12 | type ErrorCodeAttribute struct { 13 | Code ErrorCode 14 | Reason []byte 15 | } 16 | 17 | func (c ErrorCodeAttribute) String() string { 18 | return fmt.Sprintf("%d: %s", c.Code, c.Reason) 19 | } 20 | 21 | // constants for ERROR-CODE encoding. 22 | const ( 23 | errorCodeReasonStart = 4 24 | errorCodeClassByte = 2 25 | errorCodeNumberByte = 3 26 | errorCodeReasonMaxB = 763 27 | errorCodeModulo = 100 28 | ) 29 | 30 | // AddTo adds ERROR-CODE to m. 31 | func (c ErrorCodeAttribute) AddTo(m *Message) error { 32 | value := make([]byte, 0, errorCodeReasonMaxB) 33 | if len(c.Reason) > errorCodeReasonMaxB { 34 | return &AttrOverflowErr{ 35 | Got: len(c.Reason) + errorCodeReasonStart, 36 | Max: errorCodeReasonMaxB + errorCodeReasonStart, 37 | Type: AttrErrorCode, 38 | } 39 | } 40 | value = value[:errorCodeReasonStart+len(c.Reason)] 41 | number := byte(c.Code % errorCodeModulo) // error code modulo 100 42 | class := byte(c.Code / errorCodeModulo) // hundred digit 43 | value[errorCodeClassByte] = class 44 | value[errorCodeNumberByte] = number 45 | copy(value[errorCodeReasonStart:], c.Reason) 46 | m.Add(AttrErrorCode, value) 47 | return nil 48 | } 49 | 50 | // GetFrom decodes ERROR-CODE from m. Reason is valid until m.Raw is valid. 51 | func (c *ErrorCodeAttribute) GetFrom(m *Message) error { 52 | v, err := m.Get(AttrErrorCode) 53 | if err != nil { 54 | return err 55 | } 56 | if len(v) < errorCodeReasonStart { 57 | return io.ErrUnexpectedEOF 58 | } 59 | var ( 60 | class = uint16(v[errorCodeClassByte]) 61 | number = uint16(v[errorCodeNumberByte]) 62 | code = int(class*errorCodeModulo + number) 63 | ) 64 | c.Code = ErrorCode(code) 65 | c.Reason = v[errorCodeReasonStart:] 66 | return nil 67 | } 68 | 69 | //GetFromAs decodes attribute t from message 70 | func (c *ErrorCodeAttribute) GetFromAs(m *Message, t AttrType) error { 71 | return c.GetFrom(m) 72 | } 73 | 74 | // ErrorCode is code for ERROR-CODE attribute. 75 | type ErrorCode int 76 | 77 | // ErrNoDefaultReason means that default reason for provided error code 78 | // is not defined in RFC. 79 | var ErrNoDefaultReason = errors.New("no default reason for ErrorCode") 80 | 81 | // AddTo adds ERROR-CODE with default reason to m. If there 82 | // is no default reason, returns ErrNoDefaultReason. 83 | func (c ErrorCode) AddTo(m *Message) error { 84 | reason := errorReasons[c] 85 | if reason == nil { 86 | return ErrNoDefaultReason 87 | } 88 | a := &ErrorCodeAttribute{ 89 | Code: c, 90 | Reason: reason, 91 | } 92 | return a.AddTo(m) 93 | } 94 | 95 | // Possible error codes. 96 | const ( 97 | CodeTryAlternate ErrorCode = 300 98 | CodeBadRequest ErrorCode = 400 99 | CodeUnauthorised ErrorCode = 401 100 | CodeUnknownAttribute ErrorCode = 420 101 | CodeStaleNonce ErrorCode = 428 102 | CodeRoleConflict ErrorCode = 478 103 | CodeServerError ErrorCode = 500 104 | ) 105 | 106 | // Error codes from RFC 5766. 107 | // 108 | // https://trac.tools.ietf.org/html/rfc5766#section-15 109 | const ( 110 | CodeForbidden ErrorCode = 403 // Forbidden 111 | CodeAllocMismatch ErrorCode = 437 // Allocation Mismatch 112 | CodeWrongCredentials ErrorCode = 441 // Wrong Credentials 113 | CodeUnsupportedTransProto ErrorCode = 442 // Unsupported Transport Protocol 114 | CodeAllocQuotaReached ErrorCode = 486 // Allocation Quota Reached 115 | CodeInsufficientCapacity ErrorCode = 508 // Insufficient Capacity 116 | ) 117 | 118 | var errorReasons = map[ErrorCode][]byte{ 119 | CodeTryAlternate: []byte("Try Alternate"), 120 | CodeBadRequest: []byte("Bad Request"), 121 | CodeUnauthorised: []byte("Unauthorised"), 122 | CodeUnknownAttribute: []byte("Unknown Attribute"), 123 | CodeStaleNonce: []byte("Stale nonce"), 124 | CodeServerError: []byte("Server Error"), 125 | CodeRoleConflict: []byte("Role Conflict"), 126 | 127 | // RFC 5766. 128 | CodeForbidden: []byte("Forbidden"), 129 | CodeAllocMismatch: []byte("Allocation Mismatch"), 130 | CodeWrongCredentials: []byte("Wrong Credentials"), 131 | CodeUnsupportedTransProto: []byte("Unsupported Transport Protocol"), 132 | CodeAllocQuotaReached: []byte("Allocation Quota Reached"), 133 | CodeInsufficientCapacity: []byte("Insufficient Capacity"), 134 | } 135 | -------------------------------------------------------------------------------- /stun/errorcode_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "encoding/base64" 5 | "io" 6 | "testing" 7 | ) 8 | 9 | func BenchmarkErrorCode_AddTo(b *testing.B) { 10 | m := New() 11 | b.ReportAllocs() 12 | for i := 0; i < b.N; i++ { 13 | CodeStaleNonce.AddTo(m) 14 | m.Reset() 15 | } 16 | } 17 | 18 | func BenchmarkErrorCodeAttribute_AddTo(b *testing.B) { 19 | m := New() 20 | b.ReportAllocs() 21 | a := &ErrorCodeAttribute{ 22 | Code: 404, 23 | Reason: []byte("not found!"), 24 | } 25 | for i := 0; i < b.N; i++ { 26 | a.AddTo(m) 27 | m.Reset() 28 | } 29 | } 30 | 31 | func BenchmarkErrorCodeAttribute_GetFrom(b *testing.B) { 32 | m := New() 33 | b.ReportAllocs() 34 | a := &ErrorCodeAttribute{ 35 | Code: 404, 36 | Reason: []byte("not found!"), 37 | } 38 | a.AddTo(m) 39 | for i := 0; i < b.N; i++ { 40 | a.GetFrom(m) 41 | } 42 | } 43 | 44 | func TestErrorCodeAttribute_GetFrom(t *testing.T) { 45 | m := New() 46 | m.Add(AttrErrorCode, []byte{1}) 47 | c := new(ErrorCodeAttribute) 48 | if err := c.GetFrom(m); err != io.ErrUnexpectedEOF { 49 | t.Errorf("GetFrom should return <%s>, but got <%s>", 50 | io.ErrUnexpectedEOF, err, 51 | ) 52 | } 53 | } 54 | 55 | func TestMessage_AddErrorCode(t *testing.T) { 56 | m := New() 57 | transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er") 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | copy(m.TransactionID[:], transactionID) 62 | expectedCode := ErrorCode(428) 63 | expectedReason := "Stale nonce" 64 | CodeStaleNonce.AddTo(m) 65 | m.WriteHeader() 66 | 67 | mRes := New() 68 | if _, err = mRes.ReadFrom(m.reader()); err != nil { 69 | t.Fatal(err) 70 | } 71 | errCodeAttr := new(ErrorCodeAttribute) 72 | if err = errCodeAttr.GetFrom(mRes); err != nil { 73 | t.Error(err) 74 | } 75 | code := errCodeAttr.Code 76 | if err != nil { 77 | t.Error(err) 78 | } 79 | if code != expectedCode { 80 | t.Error("bad code", code) 81 | } 82 | if string(errCodeAttr.Reason) != expectedReason { 83 | t.Error("bad reason", string(errCodeAttr.Reason)) 84 | } 85 | } 86 | 87 | func TestErrorCode(t *testing.T) { 88 | a := &ErrorCodeAttribute{ 89 | Code: 404, 90 | Reason: []byte("not found!"), 91 | } 92 | if a.String() != "404: not found!" { 93 | t.Error("bad string", a) 94 | } 95 | m := New() 96 | cod := ErrorCode(666) 97 | if err := cod.AddTo(m); err != ErrNoDefaultReason { 98 | t.Error("should be ErrNoDefaultReason", err) 99 | } 100 | if err := a.GetFrom(m); err == nil { 101 | t.Error("attr should not be in message") 102 | } 103 | a.Reason = make([]byte, 2048) 104 | if err := a.AddTo(m); err == nil { 105 | t.Error("should error") 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /stun/errors.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | // DecodeErr records an error and place when it is occurred. 4 | type DecodeErr struct { 5 | Place DecodeErrPlace 6 | Message string 7 | } 8 | 9 | // IsInvalidCookie returns true if error means that magic cookie 10 | // value is invalid. 11 | func (e DecodeErr) IsInvalidCookie() bool { 12 | return e.Place == DecodeErrPlace{"message", "cookie"} 13 | } 14 | 15 | // IsPlaceParent reports if error place parent is p. 16 | func (e DecodeErr) IsPlaceParent(p string) bool { 17 | return e.Place.Parent == p 18 | } 19 | 20 | // IsPlaceChildren reports if error place children is c. 21 | func (e DecodeErr) IsPlaceChildren(c string) bool { 22 | return e.Place.Children == c 23 | } 24 | 25 | // IsPlace reports if error place is p. 26 | func (e DecodeErr) IsPlace(p DecodeErrPlace) bool { 27 | return e.Place == p 28 | } 29 | 30 | // DecodeErrPlace records a place where error is occurred. 31 | type DecodeErrPlace struct { 32 | Parent string 33 | Children string 34 | } 35 | 36 | func (p DecodeErrPlace) String() string { 37 | return p.Parent + "/" + p.Children 38 | } 39 | 40 | func (e DecodeErr) Error() string { 41 | return "BadFormat for " + e.Place.String() + ": " + e.Message 42 | } 43 | 44 | func newDecodeErr(parent, children, message string) *DecodeErr { 45 | return &DecodeErr{ 46 | Place: DecodeErrPlace{Parent: parent, Children: children}, 47 | Message: message, 48 | } 49 | } 50 | 51 | // TODO(ar): rewrite errors to be more precise. 52 | func newAttrDecodeErr(children, message string) *DecodeErr { 53 | return newDecodeErr("attribute", children, message) 54 | } 55 | -------------------------------------------------------------------------------- /stun/errors_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import "testing" 4 | 5 | func TestDecodeErr_IsInvalidCookie(t *testing.T) { 6 | m := new(Message) 7 | m.WriteHeader() 8 | decoded := new(Message) 9 | m.Raw[4] = 55 10 | _, err := decoded.Write(m.Raw) 11 | if err == nil { 12 | t.Fatal("should error") 13 | } 14 | expected := "BadFormat for message/cookie: " + 15 | "3712a442 is invalid magic cookie (should be 2112a442)" 16 | if err.Error() != expected { 17 | t.Error(err, "!=", expected) 18 | } 19 | dErr, ok := err.(*DecodeErr) 20 | if !ok { 21 | t.Error("not decode error") 22 | } 23 | if !dErr.IsInvalidCookie() { 24 | t.Error("IsInvalidCookie = false, should be true") 25 | } 26 | if !dErr.IsPlaceChildren("cookie") { 27 | t.Error("bad children") 28 | } 29 | if !dErr.IsPlaceParent("message") { 30 | t.Error("bad parent") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /stun/examples/README.md: -------------------------------------------------------------------------------- 1 | # Fuzzer corpus 2 | 3 | examples directory contains corpus for fuzzer. -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/07d9d07df5b341d90ce313f890431fe22673e88e-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/07d9d07df5b341d90ce313f890431fe22673e88e-2 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/0f06d0415ec78e5a0670024180429a0d6622e90c-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/0f06d0415ec78e5a0670024180429a0d6622e90c-4 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/25f2f9fdee7f38b2ce075a03be4c487da027e0c9-1: -------------------------------------------------------------------------------- 1 | bHnary.L+infinityndi -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/323cc3d2eb5115119562afa212f6c124fb4c2f43-6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/323cc3d2eb5115119562afa212f6c124fb4c2f43-6 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/325742b8349899bd75473819334034de786d8173-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/325742b8349899bd75473819334034de786d8173-3 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/387d69f1e8239e36d3298b9e6459fcfe28aa60fe-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/387d69f1e8239e36d3298b9e6459fcfe28aa60fe-3 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/45921a3525e058441c0ffd7be81d6b2ac8fc72c7-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/45921a3525e058441c0ffd7be81d6b2ac8fc72c7-4 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/85745364c54dd7e3a7fe0019b98ce8af454ae963-7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/85745364c54dd7e3a7fe0019b98ce8af454ae963-7 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/895d16bc50ce45b6b3684175b57216876084bd81-8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/895d16bc50ce45b6b3684175b57216876084bd81-8 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/a26e2093b2d3212cd9ff57dbb8d4f62972077971-9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/a26e2093b2d3212cd9ff57dbb8d4f62972077971-9 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/a456f433f4c433a00b723e645759877bbccdd969-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/a456f433f4c433a00b723e645759877bbccdd969-4 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/a788ca1e84797136fec6e61f8bf1206622aec141-1: -------------------------------------------------------------------------------- 1 | 145519152283668U806 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/ac92f2f215a192e18307fb229c3e31d1d36ea72b-9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/ac92f2f215a192e18307fb229c3e31d1d36ea72b-9 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/b1ad18e20a8d34c86cec4db711408a71b16ed27d-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/b1ad18e20a8d34c86cec4db711408a71b16ed27d-5 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/bdc18abc9f9705cc46de5537fbf88bc16af9ea26-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/bdc18abc9f9705cc46de5537fbf88bc16af9ea26-2 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/c9e72ef3998f3e522d3c8e47b231a879f2372220-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/c9e72ef3998f3e522d3c8e47b231a879f2372220-4 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/ceafbc9130e5e938b7c76df54e1a2898e734cbdd-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/ceafbc9130e5e938b7c76df54e1a2898e734cbdd-4 -------------------------------------------------------------------------------- /stun/examples/stun-msg/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-msg/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709 -------------------------------------------------------------------------------- /stun/examples/stun-msg/crashers/1cc1e3902aa8cce338eb7b6e71d9552f4c6a79f7: -------------------------------------------------------------------------------- 1 | 000000000000000000 -------------------------------------------------------------------------------- /stun/examples/stun-msg/crashers/1cc1e3902aa8cce338eb7b6e71d9552f4c6a79f7.output: -------------------------------------------------------------------------------- 1 | panic: BadFormat for message/cookie: 0 is invalid magic cookie (should be 2112a442) 2 | 3 | goroutine 1 [running]: 4 | github.com/gortc/stun.FuzzMessage(0x7f2282f33000, 0x14, 0x200000, 0xc42000c740) 5 | /tmp/go-fuzz-build544265286/gopath/src/github.com/gortc/stun/fuzz.go:21 +0x356 6 | go-fuzz-dep.Main(0x511330) 7 | /tmp/go-fuzz-build544265286/goroot/src/go-fuzz-dep/main.go:49 +0xde 8 | main.main() 9 | /tmp/go-fuzz-build544265286/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 10 | 11 | goroutine 17 [syscall, locked to thread]: 12 | runtime.goexit() 13 | /tmp/go-fuzz-build544265286/goroot/src/runtime/asm_amd64.s:2197 +0x1 14 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-msg/crashers/1cc1e3902aa8cce338eb7b6e71d9552f4c6a79f7.quoted: -------------------------------------------------------------------------------- 1 | "00\x00\x000000000000000000" 2 | -------------------------------------------------------------------------------- /stun/examples/stun-msg/suppressions/682e7dcec53e1feb7a449dd7aa573cf2a06c3e92: -------------------------------------------------------------------------------- 1 | panic: BadFormat for message/cookie: 0 is invalid magic cookie (should be 2112a442) 2 | github.com/gortc/stun.FuzzMessage 3 | go-fuzz-dep.Main 4 | main.main 5 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/0faf54d4bd09022d40749a9a2607c517cc756cab-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/0faf54d4bd09022d40749a9a2607c517cc756cab-4 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/12a8b892d03fca186b2f182ebbbd27ac591c5e1d-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/12a8b892d03fca186b2f182ebbbd27ac591c5e1d-4 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/2aa1442c4a0c09e5a52ad31d325e74a1b63d6541-4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/2aa1442c4a0c09e5a52ad31d325e74a1b63d6541-4 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/2d0134ed3b9de132c720fe697b532b4c232ff9fe-6: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/31dfbd93e8e15ed443151ba2f3117fb0457929e6-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/31dfbd93e8e15ed443151ba2f3117fb0457929e6-2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/320355ced694aa69924f6bb82e7b74f420303fd9-6: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/403db8fb298e330d8d10b4df6ce302b168be125c-9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/403db8fb298e330d8d10b4df6ce302b168be125c-9 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/4a0a19218e082a343a1b17e5333409af9d98f0f5-5: -------------------------------------------------------------------------------- 1 | f -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/5184eb8960542005471ed1e60fd57f3617ca81fa-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/5184eb8960542005471ed1e60fd57f3617ca81fa-3 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/5c2dd944dde9e08881bef0894fe7b22a5c9c4b06-2: -------------------------------------------------------------------------------- 1 | j -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/69dbd1b5c36797f81437a3ebf28409da9912905c-6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/69dbd1b5c36797f81437a3ebf28409da9912905c-6 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/6b0d31c0d563223024da45691584643ac78c96e8-1: -------------------------------------------------------------------------------- 1 | m -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/889ab7aaf54aa9410297826cc8cb91119938cedb-7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/889ab7aaf54aa9410297826cc8cb91119938cedb-7 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/9be845a433ab87917f70fe5c8ed0c81d099ffa33-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/9be845a433ab87917f70fe5c8ed0c81d099ffa33-5 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/a277d65fbc50cfd312f8323fc4778af4acd80631-10: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/a277d65fbc50cfd312f8323fc4778af4acd80631-10 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/a7c309ed0e4ab135a14951675c982dd148fbc523-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/a7c309ed0e4ab135a14951675c982dd148fbc523-5 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/ac9231da4082430afe8f4d40127814c613648d8e-1: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/ad4d9292d016d8d7c4ab7a4a43e7a8f91a177591-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/ad4d9292d016d8d7c4ab7a4a43e7a8f91a177591-2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/bc26964852021aa26278b88156153fe4b53820cd-8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/bc26964852021aa26278b88156153fe4b53820cd-8 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/c3156e00d3c2588c639e0d3cf6821258b05761c7-4: -------------------------------------------------------------------------------- 1 | Q -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/c9ee5681d3c59f7541c27a38b67edf46259e187b-3: -------------------------------------------------------------------------------- 1 | V -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/ca4c203b3dd9f53dfff08f2ab1ed3801fedf480b-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/ca4c203b3dd9f53dfff08f2ab1ed3801fedf480b-3 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/d1854cae891ec7b29161ccaf79a24b00c274bdaa: -------------------------------------------------------------------------------- 1 | n -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/ea080fac5ccfc34e1cf7c5e2ffa5dacd377b701c-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/ea080fac5ccfc34e1cf7c5e2ffa5dacd377b701c-5 -------------------------------------------------------------------------------- /stun/examples/stun-setters/corpus/ee9791faca6cc73cb213f104dc9b32f202f53612-5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/corpus/ee9791faca6cc73cb213f104dc9b32f202f53612-5 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/43eb31a6590debc599d5839bb8a55afa80e49201: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/crashers/43eb31a6590debc599d5839bb8a55afa80e49201 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/43eb31a6590debc599d5839bb8a55afa80e49201.output: -------------------------------------------------------------------------------- 1 | panic: runtime error: slice bounds out of range 2 | 3 | goroutine 1 [running]: 4 | github.com/gortc/stun.(*XORMappedAddress).GetFromAs(0xc42000c900, 0xc420018320, 0x20, 0xc42001a3d8, 0xc420018338) 5 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/xoraddr.go:111 +0x2ae 6 | github.com/gortc/stun.(*XORMappedAddress).GetFrom(0xc42000c900, 0xc420018320, 0x7f73bf9e2001, 0x2) 7 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/xoraddr.go:137 +0x3c 8 | github.com/gortc/stun.FuzzSetters(0x7f73bf9e2000, 0x3, 0x200000, 0x3) 9 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/fuzz.go:97 +0x33c 10 | go-fuzz-dep.Main(0x517548) 11 | /tmp/go-fuzz-build843731397/goroot/src/go-fuzz-dep/main.go:49 +0xde 12 | main.main() 13 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 14 | 15 | goroutine 17 [syscall, locked to thread]: 16 | runtime.goexit() 17 | /tmp/go-fuzz-build843731397/goroot/src/runtime/asm_amd64.s:2197 +0x1 18 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/43eb31a6590debc599d5839bb8a55afa80e49201.quoted: -------------------------------------------------------------------------------- 1 | "\xef\x00\x01" 2 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/4dd09422696288b4f97acd0b7f51ffb3f07dec28: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/crashers/4dd09422696288b4f97acd0b7f51ffb3f07dec28 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/4dd09422696288b4f97acd0b7f51ffb3f07dec28.output: -------------------------------------------------------------------------------- 1 | panic: runtime error: index out of range 2 | 3 | goroutine 1 [running]: 4 | github.com/gortc/stun.fastXORBytes(0xc420010188, 0x4, 0x8, 0xc42001a21c, 0x5, 0x24, 0xc420053c98, 0x10, 0x10, 0xc420053c01) 5 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/xor.go:38 +0xf5 6 | github.com/gortc/stun.xorBytes(0xc420010188, 0x4, 0x8, 0xc42001a21c, 0x5, 0x24, 0xc420053c98, 0x10, 0x10, 0x40) 7 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/xor.go:59 +0x9d 8 | github.com/gortc/stun.(*XORMappedAddress).GetFromAs(0xc42000c600, 0xc4200181e0, 0x20, 0xc42001a218, 0xc4200181f8) 9 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/xoraddr.go:111 +0x281 10 | github.com/gortc/stun.(*XORMappedAddress).GetFrom(0xc42000c600, 0xc4200181e0, 0x7f58dc286001, 0x9) 11 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/xoraddr.go:137 +0x3c 12 | github.com/gortc/stun.FuzzSetters(0x7f58dc286000, 0xa, 0x200000, 0xc420092140) 13 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/fuzz.go:97 +0x33c 14 | go-fuzz-dep.Main(0x517548) 15 | /tmp/go-fuzz-build843731397/goroot/src/go-fuzz-dep/main.go:49 +0xde 16 | main.main() 17 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 18 | 19 | goroutine 17 [syscall, locked to thread]: 20 | runtime.goexit() 21 | /tmp/go-fuzz-build843731397/goroot/src/runtime/asm_amd64.s:2197 +0x1 22 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/4dd09422696288b4f97acd0b7f51ffb3f07dec28.quoted: -------------------------------------------------------------------------------- 1 | "\xef\x00\x010000000" 2 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/5c0137314c031cc920b1ac6497600cc1ad234a9e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/crashers/5c0137314c031cc920b1ac6497600cc1ad234a9e -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/5c0137314c031cc920b1ac6497600cc1ad234a9e.output: -------------------------------------------------------------------------------- 1 | failed to add atribute to m2 2 | panic: Length of USERNAME attribute 516 exceeds maximum 513 3 | 4 | goroutine 1 [running]: 5 | github.com/gortc/stun.FuzzSetters(0x7f95ca828000, 0x205, 0x200000, 0xc42000c720) 6 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/fuzz.go:119 +0x64a 7 | go-fuzz-dep.Main(0x517690) 8 | /tmp/go-fuzz-build224667416/goroot/src/go-fuzz-dep/main.go:49 +0xde 9 | main.main() 10 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 11 | 12 | goroutine 17 [syscall, locked to thread]: 13 | runtime.goexit() 14 | /tmp/go-fuzz-build224667416/goroot/src/runtime/asm_amd64.s:2197 +0x1 15 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/5c0137314c031cc920b1ac6497600cc1ad234a9e.quoted: -------------------------------------------------------------------------------- 1 | "\xef0000000000000000000" + 2 | "00000000000000000000" + 3 | "00000000000000000000" + 4 | "00000000000000000000" + 5 | "00000000000000000000" + 6 | "00000000000000000000" + 7 | "00000000000000000000" + 8 | "00000000000000000000" + 9 | "00000000000000000000" + 10 | "00000000000000000000" + 11 | "00000000000000000000" + 12 | "00000000000000000000" + 13 | "00000000000000000000" + 14 | "00000000000000000000" + 15 | "00000000000000000000" + 16 | "00000000000000000000" + 17 | "00000000000000000000" + 18 | "00000000000000000000" + 19 | "00000000000000000000" + 20 | "00000000000000000000" + 21 | "00000000000000000000" + 22 | "00000000000000000000" + 23 | "00000000000000000000" + 24 | "00000000000000000000" + 25 | "00000000000000000000" + 26 | "00000000000000000" 27 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/6934105ad50010b814c933314b1da6841431bc8b: -------------------------------------------------------------------------------- 1 | 00000 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/6934105ad50010b814c933314b1da6841431bc8b.output: -------------------------------------------------------------------------------- 1 | not equal 2 | panic: not equal 3 | 4 | goroutine 1 [running]: 5 | github.com/gortc/stun.FuzzSetters(0x7f8dd6ff0000, 0x5, 0x200000, 0xc42000c720) 6 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/fuzz.go:114 +0x4ff 7 | go-fuzz-dep.Main(0x517548) 8 | /tmp/go-fuzz-build843731397/goroot/src/go-fuzz-dep/main.go:49 +0xde 9 | main.main() 10 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 11 | 12 | goroutine 17 [syscall, locked to thread]: 13 | runtime.goexit() 14 | /tmp/go-fuzz-build843731397/goroot/src/runtime/asm_amd64.s:2197 +0x1 15 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/6934105ad50010b814c933314b1da6841431bc8b.quoted: -------------------------------------------------------------------------------- 1 | "00000" 2 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/72d92b32ce3d5c8239c45e57f47eff0e6e8abf05: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/crashers/72d92b32ce3d5c8239c45e57f47eff0e6e8abf05 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/72d92b32ce3d5c8239c45e57f47eff0e6e8abf05.output: -------------------------------------------------------------------------------- 1 | failed to add atribute to m2 2 | panic: Length of USERNAME attribute 514 exceeds maximum 513 3 | 4 | goroutine 1 [running]: 5 | github.com/gortc/stun.FuzzSetters(0x7f6511967000, 0x203, 0x200000, 0xc42000c720) 6 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/fuzz.go:119 +0x64a 7 | go-fuzz-dep.Main(0x517690) 8 | /tmp/go-fuzz-build224667416/goroot/src/go-fuzz-dep/main.go:49 +0xde 9 | main.main() 10 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 11 | 12 | goroutine 17 [syscall, locked to thread]: 13 | runtime.goexit() 14 | /tmp/go-fuzz-build224667416/goroot/src/runtime/asm_amd64.s:2197 +0x1 15 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/72d92b32ce3d5c8239c45e57f47eff0e6e8abf05.quoted: -------------------------------------------------------------------------------- 1 | "\xef0000000000000000000" + 2 | "00000000000000000000" + 3 | "00000000000000000000" + 4 | "00000000000000000000" + 5 | "00000000000000000000" + 6 | "00000000000000000000" + 7 | "00000000000000000000" + 8 | "00000000000000000000" + 9 | "00000000000000000000" + 10 | "00000000000000000000" + 11 | "00000000000000000000" + 12 | "00000000000000000000" + 13 | "00000000000000000000" + 14 | "00000000000000000000" + 15 | "00000000000000000000" + 16 | "00000000000000000000" + 17 | "00000000000000000000" + 18 | "00000000000000000000" + 19 | "00000000000000000000" + 20 | "00000000000000000000" + 21 | "00000000000000000000" + 22 | "00000000000000000000" + 23 | "00000000000000000000" + 24 | "00000000000000000000" + 25 | "00000000000000000000" + 26 | "000000000000000" 27 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/8dc00598417d4eb788a77ac6ccef3cb484905d8b: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/8dc00598417d4eb788a77ac6ccef3cb484905d8b.output: -------------------------------------------------------------------------------- 1 | panic: runtime error: index out of range 2 | 3 | goroutine 1 [running]: 4 | github.com/gortc/stun.(*ErrorCodeAttribute).GetFrom(0xc42000cfa0, 0xc420018550, 0x7f6a6451c000, 0x1) 5 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/errorcode.go:55 +0xed 6 | github.com/gortc/stun.FuzzSetters(0x7f6a6451c000, 0x1, 0x200000, 0x3) 7 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/fuzz.go:97 +0x33c 8 | go-fuzz-dep.Main(0x517548) 9 | /tmp/go-fuzz-build843731397/goroot/src/go-fuzz-dep/main.go:49 +0xde 10 | main.main() 11 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 12 | 13 | goroutine 17 [syscall, locked to thread]: 14 | runtime.goexit() 15 | /tmp/go-fuzz-build843731397/goroot/src/runtime/asm_amd64.s:2197 +0x1 16 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/8dc00598417d4eb788a77ac6ccef3cb484905d8b.quoted: -------------------------------------------------------------------------------- 1 | "\x05" 2 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/90c77b8e1c800456b5a4a46693d0ff806aa94fa5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/crashers/90c77b8e1c800456b5a4a46693d0ff806aa94fa5 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/90c77b8e1c800456b5a4a46693d0ff806aa94fa5.output: -------------------------------------------------------------------------------- 1 | failed to add atribute to m2 2 | panic: Length of USERNAME attribute 524 exceeds maximum 513 3 | 4 | goroutine 1 [running]: 5 | github.com/gortc/stun.FuzzSetters(0x7fe06992a000, 0x20d, 0x200000, 0xc42000c720) 6 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/fuzz.go:119 +0x64a 7 | go-fuzz-dep.Main(0x517690) 8 | /tmp/go-fuzz-build224667416/goroot/src/go-fuzz-dep/main.go:49 +0xde 9 | main.main() 10 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 11 | 12 | goroutine 17 [syscall, locked to thread]: 13 | runtime.goexit() 14 | /tmp/go-fuzz-build224667416/goroot/src/runtime/asm_amd64.s:2197 +0x1 15 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/90c77b8e1c800456b5a4a46693d0ff806aa94fa5.quoted: -------------------------------------------------------------------------------- 1 | "\xef\x17/\xff\xff\u007fuintptr\xff\xef\xf2\xc5596" + 2 | "0464477539\xbdM\x00\x01\xbd\xbf\xefABC" + 3 | "DEFGHIJKLMNOPQRSTUVW" + 4 | "XYZ\xbd\x18�abcdefghijkl" + 5 | "mnopqrstuvwxyz012006" + 6 | "2uvwxyz0\xc5:\xcd\xcd\xdfY\xbf\xbd\x18�" + 7 | "\ny�\xbd\xbf\xef0(\xbd\xbf\xefM\x00\x01\xbd\xbf\xefA" + 8 | "BCDEFGHIJKLMABCDEFGH" + 9 | "IJKLMNOPQRSTUVWXYZab" + 10 | "cdefghijklmnopqrstuv" + 11 | "wxyz0123456789-_NOPQ" + 12 | "RSTUVWXYZ\xbd\x18�ab\xbf\xbd\x18\xef" + 13 | "\xbf\xbd\nyklmnopqrstuvwxyn" + 14 | "tptr\xff\xef\xf2\xc5596046447753" + 15 | "9\xbdM\x00\x01\xbd\xbf\xefAOPQRSTUVWXY" + 16 | "Z\xbd\x18�abcdefghijklmn" + 17 | "opqrstuvwxyz0120062u" + 18 | "vwxyz0\xc5:\xcd\xcd\xdfY\xbf\xbd\x18�\ny" + 19 | "�\xbd\xbf\xef0(\xbd\xbf\xefM\x00\x01\xbd\xbf\xefABC" + 20 | "DEFGHIJKLMABCDEFGHIJ" + 21 | "KLMNOPQRSTUVWXYZabcd" + 22 | "efghijklmnopqrstuvwx" + 23 | "yz0123456789-_NOPQRS" + 24 | "TUVWXYZ\xbd\x18�ab\xbf\xbd\x18�" + 25 | "\nyklmnopqrstuvwxyz01" + 26 | "20456789-_\xbdz01204567" + 27 | "89-_\xbd" 28 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/a6de690b04292410e3c88d2e3cd967e6cb0e51ec: -------------------------------------------------------------------------------- 1 | m -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/a6de690b04292410e3c88d2e3cd967e6cb0e51ec.output: -------------------------------------------------------------------------------- 1 | panic: runtime error: slice bounds out of range 2 | 3 | goroutine 1 [running]: 4 | github.com/gortc/stun.(*MappedAddress).getAs(0xc42000c960, 0xc420018320, 0x8023, 0xc42001a3d8, 0xc420018338) 5 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/addr.go:71 +0x1e8 6 | github.com/gortc/stun.(*AlternateServer).GetFrom(0xc42000c960, 0xc420018320, 0x7f47a877a001, 0x2) 7 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/addr.go:37 +0x3c 8 | github.com/gortc/stun.FuzzSetters(0x7f47a877a000, 0x3, 0x200000, 0x3) 9 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/fuzz.go:97 +0x33c 10 | go-fuzz-dep.Main(0x517548) 11 | /tmp/go-fuzz-build843731397/goroot/src/go-fuzz-dep/main.go:49 +0xde 12 | main.main() 13 | /tmp/go-fuzz-build843731397/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 14 | 15 | goroutine 17 [syscall, locked to thread]: 16 | runtime.goexit() 17 | /tmp/go-fuzz-build843731397/goroot/src/runtime/asm_amd64.s:2197 +0x1 18 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/a6de690b04292410e3c88d2e3cd967e6cb0e51ec.quoted: -------------------------------------------------------------------------------- 1 | "m\x00\x01" 2 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/f9309435560bc14774700f46f7ba898bfb982441: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-setters/crashers/f9309435560bc14774700f46f7ba898bfb982441 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/f9309435560bc14774700f46f7ba898bfb982441.output: -------------------------------------------------------------------------------- 1 | failed to add atribute to m2 2 | panic: Length of USERNAME attribute 537 exceeds maximum 513 3 | 4 | goroutine 1 [running]: 5 | github.com/gortc/stun.FuzzSetters(0x7f83c660e000, 0x21a, 0x200000, 0x3) 6 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/fuzz.go:119 +0x64a 7 | go-fuzz-dep.Main(0x517690) 8 | /tmp/go-fuzz-build224667416/goroot/src/go-fuzz-dep/main.go:49 +0xde 9 | main.main() 10 | /tmp/go-fuzz-build224667416/gopath/src/github.com/gortc/stun/go.fuzz.main/main.go:10 +0x2d 11 | 12 | goroutine 17 [syscall, locked to thread]: 13 | runtime.goexit() 14 | /tmp/go-fuzz-build224667416/goroot/src/runtime/asm_amd64.s:2197 +0x1 15 | exit status 2 -------------------------------------------------------------------------------- /stun/examples/stun-setters/crashers/f9309435560bc14774700f46f7ba898bfb982441.quoted: -------------------------------------------------------------------------------- 1 | "\xef\x17/\xff\xff\u007fuintptr\xff\xef\xf2\xc5596" + 2 | "0464477539\xbdM\x00\x01\xbd\xbf\xefABC" + 3 | "DEFGHIJKLMNOPQRSTUVW" + 4 | "XYZ\xbd\x18�abcdefghijkl" + 5 | "mnopqrstuvwxyz012006" + 6 | "2uvwxyz0\xc5:\xcd\xcd\xdfY\xbf\xbd\x18�" + 7 | "\ny�\xbd\xbf\xef0(\xbd\xbf\xefM\x00\x01\xbd\xbf\xefA" + 8 | "BCDEFGHIJKLMABCDEFGH" + 9 | "IJKLMNOPQRSTUVWXYZab" + 10 | "cdefghijklmnopqrstuv" + 11 | "wxyz0123456789-_NOPQ" + 12 | "RSTUVWXYZ\xbd\x18�ab\xbf\xbd\x18\xef" + 13 | "\xbf\xbd\nyklmnopqrstuvwxyn" + 14 | "tptr\xff\xef\xf2\xc5596046447753" + 15 | "9\xbdM\x00\x01\xbd\xbf\xefABCDEFGHIJKL" + 16 | "MNOPQRSTUVWXYZ\xbd\x18�a" + 17 | "bcdefghijklmnopqrstu" + 18 | "vwxyz0120062uvwxyz0\xc5" + 19 | ":\xcd\xcd\xdfY\xbf\xbd\x18�\ny�\xbd\xbf\xef0" + 20 | "(\xbd\xbf\xefM\x00\x01\xbd\xbf\xefABCDEFGHIJ" + 21 | "KLMABCDEFGHIJKLMNOPQ" + 22 | "RSTUVWXYZabcdefghijk" + 23 | "lmnopqrstuvwxyz01234" + 24 | "56789-_NOPQRSTUVWXYZ" + 25 | "\xbd\x18�ab\xbf\xbd\x18�\nyklmno" + 26 | "pqrstuvwxyz012045678" + 27 | "9-_\xbdz0120456789-_\xbd" 28 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/141f41aee117afb4798f3cba5fc9f470beed94a1: -------------------------------------------------------------------------------- 1 | panic: runtime error: index out of range 2 | github.com/gortc/stun.fastXORBytes 3 | github.com/gortc/stun.xorBytes 4 | github.com/gortc/stun.(*XORMappedAddress).GetFromAs 5 | github.com/gortc/stun.(*XORMappedAddress).GetFrom 6 | github.com/gortc/stun.FuzzSetters 7 | go-fuzz-dep.Main 8 | main.main 9 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/29b71161b1355c93a882a7fa6b8ff098113ac76b: -------------------------------------------------------------------------------- 1 | panic: Length of USERNAME attribute 514 exceeds maximum 513 2 | github.com/gortc/stun.FuzzSetters 3 | go-fuzz-dep.Main 4 | main.main 5 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/5607cf1f2a7846dd0f8f775daf7a350f5d1c0734: -------------------------------------------------------------------------------- 1 | panic: Length of USERNAME attribute 537 exceeds maximum 513 2 | github.com/gortc/stun.FuzzSetters 3 | go-fuzz-dep.Main 4 | main.main 5 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/58915993609c5d48730aecf40b3b3cede8c1a9af: -------------------------------------------------------------------------------- 1 | panic: Length of USERNAME attribute 524 exceeds maximum 513 2 | github.com/gortc/stun.FuzzSetters 3 | go-fuzz-dep.Main 4 | main.main 5 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/76d8cd83b54ce64d6d1b9aaaf16f3f383f28b804: -------------------------------------------------------------------------------- 1 | panic: not equal 2 | github.com/gortc/stun.FuzzSetters 3 | go-fuzz-dep.Main 4 | main.main 5 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/b61c92191d726cb462f0fd36cdab989880235bdf: -------------------------------------------------------------------------------- 1 | panic: runtime error: slice bounds out of range 2 | github.com/gortc/stun.(*MappedAddress).getAs 3 | github.com/gortc/stun.(*AlternateServer).GetFrom 4 | github.com/gortc/stun.FuzzSetters 5 | go-fuzz-dep.Main 6 | main.main 7 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/d8b9c1359784aa7a593c0b8141e4468ad6a50bf8: -------------------------------------------------------------------------------- 1 | panic: runtime error: slice bounds out of range 2 | github.com/gortc/stun.(*XORMappedAddress).GetFromAs 3 | github.com/gortc/stun.(*XORMappedAddress).GetFrom 4 | github.com/gortc/stun.FuzzSetters 5 | go-fuzz-dep.Main 6 | main.main 7 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/e2f854b5a3caf1482e7314722f9c3b5d2edc3e93: -------------------------------------------------------------------------------- 1 | panic: runtime error: index out of range 2 | github.com/gortc/stun.(*ErrorCodeAttribute).GetFrom 3 | github.com/gortc/stun.FuzzSetters 4 | go-fuzz-dep.Main 5 | main.main 6 | -------------------------------------------------------------------------------- /stun/examples/stun-setters/suppressions/f5d7ef9e130b5d534dee3a3da3878e610003855c: -------------------------------------------------------------------------------- 1 | panic: Length of USERNAME attribute 516 exceeds maximum 513 2 | github.com/gortc/stun.FuzzSetters 3 | go-fuzz-dep.Main 4 | main.main 5 | -------------------------------------------------------------------------------- /stun/examples/stun-typ/corpus/02af615c9595bc675b21b30ff2de725877e47207-3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-typ/corpus/02af615c9595bc675b21b30ff2de725877e47207-3 -------------------------------------------------------------------------------- /stun/examples/stun-typ/corpus/0de292b04c19b5aef164070475a47f94be80a615-1: -------------------------------------------------------------------------------- 1 | ��� -------------------------------------------------------------------------------- /stun/examples/stun-typ/corpus/2a89163095f14efe95934fc14a29c10d5a4d9ddf-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-typ/corpus/2a89163095f14efe95934fc14a29c10d5a4d9ddf-1 -------------------------------------------------------------------------------- /stun/examples/stun-typ/corpus/2f3662c1d727be578d15cd6512dc714da0092020-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-typ/corpus/2f3662c1d727be578d15cd6512dc714da0092020-1 -------------------------------------------------------------------------------- /stun/examples/stun-typ/corpus/9bdb77276c1852e1fb067820472812fcf6084024-1: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /stun/examples/stun-typ/corpus/cd8fd7e44f6096d78161657475088cd3fb87398d-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-typ/corpus/cd8fd7e44f6096d78161657475088cd3fb87398d-2 -------------------------------------------------------------------------------- /stun/examples/stun-typ/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-typ/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709 -------------------------------------------------------------------------------- /stun/examples/stun-typ/crashers/da39a3ee5e6b4b0d3255bfef95601890afd80709: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-typ/crashers/da39a3ee5e6b4b0d3255bfef95601890afd80709 -------------------------------------------------------------------------------- /stun/examples/stun-typ/crashers/da39a3ee5e6b4b0d3255bfef95601890afd80709.output: -------------------------------------------------------------------------------- 1 | signal: interrupt -------------------------------------------------------------------------------- /stun/examples/stun-typ/crashers/da39a3ee5e6b4b0d3255bfef95601890afd80709.quoted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/examples/stun-typ/crashers/da39a3ee5e6b4b0d3255bfef95601890afd80709.quoted -------------------------------------------------------------------------------- /stun/examples/stun-typ/suppressions/1df2ba61c49ca26ff8124345d40bca8057f55e7c: -------------------------------------------------------------------------------- 1 | panic: v != v2 2 | panic 3 | github.com/cydev/stun.FuzzType 4 | go-fuzz-dep.Main 5 | main.main 6 | -------------------------------------------------------------------------------- /stun/examples/stun-typ/suppressions/6580320c92b36eca1e0a55e10b062ca67868a28e: -------------------------------------------------------------------------------- 1 | signal: interrupt -------------------------------------------------------------------------------- /stun/fingerprint.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "fmt" 5 | "hash/crc32" 6 | ) 7 | 8 | // FingerprintAttr represents FINGERPRINT attribute. 9 | // 10 | // https://tools.ietf.org/html/rfc5389#section-15.5 11 | type FingerprintAttr byte 12 | 13 | // CRCMismatch represents CRC check error. 14 | type CRCMismatch struct { 15 | Expected uint32 16 | Actual uint32 17 | } 18 | 19 | func (m CRCMismatch) Error() string { 20 | return fmt.Sprintf("CRC mismatch: %x (expected) != %x (actual)", 21 | m.Expected, 22 | m.Actual, 23 | ) 24 | } 25 | 26 | // Fingerprint is shorthand for FingerprintAttr. 27 | // 28 | // Example: 29 | // 30 | // m := New() 31 | // Fingerprint.AddTo(m) 32 | var Fingerprint FingerprintAttr 33 | 34 | const ( 35 | fingerprintXORValue uint32 = 0x5354554e 36 | fingerprintSize = 4 // 32 bit 37 | ) 38 | 39 | // FingerprintValue returns CRC-32 of b XOR-ed by 0x5354554e. 40 | // 41 | // The value of the attribute is computed as the CRC-32 of the STUN message 42 | // up to (but excluding) the FINGERPRINT attribute itself, XOR'ed with 43 | // the 32-bit value 0x5354554e (the XOR helps in cases where an 44 | // application packet is also using CRC-32 in it). 45 | func FingerprintValue(b []byte) uint32 { 46 | return crc32.ChecksumIEEE(b) ^ fingerprintXORValue // XOR 47 | } 48 | 49 | // AddTo adds fingerprint to message. 50 | func (FingerprintAttr) AddTo(m *Message) error { 51 | l := m.Length 52 | // length in header should include size of fingerprint attribute 53 | m.Length += fingerprintSize + attributeHeaderSize // increasing length 54 | m.WriteLength() // writing Length to Raw 55 | b := make([]byte, fingerprintSize) 56 | val := FingerprintValue(m.Raw) 57 | bin.PutUint32(b, val) 58 | m.Length = l 59 | m.Add(AttrFingerprint, b) 60 | return nil 61 | } 62 | 63 | // Check reads fingerprint value from m and checks it, returning error if any. 64 | // Can return *AttrLengthErr, ErrAttributeNotFound, and *CRCMismatch. 65 | func (FingerprintAttr) Check(m *Message) error { 66 | b, err := m.Get(AttrFingerprint) 67 | if err != nil { 68 | return err 69 | } 70 | if len(b) != fingerprintSize { 71 | return &AttrLengthErr{ 72 | Expected: fingerprintSize, 73 | Got: len(b), 74 | Attr: AttrFingerprint, 75 | } 76 | } 77 | val := bin.Uint32(b) 78 | attrStart := len(m.Raw) - (fingerprintSize + attributeHeaderSize) 79 | expected := FingerprintValue(m.Raw[:attrStart]) 80 | if expected != val { 81 | return &CRCMismatch{Expected: expected, Actual: val} 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /stun/fingerprint_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkFingerprint_AddTo(b *testing.B) { 9 | b.ReportAllocs() 10 | m := new(Message) 11 | s := NewSoftware("software") 12 | addr := &XORMappedAddress{ 13 | IP: net.IPv4(213, 1, 223, 5), 14 | } 15 | addAttr(b, m, addr) 16 | addAttr(b, m, s) 17 | b.SetBytes(int64(len(m.Raw))) 18 | for i := 0; i < b.N; i++ { 19 | Fingerprint.AddTo(m) 20 | m.WriteLength() 21 | m.Length -= attributeHeaderSize + fingerprintSize 22 | m.Raw = m.Raw[:m.Length+messageHeaderSize] 23 | m.Attributes = m.Attributes[:len(m.Attributes)-1] 24 | } 25 | } 26 | 27 | func TestFingerprint_Check(t *testing.T) { 28 | m := new(Message) 29 | addAttr(t, m, NewSoftware("software")) 30 | m.WriteHeader() 31 | Fingerprint.AddTo(m) 32 | m.WriteHeader() 33 | if err := Fingerprint.Check(m); err != nil { 34 | t.Error(err) 35 | } 36 | m.Raw[3] = m.Raw[3] + 1 37 | if err, ok := Fingerprint.Check(m).(*CRCMismatch); !ok { 38 | t.Error(err, "should be *CRCMissmatch") 39 | } 40 | } 41 | 42 | func TestFingerprint_CheckBad(t *testing.T) { 43 | m := new(Message) 44 | addAttr(t, m, NewSoftware("software")) 45 | m.WriteHeader() 46 | if err := Fingerprint.Check(m); err == nil { 47 | t.Error("should error") 48 | } 49 | m.Add(AttrFingerprint, []byte{1, 2, 3}) 50 | if err := Fingerprint.Check(m); err == nil { 51 | t.Error("should error") 52 | } 53 | } 54 | 55 | func BenchmarkFingerprint_Check(b *testing.B) { 56 | b.ReportAllocs() 57 | m := new(Message) 58 | s := NewSoftware("software") 59 | addr := &XORMappedAddress{ 60 | IP: net.IPv4(213, 1, 223, 5), 61 | } 62 | addAttr(b, m, addr) 63 | addAttr(b, m, s) 64 | m.WriteHeader() 65 | Fingerprint.AddTo(m) 66 | m.WriteHeader() 67 | b.SetBytes(int64(len(m.Raw))) 68 | for i := 0; i < b.N; i++ { 69 | if err := Fingerprint.Check(m); err != nil { 70 | b.Fatal(err) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /stun/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package stun 4 | 5 | import ( 6 | "encoding/binary" 7 | "fmt" 8 | ) 9 | 10 | var ( 11 | m = New() 12 | ) 13 | 14 | // FuzzMessage is go-fuzz endpoint for message. 15 | func FuzzMessage(data []byte) int { 16 | m.Reset() 17 | // fuzzer dont know about cookies 18 | binary.BigEndian.PutUint32(data[4:8], magicCookie) 19 | // trying to read data as message 20 | if _, err := m.Write(data); err != nil { 21 | return 0 22 | } 23 | m2 := New() 24 | if _, err := m2.Write(m.Raw); err != nil { 25 | panic(err) 26 | } 27 | if m2.TransactionID != m.TransactionID { 28 | panic("transaction ID mismatch") 29 | } 30 | if m2.Type != m.Type { 31 | panic("type missmatch") 32 | } 33 | if len(m2.Attributes) != len(m.Attributes) { 34 | panic("attributes length missmatch") 35 | } 36 | return 1 37 | } 38 | 39 | // FuzzType is go-fuzz endpoint for message type. 40 | func FuzzType(data []byte) int { 41 | t := MessageType{} 42 | vt, _ := binary.Uvarint(data) 43 | v := uint16(vt) & 0x1fff // first 3 bits are empty 44 | t.ReadValue(v) 45 | v2 := t.Value() 46 | if v != v2 { 47 | panic("v != v2") 48 | } 49 | t2 := MessageType{} 50 | t2.ReadValue(v2) 51 | if t2 != t { 52 | panic("t2 != t") 53 | } 54 | return 0 55 | } 56 | 57 | type attr interface { 58 | Getter 59 | Setter 60 | } 61 | 62 | type attrs []struct { 63 | g attr 64 | t AttrType 65 | } 66 | 67 | func (a attrs) pick(v byte) struct { 68 | g attr 69 | t AttrType 70 | } { 71 | idx := int(v) % len(a) 72 | return a[idx] 73 | } 74 | 75 | func FuzzSetters(data []byte) int { 76 | var ( 77 | m1 = &Message{ 78 | Raw: make([]byte, 0, 2048), 79 | } 80 | m2 = &Message{ 81 | Raw: make([]byte, 0, 2048), 82 | } 83 | m3 = &Message{ 84 | Raw: make([]byte, 0, 2048), 85 | } 86 | ) 87 | attributes := attrs{ 88 | {new(Realm), AttrRealm}, 89 | {new(XORMappedAddress), AttrXORMappedAddress}, 90 | {new(Nonce), AttrNonce}, 91 | {new(Software), AttrSoftware}, 92 | {new(AlternateServer), AttrAlternateServer}, 93 | {new(ErrorCodeAttribute), AttrErrorCode}, 94 | {new(UnknownAttributes), AttrUnknownAttributes}, 95 | {new(Username), AttrUsername}, 96 | } 97 | var firstByte = byte(0) 98 | if len(data) > 0 { 99 | firstByte = data[0] 100 | } 101 | a := attributes.pick(firstByte) 102 | value := data 103 | if len(data) > 1 { 104 | value = value[1:] 105 | } 106 | m1.WriteHeader() 107 | m1.Add(a.t, value) 108 | err := a.g.GetFrom(m1) 109 | if err == ErrAttributeNotFound { 110 | fmt.Println("unexpected 404") 111 | panic(err) 112 | } 113 | if err != nil { 114 | return 1 115 | } 116 | m2.WriteHeader() 117 | if err = a.g.AddTo(m2); err != nil { 118 | // We allow decoding some text attributes 119 | // when their length is too big, but 120 | // not encoding. 121 | _, ok := err.(*AttrOverflowErr) 122 | if !ok { 123 | panic(err) 124 | } 125 | return 1 126 | } 127 | m3.WriteHeader() 128 | v, err := m2.Get(a.t) 129 | if err != nil { 130 | panic(err) 131 | } 132 | m3.Add(a.t, v) 133 | 134 | if !m2.Equal(m3) { 135 | fmt.Println(m2, "not equal", m3) 136 | panic("not equal") 137 | } 138 | return 1 139 | } 140 | -------------------------------------------------------------------------------- /stun/fuzz_test.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package stun 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestMessageType_FuzzerCrash1(t *testing.T) { 13 | input := []byte("\x9c\xbe\x03") 14 | FuzzType(input) 15 | } 16 | 17 | func TestMessageCrash2(t *testing.T) { 18 | input := []byte("00\x00\x000000000000000000") 19 | FuzzMessage(input) 20 | } 21 | 22 | func corpus(t *testing.T, function, typ string) [][]byte { 23 | var data [][]byte 24 | p := filepath.Join("examples", function, typ) 25 | f, err := os.Open(p) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | list, err := f.Readdir(-1) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | for _, d := range list { 34 | if strings.Contains(d.Name(), ".") { 35 | // Skipping non-raw files. 36 | continue 37 | } 38 | df, err := os.Open(filepath.Join(p, d.Name())) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | buf := make([]byte, 5000) 43 | n, _ := df.Read(buf) 44 | data = append(data, buf[:n]) 45 | df.Close() 46 | } 47 | return data 48 | } 49 | 50 | func TestFuzzMessage_Coverage(t *testing.T) { 51 | for _, buf := range corpus(t, "stun-msg", "corpus") { 52 | FuzzMessage(buf) 53 | } 54 | } 55 | 56 | func TestFuzzMessage_Crashers(t *testing.T) { 57 | for _, buf := range corpus(t, "stun-msg", "crashers") { 58 | FuzzMessage(buf) 59 | } 60 | } 61 | 62 | func TestFuzzType_Coverage(t *testing.T) { 63 | for _, buf := range corpus(t, "stun-typ", "corpus") { 64 | FuzzType(buf) 65 | } 66 | } 67 | 68 | func TestFuzzType_Crashers(t *testing.T) { 69 | for _, buf := range corpus(t, "stun-typ", "crashers") { 70 | FuzzType(buf) 71 | } 72 | } 73 | 74 | func TestAttrPick(t *testing.T) { 75 | attributes := attrs{ 76 | {new(XORMappedAddress), AttrXORMappedAddress}, 77 | } 78 | for i := byte(0); i < 255; i++ { 79 | attributes.pick(i) 80 | } 81 | } 82 | 83 | func TestFuzzSetters_Crashers(t *testing.T) { 84 | for _, buf := range corpus(t, "stun-setters", "crashers") { 85 | FuzzSetters(buf) 86 | } 87 | } 88 | 89 | func TestFuzzSetters_Coverage(t *testing.T) { 90 | for _, buf := range corpus(t, "stun-setters", "corpus") { 91 | FuzzSetters(buf) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /stun/helpers.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | // Interfaces that are implemented by message attributes, shorthands for them, 4 | // or helpers for message fields as type or transaction id. 5 | type ( 6 | // Setter sets *Message attribute. 7 | Setter interface { 8 | AddTo(m *Message) error 9 | } 10 | // Getter parses attribute from *Message. 11 | Getter interface { 12 | GetFrom(m *Message) error 13 | } 14 | // Checker checks *Message attribute. 15 | Checker interface { 16 | Check(m *Message) error 17 | } 18 | ) 19 | 20 | // Build resets message and applies setters to it in batch, returning on 21 | // first error. To prevent allocations, pass pointers to values. 22 | // 23 | // Example: 24 | // var ( 25 | // t = BindingRequest 26 | // username = NewUsername("username") 27 | // nonce = NewNonce("nonce") 28 | // realm = NewRealm("example.org") 29 | // ) 30 | // m := new(Message) 31 | // m.Build(t, username, nonce, realm) // 4 allocations 32 | // m.Build(&t, &username, &nonce, &realm) // 0 allocations 33 | // 34 | // See BenchmarkBuildOverhead. 35 | func (m *Message) Build(setters ...Setter) error { 36 | m.Reset() 37 | m.WriteHeader() 38 | for _, s := range setters { 39 | if err := s.AddTo(m); err != nil { 40 | return err 41 | } 42 | } 43 | return nil 44 | } 45 | 46 | // Check applies checkers to message in batch, returning on first error. 47 | func (m *Message) Check(checkers ...Checker) error { 48 | for _, c := range checkers { 49 | if err := c.Check(m); err != nil { 50 | return err 51 | } 52 | } 53 | return nil 54 | } 55 | 56 | // Parse applies getters to message in batch, returning on first error. 57 | func (m *Message) Parse(getters ...Getter) error { 58 | for _, c := range getters { 59 | if err := c.GetFrom(m); err != nil { 60 | return err 61 | } 62 | } 63 | return nil 64 | } 65 | 66 | // MustBuild wraps Build call and panics on error. 67 | func MustBuild(setters ...Setter) *Message { 68 | m, err := Build(setters...) 69 | if err != nil { 70 | panic(err) 71 | } 72 | return m 73 | } 74 | 75 | // Build wraps Message.Build method. 76 | func Build(setters ...Setter) (*Message, error) { 77 | m := new(Message) 78 | return m, m.Build(setters...) 79 | } 80 | -------------------------------------------------------------------------------- /stun/helpers_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkBuildOverhead(b *testing.B) { 9 | var ( 10 | t = BindingRequest 11 | username = NewUsername("username") 12 | nonce = NewNonce("nonce") 13 | realm = NewRealm("example.org") 14 | ) 15 | b.Run("Build", func(b *testing.B) { 16 | b.ReportAllocs() 17 | m := new(Message) 18 | for i := 0; i < b.N; i++ { 19 | m.Build(&t, &username, &nonce, &realm, &Fingerprint) 20 | } 21 | }) 22 | b.Run("BuildNonPointer", func(b *testing.B) { 23 | b.ReportAllocs() 24 | m := new(Message) 25 | for i := 0; i < b.N; i++ { 26 | m.Build(t, username, nonce, realm, Fingerprint) 27 | } 28 | }) 29 | b.Run("Raw", func(b *testing.B) { 30 | b.ReportAllocs() 31 | m := new(Message) 32 | for i := 0; i < b.N; i++ { 33 | m.Reset() 34 | m.WriteHeader() 35 | m.SetType(t) 36 | username.AddTo(m) 37 | nonce.AddTo(m) 38 | realm.AddTo(m) 39 | Fingerprint.AddTo(m) 40 | } 41 | }) 42 | } 43 | 44 | func TestMessage_Apply(t *testing.T) { 45 | var ( 46 | integrity = NewShortTermIntegrity("password") 47 | decoded = new(Message) 48 | ) 49 | m, err := Build(BindingRequest, TransactionIDSetter, 50 | NewUsername("username"), 51 | NewNonce("nonce"), 52 | NewRealm("example.org"), 53 | integrity, 54 | Fingerprint, 55 | ) 56 | if err != nil { 57 | t.Fatal("failed to build:", err) 58 | } 59 | if m.Check(Fingerprint, integrity); err != nil { 60 | t.Fatal(err) 61 | } 62 | if _, err := decoded.Write(m.Raw); err != nil { 63 | t.Fatal(err) 64 | } 65 | if !decoded.Equal(m) { 66 | t.Error("not equal") 67 | } 68 | if err := integrity.Check(decoded); err != nil { 69 | t.Fatal(err) 70 | } 71 | } 72 | 73 | type errReturner struct { 74 | Err error 75 | } 76 | 77 | func (e errReturner) AddTo(m *Message) error { 78 | return e.Err 79 | } 80 | 81 | func (e errReturner) Check(m *Message) error { 82 | return e.Err 83 | } 84 | 85 | func (e errReturner) GetFrom(m *Message) error { 86 | return e.Err 87 | } 88 | 89 | func TestHelpersErrorHandling(t *testing.T) { 90 | m := New() 91 | e := errReturner{Err: errors.New("tError")} 92 | if err := m.Build(e); err != e.Err { 93 | t.Error(err, "!=", e.Err) 94 | } 95 | if err := m.Check(e); err != e.Err { 96 | t.Error(err, "!=", e.Err) 97 | } 98 | if err := m.Parse(e); err != e.Err { 99 | t.Error(err, "!=", e.Err) 100 | } 101 | t.Run("MustBuild", func(t *testing.T) { 102 | t.Run("Positive", func(t *testing.T) { 103 | MustBuild(NewTransactionIDSetter(TransactionID{})) 104 | }) 105 | defer func() { 106 | if p := recover(); p != e.Err { 107 | t.Errorf("%s != %s", 108 | p, e.Err, 109 | ) 110 | } 111 | }() 112 | MustBuild(e) 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /stun/integrity.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/md5" // #nosec 6 | "crypto/sha1" 7 | "errors" 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | // separator for credentials. 13 | const credentialsSep = ":" 14 | 15 | // NewLongTermIntegrity returns new MessageIntegrity with key for long-term 16 | // credentials. Password, username, and realm must be SASL-prepared. 17 | func NewLongTermIntegrity(username, realm, password string) MessageIntegrity { 18 | k := strings.Join( 19 | []string{ 20 | username, 21 | realm, 22 | password, 23 | }, 24 | credentialsSep, 25 | ) 26 | // #nosec 27 | h := md5.New() 28 | fmt.Fprint(h, k) 29 | return MessageIntegrity(h.Sum(nil)) 30 | } 31 | 32 | // NewShortTermIntegrity returns new MessageIntegrity with key for short-term 33 | // credentials. Password must be SASL-prepared. 34 | func NewShortTermIntegrity(password string) MessageIntegrity { 35 | return MessageIntegrity(password) 36 | } 37 | 38 | // MessageIntegrity represents MESSAGE-INTEGRITY attribute. AddTo and GetFrom 39 | // methods will allocate memory for cryptographic functions. Zero-allocation 40 | // version of MessageIntegrity is not implemented. Implementation and changes 41 | // to it is subject to security review. 42 | // 43 | // https://tools.ietf.org/html/rfc5389#section-15.4 44 | type MessageIntegrity []byte 45 | 46 | // ErrFingerprintBeforeIntegrity means that FINGEPRINT attribute is already in 47 | // message, so MESSAGE-INTEGRITY attribute cannot be added. 48 | var ErrFingerprintBeforeIntegrity = errors.New( 49 | "FINGERPRINT before MESSAGE-INTEGRITY attribute", 50 | ) 51 | 52 | func (i MessageIntegrity) String() string { 53 | return fmt.Sprintf("KEY: 0x%x", []byte(i)) 54 | } 55 | 56 | const messageIntegritySize = 20 57 | 58 | // AddTo adds MESSAGE-INTEGRITY attribute to message. Be advised, CPU 59 | // and allocations costly, can be cause of DOS. 60 | func (i MessageIntegrity) AddTo(m *Message) error { 61 | for _, a := range m.Attributes { 62 | // Message should not contain FINGERPRINT attribute 63 | // before MESSAGE-INTEGRITY. 64 | if a.Type == AttrFingerprint { 65 | return ErrFingerprintBeforeIntegrity 66 | } 67 | } 68 | // The text used as input to HMAC is the STUN message, 69 | // including the header, up to and including the attribute preceding the 70 | // MESSAGE-INTEGRITY attribute. 71 | length := m.Length 72 | // Adjusting m.Length to contain MESSAGE-INTEGRITY TLV. 73 | m.Length += messageIntegritySize + attributeHeaderSize 74 | m.WriteLength() // writing length to m.Raw 75 | v := newHMAC(i, m.Raw) // calculating HMAC for adjusted m.Raw 76 | m.Length = length // changing m.Length back 77 | m.Add(AttrMessageIntegrity, v) 78 | return nil 79 | } 80 | 81 | // IntegrityErr occurs when computed HMAC differs from expected. 82 | type IntegrityErr struct { 83 | Expected []byte 84 | Actual []byte 85 | } 86 | 87 | func (i *IntegrityErr) Error() string { 88 | return fmt.Sprintf( 89 | "Integrity check failed: 0x%x (expected) !- 0x%x (actual)", 90 | i.Expected, i.Actual, 91 | ) 92 | } 93 | 94 | func newHMAC(key, message []byte) []byte { 95 | mac := hmac.New(sha1.New, key) 96 | writeOrPanic(mac, message) 97 | return mac.Sum(nil) 98 | } 99 | 100 | // Check checks MESSAGE-INTEGRITY attribute. Be advised, CPU and allocations 101 | // costly, can be cause of DOS. 102 | func (i MessageIntegrity) Check(m *Message) error { 103 | v, err := m.Get(AttrMessageIntegrity) 104 | if err != nil { 105 | return err 106 | } 107 | 108 | // Adjusting length in header to match m.Raw that was 109 | // used when computing HMAC. 110 | var ( 111 | length = m.Length 112 | afterIntegrity = false 113 | sizeReduced int 114 | ) 115 | for _, a := range m.Attributes { 116 | if afterIntegrity { 117 | sizeReduced += nearestPaddedValueLength(int(a.Length)) 118 | sizeReduced += attributeHeaderSize 119 | } 120 | if a.Type == AttrMessageIntegrity { 121 | afterIntegrity = true 122 | } 123 | } 124 | m.Length -= uint32(sizeReduced) 125 | m.WriteLength() 126 | // startOfHMAC should be first byte of integrity attribute. 127 | startOfHMAC := messageHeaderSize + m.Length - (attributeHeaderSize + messageIntegritySize) 128 | b := m.Raw[:startOfHMAC] // data before integrity attribute 129 | expected := newHMAC(i, b) 130 | m.Length = length 131 | m.WriteLength() // writing length back 132 | if !hmac.Equal(v, expected) { 133 | return &IntegrityErr{ 134 | Expected: expected, 135 | Actual: v, 136 | } 137 | } 138 | return nil 139 | } 140 | -------------------------------------------------------------------------------- /stun/integrity_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestMessageIntegrity_AddTo_Simple(t *testing.T) { 11 | i := NewLongTermIntegrity("user", "realm", "pass") 12 | expected, err := hex.DecodeString("8493fbc53ba582fb4c044c456bdc40eb") 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | if !bytes.Equal(expected, i) { 17 | t.Error(&IntegrityErr{ 18 | Expected: expected, 19 | Actual: i, 20 | }) 21 | } 22 | t.Run("Check", func(t *testing.T) { 23 | m := new(Message) 24 | m.WriteHeader() 25 | if err := i.AddTo(m); err != nil { 26 | t.Error(err) 27 | } 28 | NewSoftware("software").AddTo(m) 29 | m.WriteHeader() 30 | dM := new(Message) 31 | dM.Raw = m.Raw 32 | if err := dM.Decode(); err != nil { 33 | t.Error(err) 34 | } 35 | if err := i.Check(dM); err != nil { 36 | t.Error(err) 37 | } 38 | dM.Raw[24] += 12 // HMAC now invalid 39 | if err, ok := i.Check(dM).(*IntegrityErr); !ok { 40 | t.Error(err, "should be *IntegrityErr") 41 | } 42 | }) 43 | } 44 | 45 | func TestMessageIntegrityWithFingerprint(t *testing.T) { 46 | m := new(Message) 47 | m.TransactionID = TransactionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} 48 | m.WriteHeader() 49 | NewSoftware("software").AddTo(m) 50 | i := NewShortTermIntegrity("pwd") 51 | if i.String() != "KEY: 0x707764" { 52 | t.Error("bad string", i) 53 | } 54 | if err := i.Check(m); err == nil { 55 | t.Error("should error") 56 | } 57 | if err := i.AddTo(m); err != nil { 58 | t.Fatal(err) 59 | } 60 | if err := Fingerprint.AddTo(m); err != nil { 61 | t.Fatal(err) 62 | } 63 | if err := i.Check(m); err != nil { 64 | t.Fatal(err) 65 | } 66 | m.Raw[24] = 33 67 | errStr := fmt.Sprintf("Integrity check failed: 0x%s (expected) !- 0x%s (actual)", 68 | "19985afb819c098acfe1c2771881227f14c70eaf", 69 | "ef9da0e0caf0b0e4ff321e7b56f1e114c802cb7e", 70 | ) 71 | if err := i.Check(m); err.Error() != errStr { 72 | t.Fatal(err, "!=", errStr) 73 | } 74 | } 75 | 76 | func TestMessageIntegrity(t *testing.T) { 77 | m := new(Message) 78 | //NewSoftware("software") 79 | i := NewShortTermIntegrity("password") 80 | m.WriteHeader() 81 | if err := i.AddTo(m); err != nil { 82 | t.Error(err) 83 | } 84 | _, err := m.Get(AttrMessageIntegrity) 85 | if err != nil { 86 | t.Error(err) 87 | } 88 | } 89 | 90 | func TestMessageIntegrityBeforeFingerprint(t *testing.T) { 91 | m := new(Message) 92 | //NewSoftware("software") 93 | m.WriteHeader() 94 | Fingerprint.AddTo(m) 95 | i := NewShortTermIntegrity("password") 96 | if err := i.AddTo(m); err == nil { 97 | t.Error("should error") 98 | } 99 | } 100 | 101 | func BenchmarkMessageIntegrity_AddTo(b *testing.B) { 102 | m := new(Message) 103 | integrity := NewShortTermIntegrity("password") 104 | m.WriteHeader() 105 | b.ReportAllocs() 106 | b.SetBytes(int64(len(m.Raw))) 107 | for i := 0; i < b.N; i++ { 108 | m.WriteHeader() 109 | if err := integrity.AddTo(m); err != nil { 110 | b.Error(err) 111 | } 112 | m.Reset() 113 | } 114 | } 115 | func BenchmarkMessageIntegrity_Check(b *testing.B) { 116 | m := new(Message) 117 | NewSoftware("software").AddTo(m) 118 | integrity := NewShortTermIntegrity("password") 119 | b.ReportAllocs() 120 | m.WriteHeader() 121 | b.SetBytes(int64(len(m.Raw))) 122 | if err := integrity.AddTo(m); err != nil { 123 | b.Error(err) 124 | } 125 | m.WriteLength() 126 | for i := 0; i < b.N; i++ { 127 | if err := integrity.Check(m); err != nil { 128 | b.Fatal(err) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /stun/stun.go: -------------------------------------------------------------------------------- 1 | // Package stun implements Session Traversal Utilities for NAT (STUN) RFC 5389. 2 | // 3 | // The stun package is intended to use by package that implements extension 4 | // to STUN (e.g. TURN) or client/server applications. 5 | // 6 | // Most methods are designed to be zero allocations. If it is not enough, 7 | // low-level methods are available. On other hand, there are helpers that 8 | // reduce code repeat. 9 | // 10 | // See examples for Message for basic usage, or https://github.com/ernado/turn 11 | // package for example of stun extension implementation. 12 | package stun 13 | 14 | import ( 15 | "encoding/binary" 16 | "io" 17 | ) 18 | 19 | // bin is shorthand to binary.BigEndian. 20 | var bin = binary.BigEndian 21 | 22 | func readFullOrPanic(r io.Reader, v []byte) int { 23 | n, err := io.ReadFull(r, v) 24 | if err != nil { 25 | panic(err) 26 | } 27 | return n 28 | } 29 | 30 | func writeOrPanic(w io.Writer, v []byte) int { 31 | n, err := w.Write(v) 32 | if err != nil { 33 | panic(err) 34 | } 35 | return n 36 | } 37 | 38 | // IANA assigned ports for "stun" protocol. 39 | const ( 40 | DefaultPort = 3478 41 | DefaultTLSPort = 5349 42 | ) 43 | 44 | type transactionIDSetter struct{} 45 | 46 | func (transactionIDSetter) AddTo(m *Message) error { 47 | return m.NewTransactionID() 48 | } 49 | 50 | // TransactionIDSetter is Setter for m.TransactionIDSetter. 51 | var TransactionIDSetter Setter = transactionIDSetter{} 52 | -------------------------------------------------------------------------------- /stun/stun_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | type errorReader struct{} 9 | 10 | func (errorReader) Read([]byte) (int, error) { 11 | return 0, errors.New("failed to read") 12 | } 13 | 14 | func TestReadFullHelper(t *testing.T) { 15 | defer func() { 16 | if r := recover(); r == nil { 17 | t.Error("should panic") 18 | } 19 | }() 20 | readFullOrPanic(errorReader{}, make([]byte, 1)) 21 | } 22 | 23 | type errorWriter struct{} 24 | 25 | func (errorWriter) Write([]byte) (int, error) { 26 | return 0, errors.New("failed to write") 27 | } 28 | 29 | func TestWriteHelper(t *testing.T) { 30 | defer func() { 31 | if r := recover(); r == nil { 32 | t.Error("should panic") 33 | } 34 | }() 35 | writeOrPanic(errorWriter{}, make([]byte, 1)) 36 | } 37 | -------------------------------------------------------------------------------- /stun/testdata/README.md: -------------------------------------------------------------------------------- 1 | # Test data 2 | 3 | testdata directory contains data that is used as input for tests. 4 | Data was gathered from real world implementations of STUN in 5 | Firefox and Chrome browsers. -------------------------------------------------------------------------------- /stun/testdata/ex1_chrome.stun: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/stun/testdata/ex1_chrome.stun -------------------------------------------------------------------------------- /stun/testdata/frombrowsers.csv: -------------------------------------------------------------------------------- 1 | ip,message,crc,browser,version,system 2 | 213.87.135.35:5127,AAEAACESpEJaU3lNekUycUIveEc=,14955568470663943731,Chrome,55.0.2883.91,Android 5.1.1 3 | 213.87.135.35:35359,AAEACCESpEL/qLJHuDKc5PsGghOAKAAEqgN+GQ==,10276095995276579762,Firefox,50.0,Mobile 4 | 185.79.100.8:8337,AAEAACESpEIrY1h5L3p1UytKQkE=,12881067838541718227,Chrome,55.0.2883.91,Android 5.1.1 5 | 185.79.100.8:8422,AAEAACESpEI1Y1NTdm9yQ3NOMng=,11436996053979164376,Chrome,55.0.2883.91,Android 5.1.1 6 | 185.79.100.8:7986,AAEACCESpEJb5g0r4yyFhiAECkaAKAAEcmtDIA==,4014551455069599512,Firefox,50.0,Mobile 7 | 185.79.100.22:13841,AAEAACESpEJzVFJiOEFpdzZkUDM=,17727708163432284316,Chrome,55.0.2883.91,Android 5.1.1 8 | 213.87.161.132:10323,AAEAACESpEJUdGs3ckUvQkVwQTg=,4195490818417829438,Chrome,55.0.2883.91,Android 5.1.1 9 | 213.87.161.132:49166,AAEAACESpEJBT2J3VXZmVEZGa24=,210244277659580892,Chrome,55.0.2883.91,Android 5.1.1 10 | 91.208.134.1:59285,AAEACCESpEL8J+y5nwi+zNxNyuGAKAAEriExcw==,17182568428950801870,Firefox,51.0,Ubuntu 11 | 91.208.134.1:35576,AAEACCESpELD+wYQw3maWaeQD+yAKAAEmQBHaw==,1298525217870091633,Firefox,51.0,Ubuntu 12 | 51.15.40.89:39281,AAEAGCESpEJraER2VEl6aWJ1ZGiALwARaHR0cHM6Ly9jeWRldi5ydS8AAAA=,2038078914284839991,Chrome,55.0.2883.87,Linux x86_64 13 | 51.15.40.89:52341,AAEAGCESpEJHbHEveWt0dzB4cHOALwARaHR0cHM6Ly9jeWRldi5ydS8AAAA=,15407862181367978708,Chrome,55.0.2883.87,Linux x86_64 14 | 51.15.40.89:53197,AAEAGCESpEJQVkdXVHBjbjhBWlWALwARaHR0cHM6Ly9jeWRldi5ydS8AAAA=,17607978280141252653,Chrome,55.0.2883.87,Linux x86_64 15 | 51.15.40.89:58269,AAEACCESpEIB2FbktZDEiHN0z0iAKAAEK2VeXA==,3485408176062718742,Firefox,51.0,Ubuntu 16 | -------------------------------------------------------------------------------- /stun/textattrs.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | // NewUsername returns Username with provided value. 4 | func NewUsername(username string) Username { 5 | return Username(username) 6 | } 7 | 8 | // Username represents USERNAME attribute. 9 | // 10 | // https://tools.ietf.org/html/rfc5389#section-15.3 11 | type Username []byte 12 | 13 | func (u Username) String() string { 14 | return string(u) 15 | } 16 | 17 | const maxUsernameB = 513 18 | 19 | // AddTo adds USERNAME attribute to message. 20 | func (u Username) AddTo(m *Message) error { 21 | return TextAttribute(u).AddToAs(m, AttrUsername, maxUsernameB) 22 | } 23 | 24 | // GetFrom gets USERNAME from message. 25 | func (u *Username) GetFrom(m *Message) error { 26 | return (*TextAttribute)(u).GetFromAs(m, AttrUsername) 27 | } 28 | 29 | // NewRealm returns Realm with provided value. 30 | // Must be SASL-prepared. 31 | func NewRealm(realm string) Realm { 32 | return Realm(realm) 33 | } 34 | 35 | // Realm represents REALM attribute. 36 | // 37 | // https://tools.ietf.org/html/rfc5389#section-15.7 38 | type Realm []byte 39 | 40 | func (n Realm) String() string { 41 | return string(n) 42 | } 43 | 44 | const maxRealmB = 763 45 | 46 | // AddTo adds NONCE to message. 47 | func (n Realm) AddTo(m *Message) error { 48 | return TextAttribute(n).AddToAs(m, AttrRealm, maxRealmB) 49 | } 50 | 51 | // GetFrom gets REALM from message. 52 | func (n *Realm) GetFrom(m *Message) error { 53 | return (*TextAttribute)(n).GetFromAs(m, AttrRealm) 54 | } 55 | 56 | const softwareRawMaxB = 763 57 | 58 | // Software is SOFTWARE attribute. 59 | // 60 | // https://tools.ietf.org/html/rfc5389#section-15.10 61 | type Software []byte 62 | 63 | func (s Software) String() string { 64 | return string(s) 65 | } 66 | 67 | // NewSoftware returns *Software from string. 68 | func NewSoftware(software string) Software { 69 | return Software(software) 70 | } 71 | 72 | // AddTo adds Software attribute to m. 73 | func (s Software) AddTo(m *Message) error { 74 | return TextAttribute(s).AddToAs(m, AttrSoftware, softwareRawMaxB) 75 | } 76 | 77 | // GetFrom decodes Software from m. 78 | func (s *Software) GetFrom(m *Message) error { 79 | return (*TextAttribute)(s).GetFromAs(m, AttrSoftware) 80 | } 81 | 82 | // Nonce represents NONCE attribute. 83 | // 84 | // https://tools.ietf.org/html/rfc5389#section-15.8 85 | type Nonce []byte 86 | 87 | // NewNonce returns new nonce from string. 88 | func NewNonce(nonce string) Nonce { 89 | return Nonce(nonce) 90 | } 91 | 92 | func (n Nonce) String() string { 93 | return string(n) 94 | } 95 | 96 | const maxNonceB = 763 97 | 98 | // AddTo adds NONCE to message. 99 | func (n Nonce) AddTo(m *Message) error { 100 | return TextAttribute(n).AddToAs(m, AttrNonce, maxNonceB) 101 | } 102 | 103 | // GetFrom gets NONCE from message. 104 | func (n *Nonce) GetFrom(m *Message) error { 105 | return (*TextAttribute)(n).GetFromAs(m, AttrNonce) 106 | } 107 | 108 | // TextAttribute is helper for adding and getting text attributes. 109 | type TextAttribute []byte 110 | 111 | // AddToAs adds attribute with type t to m, checking maximum length. If maxLen 112 | // is less than 0, no check is performed. 113 | func (v TextAttribute) AddToAs(m *Message, t AttrType, maxLen int) error { 114 | if maxLen > 0 && len(v) > maxLen { 115 | return &AttrOverflowErr{ 116 | Max: maxLen, 117 | Got: len(v), 118 | Type: t, 119 | } 120 | } 121 | m.Add(t, v) 122 | return nil 123 | } 124 | 125 | // GetFromAs gets t attribute from m and appends its value to reseted v. 126 | func (v *TextAttribute) GetFromAs(m *Message, t AttrType) error { 127 | a, err := m.Get(t) 128 | if err != nil { 129 | return err 130 | } 131 | *v = append((*v)[:0], a...) 132 | return nil 133 | } 134 | 135 | func (v *TextAttribute) String() string { 136 | return string(*v) 137 | } 138 | -------------------------------------------------------------------------------- /stun/uattrs.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import "errors" 4 | 5 | // UnknownAttributes represents UNKNOWN-ATTRIBUTES attribute. 6 | // 7 | // https://tools.ietf.org/html/rfc5389#section-15.9 8 | type UnknownAttributes []AttrType 9 | 10 | func (a UnknownAttributes) String() string { 11 | s := "" 12 | if len(a) == 0 { 13 | return "" 14 | } 15 | last := len(a) - 1 16 | for i, t := range a { 17 | s += t.String() 18 | if i != last { 19 | s += ", " 20 | } 21 | } 22 | return s 23 | } 24 | 25 | // type size is 16 bit. 26 | const attrTypeSize = 4 27 | 28 | // AddTo adds UNKNOWN-ATTRIBUTES attribute to message. 29 | func (a UnknownAttributes) AddTo(m *Message) error { 30 | v := make([]byte, 0, attrTypeSize*20) // 20 should be enough 31 | // If len(a.Types) > 20, there will be allocations. 32 | for i, t := range a { 33 | v = append(v, 0, 0, 0, 0) // 4 times by 0 (16 bits) 34 | first := attrTypeSize * i 35 | last := first + attrTypeSize 36 | bin.PutUint16(v[first:last], t.Value()) 37 | } 38 | m.Add(AttrUnknownAttributes, v) 39 | return nil 40 | } 41 | 42 | // ErrBadUnknownAttrsSize means that UNKNOWN-ATTRIBUTES attribute value 43 | // has invalid length. 44 | var ErrBadUnknownAttrsSize = errors.New("bad UNKNOWN-ATTRIBUTES size") 45 | 46 | // GetFrom parses UNKNOWN-ATTRIBUTES from message. 47 | func (a *UnknownAttributes) GetFrom(m *Message) error { 48 | v, err := m.Get(AttrUnknownAttributes) 49 | if err != nil { 50 | return err 51 | } 52 | if len(v)%attrTypeSize != 0 { 53 | return ErrBadUnknownAttrsSize 54 | } 55 | *a = (*a)[:0] 56 | first := 0 57 | for first < len(v) { 58 | last := first + attrTypeSize 59 | *a = append(*a, 60 | AttrType(bin.Uint16(v[first:last])), 61 | ) 62 | first = last 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /stun/uattrs_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnknownAttributes(t *testing.T) { 8 | m := new(Message) 9 | a := &UnknownAttributes{ 10 | AttrDontFragment, 11 | AttrChannelNumber, 12 | } 13 | if a.String() != "DONT-FRAGMENT, CHANNEL-NUMBER" { 14 | t.Error("bad String:", a) 15 | } 16 | if (UnknownAttributes{}).String() != "" { 17 | t.Error("bad blank stirng") 18 | } 19 | if err := a.AddTo(m); err != nil { 20 | t.Error(err) 21 | } 22 | t.Run("GetFrom", func(t *testing.T) { 23 | attrs := make(UnknownAttributes, 10) 24 | if err := attrs.GetFrom(m); err != nil { 25 | t.Error(err) 26 | } 27 | for i, at := range *a { 28 | if at != attrs[i] { 29 | t.Error("expected", at, "!=", attrs[i]) 30 | } 31 | } 32 | mBlank := new(Message) 33 | if err := attrs.GetFrom(mBlank); err == nil { 34 | t.Error("should error") 35 | } 36 | mBlank.Add(AttrUnknownAttributes, []byte{1, 2, 3}) 37 | if err := attrs.GetFrom(mBlank); err == nil { 38 | t.Error("should error") 39 | } 40 | }) 41 | } 42 | 43 | func BenchmarkUnknownAttributes(b *testing.B) { 44 | m := new(Message) 45 | a := UnknownAttributes{ 46 | AttrDontFragment, 47 | AttrChannelNumber, 48 | AttrRealm, 49 | AttrMessageIntegrity, 50 | } 51 | b.Run("AddTo", func(b *testing.B) { 52 | b.ReportAllocs() 53 | for i := 0; i < b.N; i++ { 54 | if err := a.AddTo(m); err != nil { 55 | b.Fatal(err) 56 | } 57 | m.Reset() 58 | } 59 | }) 60 | b.Run("GetFrom", func(b *testing.B) { 61 | b.ReportAllocs() 62 | if err := a.AddTo(m); err != nil { 63 | b.Fatal(err) 64 | } 65 | attrs := make(UnknownAttributes, 0, 10) 66 | for i := 0; i < b.N; i++ { 67 | if err := attrs.GetFrom(m); err != nil { 68 | b.Fatal(err) 69 | } 70 | attrs = attrs[:0] 71 | } 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /stun/xor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package stun 6 | 7 | import ( 8 | "runtime" 9 | "unsafe" 10 | ) 11 | 12 | // #nosec 13 | const wordSize = int(unsafe.Sizeof(uintptr(0))) 14 | 15 | var supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" 16 | 17 | // fastXORBytes xors in bulk. It only works on architectures that 18 | // support unaligned read/writes. 19 | // 20 | // #nosec 21 | func fastXORBytes(dst, a, b []byte) int { 22 | n := len(a) 23 | if len(b) < n { 24 | n = len(b) 25 | } 26 | 27 | w := n / wordSize 28 | if w > 0 { 29 | dw := *(*[]uintptr)(unsafe.Pointer(&dst)) 30 | aw := *(*[]uintptr)(unsafe.Pointer(&a)) 31 | bw := *(*[]uintptr)(unsafe.Pointer(&b)) 32 | for i := 0; i < w; i++ { 33 | dw[i] = aw[i] ^ bw[i] 34 | } 35 | } 36 | 37 | for i := n - n%wordSize; i < n; i++ { 38 | dst[i] = a[i] ^ b[i] 39 | } 40 | 41 | return n 42 | } 43 | 44 | func safeXORBytes(dst, a, b []byte) int { 45 | n := len(a) 46 | if len(b) < n { 47 | n = len(b) 48 | } 49 | for i := 0; i < n; i++ { 50 | dst[i] = a[i] ^ b[i] 51 | } 52 | return n 53 | } 54 | 55 | // xorBytes xors the bytes in a and b. The destination is assumed to have enough 56 | // space. Returns the number of bytes xor'd. 57 | func xorBytes(dst, a, b []byte) int { 58 | if supportsUnaligned { 59 | return fastXORBytes(dst, a, b) 60 | } 61 | return safeXORBytes(dst, a, b) 62 | } 63 | -------------------------------------------------------------------------------- /stun/xor_test.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | func TestXORSafe(t *testing.T) { 9 | dst := make([]byte, 8) 10 | a := []byte{1, 2, 3, 4, 5, 6, 7, 8} 11 | b := []byte{8, 7, 7, 6, 6, 3, 4, 1} 12 | safeXORBytes(dst, a, b) 13 | safeXORBytes(dst, dst, a) 14 | for i, v := range dst { 15 | if b[i] != v { 16 | t.Error(b[i], "!=", v) 17 | } 18 | } 19 | } 20 | 21 | func TestXORSafeBSmaller(t *testing.T) { 22 | dst := make([]byte, 5) 23 | a := []byte{1, 2, 3, 4, 5, 6, 7, 8} 24 | b := []byte{8, 7, 7, 6, 6} 25 | safeXORBytes(dst, a, b) 26 | safeXORBytes(dst, dst, a) 27 | for i, v := range dst { 28 | if b[i] != v { 29 | t.Error(b[i], "!=", v) 30 | } 31 | } 32 | } 33 | 34 | func TestXORFast(t *testing.T) { 35 | if !supportsUnaligned { 36 | t.Skip("No support for unaligned operations.") 37 | } 38 | dst := make([]byte, 8) 39 | a := []byte{1, 2, 3, 4, 5, 6, 7, 8} 40 | b := []byte{8, 7, 7, 6, 6, 3, 4, 1} 41 | xorBytes(dst, a, b) 42 | xorBytes(dst, dst, a) 43 | for i, v := range dst { 44 | if b[i] != v { 45 | t.Error(b[i], "!=", v) 46 | } 47 | } 48 | } 49 | 50 | func TestXORFastBSmaller(t *testing.T) { 51 | if !supportsUnaligned { 52 | t.Skip("No support for unaligned operations.") 53 | } 54 | dst := make([]byte, 5) 55 | a := []byte{1, 2, 3, 4, 5, 6, 7, 8} 56 | b := []byte{8, 7, 7, 6, 6} 57 | xorBytes(dst, a, b) 58 | xorBytes(dst, dst, a) 59 | for i, v := range dst { 60 | if b[i] != v { 61 | t.Error(b[i], "!=", v) 62 | } 63 | } 64 | } 65 | 66 | func TestXORFallback(t *testing.T) { 67 | if !supportsUnaligned { 68 | t.Skip("No support for unaligned operations.") 69 | } 70 | defer func() { 71 | supportsUnaligned = true 72 | }() 73 | supportsUnaligned = false 74 | dst := make([]byte, 5) 75 | a := []byte{1, 2, 3, 4, 5, 6, 7, 8} 76 | b := []byte{8, 7, 7, 6, 6} 77 | xorBytes(dst, a, b) 78 | xorBytes(dst, dst, a) 79 | for i, v := range dst { 80 | if b[i] != v { 81 | t.Error(b[i], "!=", v) 82 | } 83 | } 84 | } 85 | 86 | func BenchmarkXOR(b *testing.B) { 87 | rand.Seed(666) 88 | a := make([]byte, 1024) 89 | c := make([]byte, 1024) 90 | rand.Read(a) 91 | rand.Read(c) 92 | b.SetBytes(1024) 93 | b.RunParallel(func(pb *testing.PB) { 94 | dst := make([]byte, len(a)) 95 | for pb.Next() { 96 | xorBytes(dst, a, c) 97 | } 98 | }) 99 | } 100 | 101 | func BenchmarkXORSafe(b *testing.B) { 102 | rand.Seed(666) 103 | a := make([]byte, 1024) 104 | c := make([]byte, 1024) 105 | rand.Read(a) 106 | rand.Read(c) 107 | b.SetBytes(1024) 108 | b.RunParallel(func(pb *testing.PB) { 109 | dst := make([]byte, len(a)) 110 | for pb.Next() { 111 | safeXORBytes(dst, a, c) 112 | } 113 | }) 114 | } 115 | 116 | func BenchmarkXORFast(b *testing.B) { 117 | if !supportsUnaligned { 118 | b.Skip("No support for unaligned operations.") 119 | } 120 | rand.Seed(666) 121 | a := make([]byte, 1024) 122 | c := make([]byte, 1024) 123 | rand.Read(a) 124 | rand.Read(c) 125 | b.SetBytes(1024) 126 | b.RunParallel(func(pb *testing.PB) { 127 | dst := make([]byte, len(a)) 128 | for pb.Next() { 129 | fastXORBytes(dst, a, c) 130 | } 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /stun/xoraddr.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "net" 8 | "strconv" 9 | ) 10 | 11 | const ( 12 | familyIPv4 uint16 = 0x01 13 | familyIPv6 uint16 = 0x02 14 | ) 15 | 16 | // XORMappedAddress implements XOR-MAPPED-ADDRESS attribute. 17 | // 18 | // https://tools.ietf.org/html/rfc5389#section-15.2 19 | type XORMappedAddress struct { 20 | IP net.IP 21 | Port int 22 | } 23 | 24 | func (a XORMappedAddress) String() string { 25 | return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port)) 26 | } 27 | 28 | // isIPv4 returns true if ip with len of net.IPv6Len seems to be ipv4. 29 | func isIPv4(ip net.IP) bool { 30 | // Optimized for performance. Copied from net.IP.To4. 31 | return isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff 32 | } 33 | 34 | // Is p all zeros? 35 | func isZeros(p net.IP) bool { 36 | for i := 0; i < len(p); i++ { 37 | if p[i] != 0 { 38 | return false 39 | } 40 | } 41 | return true 42 | } 43 | 44 | // ErrBadIPLength means that len(IP) is not net.{IPv6len,IPv4len}. 45 | var ErrBadIPLength = errors.New("invalid length of IP value") 46 | 47 | // AddToAs adds XOR-MAPPED-ADDRESS value to m as t attribute. 48 | func (a XORMappedAddress) AddToAs(m *Message, t AttrType) error { 49 | var ( 50 | family = familyIPv4 51 | ip = a.IP 52 | ) 53 | if len(a.IP) == net.IPv6len { 54 | if isIPv4(ip) { 55 | ip = ip[12:16] // like in ip.To4() 56 | } else { 57 | family = familyIPv6 58 | } 59 | } else if len(ip) != net.IPv4len { 60 | return ErrBadIPLength 61 | } 62 | value := make([]byte, 32+128) 63 | value[0] = 0 // first 8 bits are zeroes 64 | xorValue := make([]byte, net.IPv6len) 65 | copy(xorValue[4:], m.TransactionID[:]) 66 | bin.PutUint32(xorValue[0:4], magicCookie) 67 | bin.PutUint16(value[0:2], family) 68 | bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16)) 69 | xorBytes(value[4:4+len(ip)], ip, xorValue) 70 | m.Add(t, value[:4+len(ip)]) 71 | return nil 72 | } 73 | 74 | // AddTo adds XOR-MAPPED-ADDRESS to m. Can return ErrBadIPLength 75 | // if len(a.IP) is invalid. 76 | func (a XORMappedAddress) AddTo(m *Message) error { 77 | return a.AddToAs(m, AttrXORMappedAddress) 78 | } 79 | 80 | // GetFromAs decodes XOR-MAPPED-ADDRESS attribute value in message 81 | // getting it as for t type. 82 | func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error { 83 | v, err := m.Get(t) 84 | if err != nil { 85 | return err 86 | } 87 | family := bin.Uint16(v[0:2]) 88 | if family != familyIPv6 && family != familyIPv4 { 89 | return newDecodeErr("xor-mapped address", "family", 90 | fmt.Sprintf("bad value %d", family), 91 | ) 92 | } 93 | ipLen := net.IPv4len 94 | if family == familyIPv6 { 95 | ipLen = net.IPv6len 96 | } 97 | // Ensuring len(a.IP) == ipLen and reusing a.IP. 98 | if len(a.IP) < ipLen { 99 | a.IP = a.IP[:cap(a.IP)] 100 | for len(a.IP) < ipLen { 101 | a.IP = append(a.IP, 0) 102 | } 103 | } 104 | a.IP = a.IP[:ipLen] 105 | for i := range a.IP { 106 | a.IP[i] = 0 107 | } 108 | if len(v) <= 4 { 109 | return io.ErrUnexpectedEOF 110 | } 111 | if len(v[4:]) > len(a.IP) { 112 | return &AttrOverflowErr{ 113 | Got: len(v[4:]), 114 | Type: AttrXORMappedAddress, 115 | Max: len(a.IP), 116 | } 117 | } 118 | a.Port = int(bin.Uint16(v[2:4])) ^ (magicCookie >> 16) 119 | xorValue := make([]byte, 4+TransactionIDSize) 120 | bin.PutUint32(xorValue[0:4], magicCookie) 121 | copy(xorValue[4:], m.TransactionID[:]) 122 | xorBytes(a.IP, v[4:], xorValue) 123 | return nil 124 | } 125 | 126 | // GetFrom decodes XOR-MAPPED-ADDRESS attribute in message and returns 127 | // error if any. While decoding, a.IP is reused if possible and can be 128 | // rendered to invalid state (e.g. if a.IP was set to IPv6 and then 129 | // IPv4 value were decoded into it), be careful. 130 | // 131 | // Example: 132 | // 133 | // expectedIP := net.ParseIP("213.141.156.236") 134 | // expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes 135 | // expectedPort := 21254 136 | // addr := &XORMappedAddress{ 137 | // IP: expectedIP, 138 | // Port: expectedPort, 139 | // } 140 | // // addr were added to message that is decoded as newMessage 141 | // // ... 142 | // 143 | // addr.GetFrom(newMessage) 144 | // addr.IP.String() // 213.141.156.236, net.IPv4Len 145 | // expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4 146 | // // now we have len(expectedIP) = 16 and len(addr.IP) = 4. 147 | func (a *XORMappedAddress) GetFrom(m *Message) error { 148 | return a.GetFromAs(m, AttrXORMappedAddress) 149 | } 150 | -------------------------------------------------------------------------------- /turn/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | go: 6 | - 1.9.x 7 | - 1.10.x 8 | - master 9 | 10 | before_install: 11 | - go get github.com/mattn/goveralls 12 | - go get golang.org/x/tools/cmd/cover 13 | 14 | install: 15 | - go get -v -t . 16 | 17 | script: 18 | - $HOME/gopath/bin/goveralls -service=travis-ci -v -package . 19 | 20 | matrix: 21 | allow_failures: 22 | - go: 'master' 23 | fast_finish: true 24 | -------------------------------------------------------------------------------- /turn/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Aleksandr Razumov 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /turn/Makefile: -------------------------------------------------------------------------------- 1 | GO_VERSION=$(shell gobuild -v) 2 | GO := $(or $(GOROOT),/usr/lib/go)/bin/go 3 | PROCS := $(shell nproc) 4 | cores: 5 | @echo "cores: $(PROCS)" 6 | test: 7 | go test -v 8 | bench: 9 | go test -bench . 10 | bench-record: 11 | $(GO) test -bench . > "benchmarks/turn-go-$(GO_VERSION).txt" 12 | fuzz-prepare-setters: 13 | go-fuzz-build -func FuzzSetters -o turn-setters-fuzz.zip github.com/gortc/turn 14 | fuzz-setters: 15 | go-fuzz -bin=./turn-setters-fuzz.zip -workdir=examples/turn-setters 16 | fuzz-test: 17 | go test -tags gofuzz -run TestFuzz -v . 18 | lint: 19 | @echo "linting on $(PROCS) cores" 20 | @gometalinter \ 21 | -e "_test.go.+(gocyclo|errcheck|dupl)" \ 22 | -e "attributes\.go.+credentials,.+,LOW.+\(gas\)" \ 23 | -e "Message.+\(aligncheck\)" \ 24 | --enable="lll" --line-length=100 \ 25 | --enable="gofmt" \ 26 | --disable="gotype" \ 27 | --enable="goimports" \ 28 | --enable="misspell" \ 29 | --enable="unused" \ 30 | --deadline=300s \ 31 | -j $(PROCS) 32 | @echo "ok" 33 | escape: 34 | @echo "Not escapes, except autogenerated:" 35 | @go build -gcflags '-m -l' 2>&1 \ 36 | | grep -v "" \ 37 | | grep escapes 38 | format: 39 | goimports -w . 40 | docker-build: 41 | docker build -t gortc/turn . 42 | -------------------------------------------------------------------------------- /turn/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/gortc/turn.svg)](https://travis-ci.org/gortc/turn) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/amx4avytduhrs50e/branch/master?svg=true)](https://ci.appveyor.com/project/ernado/turn/branch/master) 3 | [![GoDoc](https://godoc.org/github.com/gortc/turn?status.svg)](http://godoc.org/github.com/gortc/turn) 4 | [![Coverage Status](https://coveralls.io/repos/github/gortc/turn/badge.svg?branch=master)](https://coveralls.io/github/gortc/turn?branch=master) 5 | [![Go Report](https://goreportcard.com/badge/github.com/gortc/turn)](http://goreportcard.com/report/gortc/turn) 6 | [![RFC 5766](https://img.shields.io/badge/RFC-5766-blue.svg)](https://tools.ietf.org/html/rfc5766) 7 | 8 | 9 | # turn 10 | 11 | Package turn implements RFC 5766 Traversal Using Relays around NAT. 12 | 13 | Work in progress. 14 | 15 | ``` 16 | goos: linux 17 | goarch: amd64 18 | pkg: github.com/gortc/turn 19 | PASS 20 | benchmark iter time/iter bytes alloc allocs 21 | --------- ---- --------- ----------- ------ 22 | BenchmarkChannelNumber/AddTo-12 100000000 12.50 ns/op 0 B/op 0 allocs/op 23 | BenchmarkChannelNumber/GetFrom-12 200000000 7.30 ns/op 0 B/op 0 allocs/op 24 | BenchmarkData/AddTo-12 100000000 18.40 ns/op 0 B/op 0 allocs/op 25 | BenchmarkData/AddToRaw-12 100000000 16.00 ns/op 0 B/op 0 allocs/op 26 | BenchmarkLifetime/AddTo-12 100000000 14.60 ns/op 0 B/op 0 allocs/op 27 | BenchmarkLifetime/GetFrom-12 200000000 7.28 ns/op 0 B/op 0 allocs/op 28 | ok github.com/gortc/turn 10.606s 29 | ``` -------------------------------------------------------------------------------- /turn/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | platform: x64 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | skip_tags: true 10 | 11 | clone_folder: c:\gopath\src\github.com\ernado\turn 12 | 13 | environment: 14 | GOPATH: c:\gopath 15 | GOVERSION: 1.8 16 | 17 | install: 18 | - go version 19 | - go get -v -t . 20 | 21 | build_script: 22 | - go test -v . 23 | -------------------------------------------------------------------------------- /turn/chann.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | // ChannelNumber represents CHANNEL-NUMBER attribute. 10 | // 11 | // The CHANNEL-NUMBER attribute contains the number of the channel. 12 | // 13 | // https://trac.tools.ietf.org/html/rfc5766#section-14.1 14 | type ChannelNumber int // encoded as uint16 15 | 16 | func (n ChannelNumber) String() string { return strconv.Itoa(int(n)) } 17 | 18 | // 16 bits of uint + 16 bits of RFFU = 0. 19 | const channelNumberSize = 4 20 | 21 | // AddTo adds CHANNEL-NUMBER to message. 22 | func (n ChannelNumber) AddTo(m *stun.Message) error { 23 | v := make([]byte, channelNumberSize) 24 | bin.PutUint16(v[:2], uint16(n)) 25 | // v[2:4] are zeroes (RFFU = 0) 26 | m.Add(stun.AttrChannelNumber, v) 27 | return nil 28 | } 29 | 30 | // GetFrom decodes CHANNEL-NUMBER from message. 31 | func (n *ChannelNumber) GetFrom(m *stun.Message) error { 32 | v, err := m.Get(stun.AttrChannelNumber) 33 | if err != nil { 34 | return err 35 | } 36 | if len(v) != channelNumberSize { 37 | return &BadAttrLength{ 38 | Attr: stun.AttrChannelNumber, 39 | Got: len(v), 40 | Expected: channelNumberSize, 41 | } 42 | } 43 | _ = v[channelNumberSize-1] // asserting length 44 | *n = ChannelNumber(bin.Uint16(v[:2])) 45 | // v[2:4] is RFFU and equals to 0. 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /turn/chann_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | func BenchmarkChannelNumber(b *testing.B) { 10 | b.Run("AddTo", func(b *testing.B) { 11 | b.ReportAllocs() 12 | m := new(stun.Message) 13 | for i := 0; i < b.N; i++ { 14 | n := ChannelNumber(12) 15 | if err := n.AddTo(m); err != nil { 16 | b.Fatal(err) 17 | } 18 | m.Reset() 19 | } 20 | }) 21 | b.Run("GetFrom", func(b *testing.B) { 22 | m := new(stun.Message) 23 | ChannelNumber(12).AddTo(m) 24 | for i := 0; i < b.N; i++ { 25 | var n ChannelNumber 26 | if err := n.GetFrom(m); err != nil { 27 | b.Fatal(err) 28 | } 29 | } 30 | }) 31 | } 32 | 33 | func TestChannelNumber(t *testing.T) { 34 | t.Run("String", func(t *testing.T) { 35 | n := ChannelNumber(112) 36 | if n.String() != "112" { 37 | t.Errorf("bad string %s, expedted 112", n) 38 | } 39 | }) 40 | t.Run("NoAlloc", func(t *testing.T) { 41 | m := &stun.Message{} 42 | if wasAllocs(func() { 43 | // Case with ChannelNumber on stack. 44 | n := ChannelNumber(6) 45 | n.AddTo(m) 46 | m.Reset() 47 | }) { 48 | t.Error("Unexpected allocations") 49 | } 50 | 51 | n := ChannelNumber(12) 52 | nP := &n 53 | if wasAllocs(func() { 54 | // On heap. 55 | nP.AddTo(m) 56 | m.Reset() 57 | }) { 58 | t.Error("Unexpected allocations") 59 | } 60 | }) 61 | t.Run("AddTo", func(t *testing.T) { 62 | m := new(stun.Message) 63 | n := ChannelNumber(6) 64 | if err := n.AddTo(m); err != nil { 65 | t.Error(err) 66 | } 67 | m.WriteHeader() 68 | t.Run("GetFrom", func(t *testing.T) { 69 | decoded := new(stun.Message) 70 | if _, err := decoded.Write(m.Raw); err != nil { 71 | t.Fatal("failed to decode message:", err) 72 | } 73 | var numDecoded ChannelNumber 74 | if err := numDecoded.GetFrom(decoded); err != nil { 75 | t.Fatal(err) 76 | } 77 | if numDecoded != n { 78 | t.Errorf("Decoded %d, expected %d", numDecoded, n) 79 | } 80 | if wasAllocs(func() { 81 | var num ChannelNumber 82 | num.GetFrom(decoded) 83 | }) { 84 | t.Error("Unexpected allocations") 85 | } 86 | t.Run("HandleErr", func(t *testing.T) { 87 | m := new(stun.Message) 88 | nHandle := new(ChannelNumber) 89 | if err := nHandle.GetFrom(m); err != stun.ErrAttributeNotFound { 90 | t.Errorf("%v should be not found", err) 91 | } 92 | m.Add(stun.AttrChannelNumber, []byte{1, 2, 3}) 93 | if err, ok := nHandle.GetFrom(m).(*BadAttrLength); !ok { 94 | t.Errorf("%v should be *BadAttrLength", err) 95 | } 96 | }) 97 | }) 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /turn/channeldata.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/nkbai/goice/stun" 9 | ) 10 | 11 | //MinChannelNumber minimum channel number 12 | const MinChannelNumber = 0x4000 13 | 14 | //MaxChannelNumber maximum channel number 15 | const MaxChannelNumber = 0x7fff 16 | 17 | //ChannelData represents channel data 18 | type ChannelData struct { 19 | ChannelNumber uint16 //must between 0x4000-0x7fff 20 | Data []byte //data to send, can be empty. 21 | } 22 | 23 | func (c *ChannelData) String() string { 24 | return fmt.Sprintf("{channel number=%d,data len=%d}", c.ChannelNumber, len(c.Data)) 25 | } 26 | 27 | // AddTo adds Channel Data to message. 28 | func (c *ChannelData) AddTo(m *stun.Message) error { 29 | if m.Type.Method != stun.MethodChannelData { 30 | return fmt.Errorf("channel data works only on channelData method, now is %s", m.Type.String()) 31 | } 32 | buf := new(bytes.Buffer) 33 | if c.ChannelNumber < MinChannelNumber || c.ChannelNumber > MaxChannelNumber { 34 | return fmt.Errorf("channel number is invalid :%d", c.ChannelNumber) 35 | } 36 | binary.Write(buf, binary.BigEndian, c.ChannelNumber) 37 | binary.Write(buf, binary.BigEndian, uint16(len(c.Data))) 38 | binary.Write(buf, binary.BigEndian, c.Data) //todo fix padding, when works on tcp... let 4 bytes align 39 | /* 40 | Over TCP and TLS-over-TCP, the ChannelData message MUST be padded to 41 | a multiple of four bytes in order to ensure the alignment of 42 | subsequent messages. The padding is not reflected in the length 43 | field of the ChannelData message, so the actual size of a ChannelData 44 | message (including padding) is (4 + Length) rounded up to the nearest 45 | multiple of 4. Over UDP, the padding is not required but MAY be 46 | included. 47 | */ 48 | m.Raw = buf.Bytes() 49 | return nil 50 | } 51 | 52 | // GetFrom decodes Channel Data from message. 53 | func (c *ChannelData) GetFrom(m *stun.Message) error { 54 | if m.Type.Method != stun.MethodChannelData { 55 | return fmt.Errorf("expect MethodChannelData,but got %s", m.String()) 56 | } 57 | buf := bytes.NewBuffer(m.Raw) 58 | err := binary.Read(buf, binary.BigEndian, &c.ChannelNumber) 59 | if err != nil { 60 | return err 61 | } 62 | if c.ChannelNumber < MinChannelNumber || c.ChannelNumber > MaxChannelNumber { 63 | return fmt.Errorf("channel number is invalid :%d", c.ChannelNumber) 64 | } 65 | var length uint16 66 | err = binary.Read(buf, binary.BigEndian, &length) 67 | if err != nil { 68 | return err 69 | } 70 | c.Data = make([]byte, length) 71 | err = binary.Read(buf, binary.BigEndian, c.Data) 72 | if err != nil { 73 | return err 74 | } 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /turn/chrome_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/hex" 7 | "testing" 8 | 9 | "github.com/nkbai/goice/stun" 10 | ) 11 | 12 | func TestChromeAllocRequest(t *testing.T) { 13 | var ( 14 | r = bytes.NewReader(loadData(t, "01_chromeallocreq.hex")) 15 | s = bufio.NewScanner(r) 16 | 17 | data [][]byte 18 | messages []*stun.Message 19 | ) 20 | // Decoding hex data into binary. 21 | for s.Scan() { 22 | b, err := hex.DecodeString(s.Text()) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | data = append(data, b) 27 | } 28 | // All hex streams decoded to raw binary format and stored in data slice. 29 | // Decoding packets to messages. 30 | for i, packet := range data { 31 | var ( 32 | m = new(stun.Message) 33 | ) 34 | if _, err := m.Write(packet); err != nil { 35 | t.Errorf("Packet %d: %v", i, err) 36 | } 37 | messages = append(messages, m) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /turn/data.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import "github.com/nkbai/goice/stun" 4 | 5 | // Data represents DATA attribute. 6 | // 7 | // The DATA attribute is present in all Send and Data indications. The 8 | // value portion of this attribute is variable length and consists of 9 | // the application data (that is, the data that would immediately follow 10 | // the UDP header if the data was been sent directly between the client 11 | // and the peer). 12 | // 13 | // https://trac.tools.ietf.org/html/rfc5766#section-14.4 14 | type Data []byte 15 | 16 | // AddTo adds DATA to message. 17 | func (d Data) AddTo(m *stun.Message) error { 18 | m.Add(stun.AttrData, d) 19 | return nil 20 | } 21 | 22 | // GetFrom decodes DATA from message. 23 | func (d *Data) GetFrom(m *stun.Message) error { 24 | v, err := m.Get(stun.AttrData) 25 | if err != nil { 26 | return err 27 | } 28 | *d = v 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /turn/data_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/nkbai/goice/stun" 8 | ) 9 | 10 | func BenchmarkData(b *testing.B) { 11 | b.Run("AddTo", func(b *testing.B) { 12 | m := new(stun.Message) 13 | d := make(Data, 10) 14 | for i := 0; i < b.N; i++ { 15 | d.AddTo(m) 16 | m.Reset() 17 | } 18 | }) 19 | b.Run("AddToRaw", func(b *testing.B) { 20 | m := new(stun.Message) 21 | d := make([]byte, 10) 22 | // Overhead should be low. 23 | for i := 0; i < b.N; i++ { 24 | m.Add(stun.AttrData, d) 25 | m.Reset() 26 | } 27 | }) 28 | } 29 | 30 | func TestData(t *testing.T) { 31 | t.Run("NoAlloc", func(t *testing.T) { 32 | m := new(stun.Message) 33 | v := []byte{1, 2, 3, 4} 34 | if wasAllocs(func() { 35 | // On stack. 36 | d := Data(v) 37 | d.AddTo(m) 38 | m.Reset() 39 | }) { 40 | t.Error("Unexpected allocations") 41 | } 42 | 43 | d := &Data{1, 2, 3, 4} 44 | if wasAllocs(func() { 45 | // On heap. 46 | d.AddTo(m) 47 | m.Reset() 48 | }) { 49 | t.Error("Unexpected allocations") 50 | } 51 | }) 52 | t.Run("AddTo", func(t *testing.T) { 53 | m := new(stun.Message) 54 | d := Data{1, 2, 33, 44, 0x13, 0xaf} 55 | if err := d.AddTo(m); err != nil { 56 | t.Fatal(err) 57 | } 58 | m.WriteHeader() 59 | t.Run("GetFrom", func(t *testing.T) { 60 | decoded := new(stun.Message) 61 | if _, err := decoded.Write(m.Raw); err != nil { 62 | t.Fatal("failed to decode message:", err) 63 | } 64 | var dataDecoded Data 65 | if err := dataDecoded.GetFrom(decoded); err != nil { 66 | t.Fatal(err) 67 | } 68 | if !bytes.Equal(dataDecoded, d) { 69 | t.Error(dataDecoded, "!=", d, "(expected)") 70 | } 71 | if wasAllocs(func() { 72 | var dataDecoded Data 73 | dataDecoded.GetFrom(decoded) 74 | }) { 75 | t.Error("Unexpected allocations") 76 | } 77 | t.Run("HandleErr", func(t *testing.T) { 78 | m := new(stun.Message) 79 | var handle Data 80 | if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound { 81 | t.Errorf("%v should be not found", err) 82 | } 83 | }) 84 | }) 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /turn/dontfrag.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import "github.com/nkbai/goice/stun" 4 | 5 | // DontFragmentAttr represents DONT-FRAGMENT attribute. 6 | type DontFragmentAttr bool 7 | 8 | // AddTo adds DONT-FRAGMENT attribute to message. 9 | func (DontFragmentAttr) AddTo(m *stun.Message) error { 10 | m.Add(stun.AttrDontFragment, nil) 11 | return nil 12 | } 13 | 14 | // IsSet returns true if DONT-FRAGMENT attribute is set. 15 | func (DontFragmentAttr) IsSet(m *stun.Message) bool { 16 | _, err := m.Get(stun.AttrDontFragment) 17 | return err == nil 18 | } 19 | 20 | // DontFragment is shorthand for DontFragmentAttr. 21 | var DontFragment DontFragmentAttr = true 22 | -------------------------------------------------------------------------------- /turn/dontfrag_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | func TestDontFragment(t *testing.T) { 10 | t.Run("False", func(t *testing.T) { 11 | m := new(stun.Message) 12 | m.WriteHeader() 13 | if DontFragment.IsSet(m) { 14 | t.Error("should not be set") 15 | } 16 | }) 17 | t.Run("AddTo", func(t *testing.T) { 18 | m := new(stun.Message) 19 | if err := DontFragment.AddTo(m); err != nil { 20 | t.Error(err) 21 | } 22 | m.WriteHeader() 23 | t.Run("IsSet", func(t *testing.T) { 24 | decoded := new(stun.Message) 25 | if _, err := decoded.Write(m.Raw); err != nil { 26 | t.Fatal("failed to decode message:", err) 27 | } 28 | if !DontFragment.IsSet(m) { 29 | t.Error("should be set") 30 | } 31 | if wasAllocs(func() { 32 | DontFragment.IsSet(m) 33 | }) { 34 | t.Error("unexpected allocations") 35 | } 36 | }) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /turn/evenport.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import "github.com/nkbai/goice/stun" 4 | 5 | // EvenPort represents EVEN-PORT attribute. 6 | // 7 | // This attribute allows the client to request that the port in the 8 | // relayed transport address be even, and (optionally) that the server 9 | // reserve the next-higher port number. 10 | // 11 | // https://trac.tools.ietf.org/html/rfc5766#section-14.6 12 | type EvenPort struct { 13 | // ReservePort means that the server is requested to reserve 14 | // the next-higher port number (on the same IP address) 15 | // for a subsequent allocation. 16 | ReservePort bool 17 | } 18 | 19 | func (p EvenPort) String() string { 20 | if p.ReservePort { 21 | return "reserve: true" 22 | } 23 | return "reserve: false" 24 | } 25 | 26 | const ( 27 | evenPortSize = 1 28 | firstBitSet = (1 << 8) - 1 // 0b100000000 29 | ) 30 | 31 | // AddTo adds even port to message. 32 | func (p EvenPort) AddTo(m *stun.Message) error { 33 | v := make([]byte, evenPortSize) 34 | if p.ReservePort { 35 | // Set first bit to 1. 36 | v[0] = firstBitSet 37 | } 38 | m.Add(stun.AttrEvenPort, v) 39 | return nil 40 | } 41 | 42 | // GetFrom decodes even port from message. 43 | func (p *EvenPort) GetFrom(m *stun.Message) error { 44 | v, err := m.Get(stun.AttrEvenPort) 45 | if err != nil { 46 | return err 47 | } 48 | if len(v) != evenPortSize { 49 | return &BadAttrLength{ 50 | Attr: stun.AttrEvenPort, 51 | Got: len(v), 52 | Expected: evenPortSize, 53 | } 54 | } 55 | if v[0]&firstBitSet > 0 { 56 | p.ReservePort = true 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /turn/evenport_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | func TestEvenPort(t *testing.T) { 10 | t.Run("String", func(t *testing.T) { 11 | p := EvenPort{} 12 | if p.String() != "reserve: false" { 13 | t.Errorf("bad value %q for reselve: false", p) 14 | } 15 | p.ReservePort = true 16 | if p.String() != "reserve: true" { 17 | t.Errorf("bad value %q for reselve: true", p) 18 | } 19 | }) 20 | t.Run("False", func(t *testing.T) { 21 | m := new(stun.Message) 22 | p := EvenPort{ 23 | ReservePort: false, 24 | } 25 | if err := p.AddTo(m); err != nil { 26 | t.Error(err) 27 | } 28 | m.WriteHeader() 29 | decoded := new(stun.Message) 30 | var port EvenPort 31 | decoded.Write(m.Raw) 32 | if err := port.GetFrom(m); err != nil { 33 | t.Fatal(err) 34 | } 35 | if port != p { 36 | t.Fatal("not equal") 37 | } 38 | }) 39 | t.Run("AddTo", func(t *testing.T) { 40 | m := new(stun.Message) 41 | p := EvenPort{ 42 | ReservePort: true, 43 | } 44 | if err := p.AddTo(m); err != nil { 45 | t.Error(err) 46 | } 47 | m.WriteHeader() 48 | t.Run("GetFrom", func(t *testing.T) { 49 | decoded := new(stun.Message) 50 | if _, err := decoded.Write(m.Raw); err != nil { 51 | t.Fatal("failed to decode message:", err) 52 | } 53 | port := EvenPort{} 54 | if err := port.GetFrom(decoded); err != nil { 55 | t.Fatal(err) 56 | } 57 | if port != p { 58 | t.Errorf("Decoded %q, expected %q", port, p) 59 | } 60 | if wasAllocs(func() { 61 | port.GetFrom(decoded) 62 | }) { 63 | t.Error("Unexpected allocations") 64 | } 65 | t.Run("HandleErr", func(t *testing.T) { 66 | m := new(stun.Message) 67 | var handle EvenPort 68 | if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound { 69 | t.Errorf("%v should be not found", err) 70 | } 71 | m.Add(stun.AttrEvenPort, []byte{1, 2, 3}) 72 | if err, ok := handle.GetFrom(m).(*BadAttrLength); !ok { 73 | t.Errorf("%v should be *BadAttrLength", err) 74 | } 75 | }) 76 | }) 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /turn/examples/turn-setters/corpus/1b6453892473a467d07372d45eb05abc2031647a-1: -------------------------------------------------------------------------------- 1 | 4 -------------------------------------------------------------------------------- /turn/examples/turn-setters/corpus/55df2a59ed6a888ee2f0cdfdcc8582696702de7a-1: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /turn/examples/turn-setters/corpus/a15226820da790455c2899eb3d3a0c26ae48f18e-1: -------------------------------------------------------------------------------- 1 | unt8 -------------------------------------------------------------------------------- /turn/examples/turn-setters/corpus/aff3f3449225ed816660ce1e2fe227531915a659-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/turn/examples/turn-setters/corpus/aff3f3449225ed816660ce1e2fe227531915a659-2 -------------------------------------------------------------------------------- /turn/examples/turn-setters/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkbai/goice/439e83443590a99f0fc5bbd9cfe8ae3eed38191a/turn/examples/turn-setters/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709 -------------------------------------------------------------------------------- /turn/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package turn 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/nkbai/goice/stun" 9 | ) 10 | 11 | type attr interface { 12 | stun.Getter 13 | stun.Setter 14 | } 15 | 16 | type attrs []struct { 17 | g attr 18 | t stun.AttrType 19 | } 20 | 21 | func (a attrs) pick(v byte) struct { 22 | g attr 23 | t stun.AttrType 24 | } { 25 | idx := int(v) % len(a) 26 | return a[idx] 27 | } 28 | 29 | func FuzzSetters(data []byte) int { 30 | var ( 31 | m1 = &stun.Message{ 32 | Raw: make([]byte, 0, 2048), 33 | } 34 | m2 = &stun.Message{ 35 | Raw: make([]byte, 0, 2048), 36 | } 37 | m3 = &stun.Message{ 38 | Raw: make([]byte, 0, 2048), 39 | } 40 | ) 41 | attributes := attrs{ 42 | {new(RequestedTransport), stun.AttrRequestedTransport}, 43 | {new(RelayedAddress), stun.AttrXORRelayedAddress}, 44 | {new(ChannelNumber), stun.AttrChannelNumber}, 45 | {new(Data), stun.AttrData}, 46 | {new(EvenPort), stun.AttrEvenPort}, 47 | {new(Lifetime), stun.AttrLifetime}, 48 | {new(ReservationToken), stun.AttrReservationToken}, 49 | } 50 | var firstByte = byte(0) 51 | if len(data) > 0 { 52 | firstByte = data[0] 53 | } 54 | a := attributes.pick(firstByte) 55 | value := data 56 | if len(data) > 1 { 57 | value = value[1:] 58 | } 59 | m1.WriteHeader() 60 | m1.Add(a.t, value) 61 | err := a.g.GetFrom(m1) 62 | if err == stun.ErrAttributeNotFound { 63 | fmt.Println("unexpected 404") 64 | panic(err) 65 | } 66 | if err != nil { 67 | return 1 68 | } 69 | m2.WriteHeader() 70 | if err2 := a.g.AddTo(m2); err2 != nil { 71 | fmt.Println("failed to add atribute to m2") 72 | panic(err2) 73 | } 74 | m3.WriteHeader() 75 | v, err := m2.Get(a.t) 76 | if err != nil { 77 | panic(err) 78 | } 79 | m3.Add(a.t, v) 80 | 81 | if !m2.Equal(m3) { 82 | fmt.Println(m2, "not equal", m3) 83 | panic("not equal") 84 | } 85 | return 1 86 | } 87 | -------------------------------------------------------------------------------- /turn/fuzz_test.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package turn 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func corpus(t *testing.T, function, typ string) [][]byte { 13 | var data [][]byte 14 | p := filepath.Join("examples", function, typ) 15 | f, err := os.Open(p) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | list, err := f.Readdir(-1) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | for _, d := range list { 24 | if strings.Contains(d.Name(), ".") { 25 | // Skipping non-raw files. 26 | continue 27 | } 28 | df, err := os.Open(filepath.Join(p, d.Name())) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | buf := make([]byte, 5000) 33 | n, _ := df.Read(buf) 34 | data = append(data, buf[:n]) 35 | df.Close() 36 | } 37 | return data 38 | } 39 | 40 | func TestFuzzSetters_Crashers(t *testing.T) { 41 | for _, buf := range corpus(t, "turn-setters", "crashers") { 42 | FuzzSetters(buf) 43 | } 44 | } 45 | 46 | func TestFuzzSetters_Coverage(t *testing.T) { 47 | for _, buf := range corpus(t, "turn-setters", "corpus") { 48 | FuzzSetters(buf) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /turn/lifetime.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | // DefaultLifetime in RFC 5766 is 10 minutes. 10 | // 11 | // https://trac.tools.ietf.org/html/rfc5766#section-2.2 12 | const DefaultLifetime = time.Minute * 10 13 | 14 | // Lifetime represents LIFETIME attribute. 15 | // 16 | // The LIFETIME attribute represents the duration for which the server 17 | // will maintain an allocation in the absence of a refresh. The value 18 | // portion of this attribute is 4-bytes long and consists of a 32-bit 19 | // unsigned integral value representing the number of seconds remaining 20 | // until expiration. 21 | // 22 | // https://trac.tools.ietf.org/html/rfc5766#section-14.2 23 | type Lifetime struct { 24 | time.Duration 25 | } 26 | 27 | // uint32 seconds 28 | const lifetimeSize = 4 // 4 bytes, 32 bits 29 | 30 | // AddTo adds LIFETIME to message. 31 | func (l Lifetime) AddTo(m *stun.Message) error { 32 | v := make([]byte, lifetimeSize) 33 | bin.PutUint32(v, uint32(l.Seconds())) 34 | m.Add(stun.AttrLifetime, v) 35 | return nil 36 | } 37 | 38 | // GetFrom decodes LIFETIME from message. 39 | func (l *Lifetime) GetFrom(m *stun.Message) error { 40 | v, err := m.Get(stun.AttrLifetime) 41 | if err != nil { 42 | return err 43 | } 44 | if len(v) != lifetimeSize { 45 | return &BadAttrLength{ 46 | Attr: stun.AttrLifetime, 47 | Got: len(v), 48 | Expected: lifetimeSize, 49 | } 50 | } 51 | _ = v[lifetimeSize-1] // asserting length 52 | seconds := bin.Uint32(v) 53 | l.Duration = time.Second * time.Duration(seconds) 54 | return nil 55 | } 56 | 57 | // ZeroLifetime is shorthand for setting zero lifetime 58 | // that indicates to close allocation. 59 | var ZeroLifetime stun.Setter = Lifetime{} 60 | -------------------------------------------------------------------------------- /turn/lifetime_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "testing" 5 | 6 | "fmt" 7 | "time" 8 | 9 | "github.com/nkbai/goice/stun" 10 | ) 11 | 12 | func ExampleLifetime() { 13 | // Encoding lifetime to message. 14 | m := new(stun.Message) 15 | Lifetime{time.Minute}.AddTo(m) 16 | m.WriteHeader() 17 | 18 | // Decoding message. 19 | mDec := new(stun.Message) 20 | if _, err := m.WriteTo(mDec); err != nil { 21 | panic(err) 22 | } 23 | // Decoding lifetime from message. 24 | l := Lifetime{} 25 | l.GetFrom(m) 26 | fmt.Println("Decoded:", l) 27 | // Output: 28 | // Decoded: 1m0s 29 | } 30 | 31 | func BenchmarkLifetime(b *testing.B) { 32 | b.Run("AddTo", func(b *testing.B) { 33 | b.ReportAllocs() 34 | m := new(stun.Message) 35 | for i := 0; i < b.N; i++ { 36 | l := Lifetime{time.Second} 37 | if err := l.AddTo(m); err != nil { 38 | b.Fatal(err) 39 | } 40 | m.Reset() 41 | } 42 | }) 43 | b.Run("GetFrom", func(b *testing.B) { 44 | m := new(stun.Message) 45 | Lifetime{time.Minute}.AddTo(m) 46 | for i := 0; i < b.N; i++ { 47 | l := Lifetime{} 48 | if err := l.GetFrom(m); err != nil { 49 | b.Fatal(err) 50 | } 51 | } 52 | }) 53 | } 54 | 55 | func TestLifetime(t *testing.T) { 56 | t.Run("String", func(t *testing.T) { 57 | l := Lifetime{time.Second * 10} 58 | if l.String() != "10s" { 59 | t.Errorf("bad string %s, expedted 10s", l) 60 | } 61 | }) 62 | t.Run("NoAlloc", func(t *testing.T) { 63 | m := &stun.Message{} 64 | if wasAllocs(func() { 65 | // On stack. 66 | l := Lifetime{ 67 | Duration: time.Minute, 68 | } 69 | l.AddTo(m) 70 | m.Reset() 71 | }) { 72 | t.Error("Unexpected allocations") 73 | } 74 | 75 | l := &Lifetime{time.Second} 76 | if wasAllocs(func() { 77 | // On heap. 78 | l.AddTo(m) 79 | m.Reset() 80 | }) { 81 | t.Error("Unexpected allocations") 82 | } 83 | }) 84 | t.Run("AddTo", func(t *testing.T) { 85 | m := new(stun.Message) 86 | l := Lifetime{time.Second * 10} 87 | if err := l.AddTo(m); err != nil { 88 | t.Error(err) 89 | } 90 | m.WriteHeader() 91 | t.Run("GetFrom", func(t *testing.T) { 92 | decoded := new(stun.Message) 93 | if _, err := decoded.Write(m.Raw); err != nil { 94 | t.Fatal("failed to decode message:", err) 95 | } 96 | life := Lifetime{} 97 | if err := life.GetFrom(decoded); err != nil { 98 | t.Fatal(err) 99 | } 100 | if life != l { 101 | t.Errorf("Decoded %q, expected %q", life, l) 102 | } 103 | if wasAllocs(func() { 104 | life.GetFrom(decoded) 105 | }) { 106 | t.Error("Unexpected allocations") 107 | } 108 | t.Run("HandleErr", func(t *testing.T) { 109 | m := new(stun.Message) 110 | nHandle := new(Lifetime) 111 | if err := nHandle.GetFrom(m); err != stun.ErrAttributeNotFound { 112 | t.Errorf("%v should be not found", err) 113 | } 114 | m.Add(stun.AttrLifetime, []byte{1, 2, 3}) 115 | if err, ok := nHandle.GetFrom(m).(*BadAttrLength); !ok { 116 | t.Errorf("%v should be *BadAttrLength", err) 117 | } 118 | }) 119 | }) 120 | }) 121 | } 122 | -------------------------------------------------------------------------------- /turn/peeraddr.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | // PeerAddress implements XOR-PEER-ADDRESS attribute. 10 | // 11 | // The XOR-PEER-ADDRESS specifies the address and port of the peer as 12 | // seen from the TURN server. (For example, the peer's server-reflexive 13 | // transport address if the peer is behind a NAT.) 14 | // 15 | // https://trac.tools.ietf.org/html/rfc5766#section-14.3 16 | type PeerAddress struct { 17 | IP net.IP 18 | Port int 19 | } 20 | 21 | func (a PeerAddress) String() string { 22 | return stun.XORMappedAddress(a).String() 23 | } 24 | 25 | // AddTo adds XOR-PEER-ADDRESS to message. 26 | func (a PeerAddress) AddTo(m *stun.Message) error { 27 | return (stun.XORMappedAddress)(a).AddToAs(m, stun.AttrXORPeerAddress) 28 | } 29 | 30 | // GetFrom decodes XOR-PEER-ADDRESS from message. 31 | func (a *PeerAddress) GetFrom(m *stun.Message) error { 32 | return (*stun.XORMappedAddress)(a).GetFromAs(m, stun.AttrXORPeerAddress) 33 | } 34 | -------------------------------------------------------------------------------- /turn/peeraddr_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/nkbai/goice/stun" 8 | ) 9 | 10 | func TestPeerAddress(t *testing.T) { 11 | // Simple tests because already tested in stun. 12 | a := PeerAddress{ 13 | IP: net.IPv4(111, 11, 1, 2), 14 | Port: 333, 15 | } 16 | m := new(stun.Message) 17 | if err := a.AddTo(m); err != nil { 18 | t.Fatal(err) 19 | } 20 | m.WriteHeader() 21 | decoded := new(stun.Message) 22 | decoded.Write(m.Raw) 23 | var aGot PeerAddress 24 | if err := aGot.GetFrom(decoded); err != nil { 25 | t.Fatal(err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /turn/relayedaddr.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | // RelayedAddress implements XOR-RELAYED-ADDRESS attribute. 10 | // 11 | // The XOR-PEER-ADDRESS specifies the address and port of the peer as 12 | // seen from the TURN server. (For example, the peer's server-reflexive 13 | // transport address if the peer is behind a NAT.) 14 | // 15 | // https://trac.tools.ietf.org/html/rfc5766#section-14.5 16 | type RelayedAddress struct { 17 | IP net.IP 18 | Port int 19 | } 20 | 21 | func (a RelayedAddress) String() string { 22 | return (stun.XORMappedAddress)(a).String() 23 | } 24 | 25 | // AddTo adds XOR-PEER-ADDRESS to message. 26 | func (a RelayedAddress) AddTo(m *stun.Message) error { 27 | return (stun.XORMappedAddress)(a).AddToAs(m, stun.AttrXORRelayedAddress) 28 | } 29 | 30 | // GetFrom decodes XOR-PEER-ADDRESS from message. 31 | func (a *RelayedAddress) GetFrom(m *stun.Message) error { 32 | return (*stun.XORMappedAddress)(a).GetFromAs(m, stun.AttrXORRelayedAddress) 33 | } 34 | -------------------------------------------------------------------------------- /turn/relayedaddr_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/nkbai/goice/stun" 8 | ) 9 | 10 | func TestRelayedAddress(t *testing.T) { 11 | // Simple tests because already tested in stun. 12 | a := RelayedAddress{ 13 | IP: net.IPv4(111, 11, 1, 2), 14 | Port: 333, 15 | } 16 | m := new(stun.Message) 17 | if err := a.AddTo(m); err != nil { 18 | t.Fatal(err) 19 | } 20 | m.WriteHeader() 21 | decoded := new(stun.Message) 22 | decoded.Write(m.Raw) 23 | var aGot RelayedAddress 24 | if err := aGot.GetFrom(decoded); err != nil { 25 | t.Fatal(err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /turn/reqtrans.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | // Protocol is IANA assigned protocol number. 10 | type Protocol byte 11 | 12 | const ( 13 | // ProtoUDP is IANA assigned protocol number for UDP. 14 | ProtoUDP Protocol = 17 15 | ) 16 | 17 | func (p Protocol) String() string { 18 | switch p { 19 | case ProtoUDP: 20 | return "UDP" 21 | default: 22 | return strconv.Itoa(int(p)) 23 | } 24 | } 25 | 26 | // RequestedTransport represents REQUESTED-TRANSPORT attribute. 27 | // 28 | // This attribute is used by the client to request a specific transport 29 | // protocol for the allocated transport address. RFC 5766 only allows the use of 30 | // codepoint 17 (User Datagram Protocol). 31 | // 32 | // https://trac.tools.ietf.org/html/rfc5766#section-14.7 33 | type RequestedTransport struct { 34 | Protocol Protocol 35 | } 36 | 37 | func (t RequestedTransport) String() string { 38 | return "protocol: " + t.Protocol.String() 39 | } 40 | 41 | const requestedTransportSize = 4 42 | 43 | // AddTo adds REQUESTED-TRANSPORT to message. 44 | func (t RequestedTransport) AddTo(m *stun.Message) error { 45 | v := make([]byte, requestedTransportSize) 46 | v[0] = byte(t.Protocol) 47 | // b[1:4] is RFFU = 0. 48 | // The RFFU field MUST be set to zero on transmission and MUST be 49 | // ignored on reception. It is reserved for future uses. 50 | m.Add(stun.AttrRequestedTransport, v) 51 | return nil 52 | } 53 | 54 | // GetFrom decodes REQUESTED-TRANSPORT from message. 55 | func (t *RequestedTransport) GetFrom(m *stun.Message) error { 56 | v, err := m.Get(stun.AttrRequestedTransport) 57 | if err != nil { 58 | return err 59 | } 60 | if len(v) != requestedTransportSize { 61 | return &BadAttrLength{ 62 | Attr: stun.AttrRequestedTransport, 63 | Got: len(v), 64 | Expected: requestedTransportSize, 65 | } 66 | } 67 | t.Protocol = Protocol(v[0]) 68 | return nil 69 | } 70 | 71 | // RequestedTransportUDP is setter for requested transport attribute with 72 | // value ProtoUDP (17). 73 | var RequestedTransportUDP stun.Setter = RequestedTransport{ 74 | Protocol: ProtoUDP, 75 | } 76 | -------------------------------------------------------------------------------- /turn/reqtrans_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nkbai/goice/stun" 7 | ) 8 | 9 | func TestRequestedTransport(t *testing.T) { 10 | t.Run("String", func(t *testing.T) { 11 | r := RequestedTransport{ 12 | Protocol: ProtoUDP, 13 | } 14 | if r.String() != "protocol: UDP" { 15 | t.Errorf("bad string %q, expected %q", r, 16 | "protocol: UDP", 17 | ) 18 | } 19 | r.Protocol = 254 20 | if r.String() != "protocol: 254" { 21 | if r.String() != "protocol: UDP" { 22 | t.Errorf("bad string %q, expected %q", r, 23 | "protocol: 254", 24 | ) 25 | } 26 | } 27 | }) 28 | t.Run("NoAlloc", func(t *testing.T) { 29 | m := &stun.Message{} 30 | if wasAllocs(func() { 31 | // On stack. 32 | r := RequestedTransport{ 33 | Protocol: ProtoUDP, 34 | } 35 | r.AddTo(m) 36 | m.Reset() 37 | }) { 38 | t.Error("Unexpected allocations") 39 | } 40 | 41 | r := &RequestedTransport{ 42 | Protocol: ProtoUDP, 43 | } 44 | if wasAllocs(func() { 45 | // On heap. 46 | r.AddTo(m) 47 | m.Reset() 48 | }) { 49 | t.Error("Unexpected allocations") 50 | } 51 | }) 52 | t.Run("AddTo", func(t *testing.T) { 53 | m := new(stun.Message) 54 | r := RequestedTransport{ 55 | Protocol: ProtoUDP, 56 | } 57 | if err := r.AddTo(m); err != nil { 58 | t.Error(err) 59 | } 60 | m.WriteHeader() 61 | t.Run("GetFrom", func(t *testing.T) { 62 | decoded := new(stun.Message) 63 | if _, err := decoded.Write(m.Raw); err != nil { 64 | t.Fatal("failed to decode message:", err) 65 | } 66 | req := RequestedTransport{ 67 | Protocol: ProtoUDP, 68 | } 69 | if err := req.GetFrom(decoded); err != nil { 70 | t.Fatal(err) 71 | } 72 | if req != r { 73 | t.Errorf("Decoded %q, expected %q", req, r) 74 | } 75 | if wasAllocs(func() { 76 | r.GetFrom(decoded) 77 | }) { 78 | t.Error("Unexpected allocations") 79 | } 80 | t.Run("HandleErr", func(t *testing.T) { 81 | m := new(stun.Message) 82 | var handle RequestedTransport 83 | if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound { 84 | t.Errorf("%v should be not found", err) 85 | } 86 | m.Add(stun.AttrRequestedTransport, []byte{1, 2, 3}) 87 | if err, ok := handle.GetFrom(m).(*BadAttrLength); !ok { 88 | t.Errorf("%v should be *BadAttrLength", err) 89 | } 90 | }) 91 | }) 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /turn/rsrvtoken.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import "github.com/nkbai/goice/stun" 4 | 5 | // ReservationToken represents RESERVATION-TOKEN attribute. 6 | // 7 | // The RESERVATION-TOKEN attribute contains a token that uniquely 8 | // identifies a relayed transport address being held in reserve by the 9 | // server. The server includes this attribute in a success response to 10 | // tell the client about the token, and the client includes this 11 | // attribute in a subsequent Allocate request to request the server use 12 | // that relayed transport address for the allocation. 13 | // 14 | // https://trac.tools.ietf.org/html/rfc5766#section-14.9 15 | type ReservationToken []byte 16 | 17 | const reservationTokenSize = 8 // 8 bytes 18 | 19 | // AddTo adds RESERVATION-TOKEN to message. 20 | func (t ReservationToken) AddTo(m *stun.Message) error { 21 | if len(t) != reservationTokenSize { 22 | return &BadAttrLength{ 23 | Attr: stun.AttrReservationToken, 24 | Expected: reservationTokenSize, 25 | Got: len(t), 26 | } 27 | } 28 | m.Add(stun.AttrReservationToken, t) 29 | return nil 30 | } 31 | 32 | // GetFrom decodes RESERVATION-TOKEN from message. 33 | func (t *ReservationToken) GetFrom(m *stun.Message) error { 34 | v, err := m.Get(stun.AttrReservationToken) 35 | if err != nil { 36 | return err 37 | } 38 | if len(v) != reservationTokenSize { 39 | return &BadAttrLength{ 40 | Attr: stun.AttrReservationToken, 41 | Expected: reservationTokenSize, 42 | Got: len(v), 43 | } 44 | } 45 | *t = v 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /turn/rsrvtoken_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/nkbai/goice/stun" 8 | ) 9 | 10 | func TestReservationToken(t *testing.T) { 11 | t.Run("NoAlloc", func(t *testing.T) { 12 | m := &stun.Message{} 13 | tok := make([]byte, 8) 14 | if wasAllocs(func() { 15 | // On stack. 16 | tk := ReservationToken(tok) 17 | tk.AddTo(m) 18 | m.Reset() 19 | }) { 20 | t.Error("Unexpected allocations") 21 | } 22 | 23 | tk := make(ReservationToken, 8) 24 | if wasAllocs(func() { 25 | // On heap. 26 | tk.AddTo(m) 27 | m.Reset() 28 | }) { 29 | t.Error("Unexpected allocations") 30 | } 31 | }) 32 | t.Run("AddTo", func(t *testing.T) { 33 | m := new(stun.Message) 34 | tk := make(ReservationToken, 8) 35 | tk[2] = 33 36 | tk[7] = 1 37 | if err := tk.AddTo(m); err != nil { 38 | t.Error(err) 39 | } 40 | m.WriteHeader() 41 | t.Run("HandleErr", func(t *testing.T) { 42 | badTk := ReservationToken{34, 45} 43 | if err, ok := badTk.AddTo(m).(*BadAttrLength); !ok { 44 | t.Errorf("%v should be *BadAttrLength", err) 45 | } 46 | }) 47 | t.Run("GetFrom", func(t *testing.T) { 48 | decoded := new(stun.Message) 49 | if _, err := decoded.Write(m.Raw); err != nil { 50 | t.Fatal("failed to decode message:", err) 51 | } 52 | var tok ReservationToken 53 | if err := tok.GetFrom(decoded); err != nil { 54 | t.Fatal(err) 55 | } 56 | if !bytes.Equal(tok, tk) { 57 | t.Errorf("Decoded %v, expected %v", tok, tk) 58 | } 59 | if wasAllocs(func() { 60 | tok.GetFrom(decoded) 61 | }) { 62 | t.Error("Unexpected allocations") 63 | } 64 | t.Run("HandleErr", func(t *testing.T) { 65 | m := new(stun.Message) 66 | var handle ReservationToken 67 | if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound { 68 | t.Errorf("%v should be not found", err) 69 | } 70 | m.Add(stun.AttrReservationToken, []byte{1, 2, 3}) 71 | if err, ok := handle.GetFrom(m).(*BadAttrLength); !ok { 72 | t.Errorf("%v should be *BadAttrLength", err) 73 | } 74 | }) 75 | }) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /turn/testdata/01_chromeallocreq.hex: -------------------------------------------------------------------------------- 1 | 000300242112a442626b4a6849664c3630526863802f0016687474703a2f2f6c6f63616c686f73743a333030302f00000019000411000000 2 | 011300582112a442626b4a6849664c36305268630009001000000401556e617574686f72697a656400150010356130323039623563623830363130360014000b61312e63796465762e7275758022001a436f7475726e2d342e352e302e33202764616e204569646572272300 3 | 0003006c2112a442324e50695a437a4634535034802f0016687474703a2f2f6c6f63616c686f73743a333030302f000000190004110000000006000665726e61646f00000014000b61312e63796465762e7275000015001035613032303962356362383036313036000800145c8743f3b64bec0880cdd8d476d37b801a6c3d33 4 | 010300582112a442324e50695a437a4634535034001600080001fb922b1ab211002000080001adb2f49f38ae000d0004000002588022001a436f7475726e2d342e352e302e33202764616e204569646572277475000800145d7e85b767a519ffce91dbf0a96775e370db92e3 5 | -------------------------------------------------------------------------------- /turn/turn.go: -------------------------------------------------------------------------------- 1 | // Package turn implements RFC 5766 Traversal Using Relays around NAT. 2 | package turn 3 | 4 | import ( 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/nkbai/goice/stun" 9 | ) 10 | 11 | // bin is shorthand for binary.BigEndian. 12 | var bin = binary.BigEndian 13 | 14 | // BadAttrLength means that length for attribute is invalid. 15 | type BadAttrLength struct { 16 | Attr stun.AttrType 17 | Got int 18 | Expected int 19 | } 20 | 21 | func (e BadAttrLength) Error() string { 22 | return fmt.Sprintf("incorrect length for %s: got %d, expected %d", 23 | e.Attr, 24 | e.Got, 25 | e.Expected, 26 | ) 27 | } 28 | 29 | // Default ports for TURN from RFC 5766 Section 4. 30 | const ( 31 | // DefaultPort for TURN is same as STUN. 32 | DefaultPort = stun.DefaultPort 33 | // DefaultTLSPort is for TURN over TLS and is same as STUN. 34 | DefaultTLSPort = stun.DefaultTLSPort 35 | ) 36 | 37 | var ( 38 | // AllocateRequest is shorthand for allocation request message type. 39 | AllocateRequest = stun.NewType(stun.MethodAllocate, stun.ClassRequest) 40 | // CreatePermissionRequest is shorthand for create permission request type. 41 | CreatePermissionRequest = stun.NewType(stun.MethodCreatePermission, stun.ClassRequest) 42 | // CreatePermissionResponse is shorthand for create permission response type 43 | CreatePermissionResponse = stun.NewType(stun.MethodCreatePermission, stun.ClassSuccessResponse) 44 | // SendIndication is shorthand for send indication message type to turn server. 45 | SendIndication = stun.NewType(stun.MethodSend, stun.ClassIndication) 46 | //DataIndication is shorthand for data indication message type from turn server. 47 | DataIndication = stun.NewType(stun.MethodData, stun.ClassIndication) 48 | //ChannelBindRequest is shorthand for send channel bind message to turn server 49 | ChannelBindRequest = stun.NewType(stun.MethodChannelBind, stun.ClassRequest) 50 | // RefreshRequest is shorthand for refresh request message type. 51 | RefreshRequest = stun.NewType(stun.MethodRefresh, stun.ClassRequest) 52 | // ChannelDataRequest is fake request 53 | ChannelDataRequest = stun.NewType(stun.MethodChannelData, stun.ClassRequest) 54 | // RefreshResponse is shorthand for a success refresh response 55 | RefreshResponse = stun.NewType(stun.MethodRefresh, stun.ClassSuccessResponse) 56 | ) 57 | -------------------------------------------------------------------------------- /turn/turn_test.go: -------------------------------------------------------------------------------- 1 | package turn 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/nkbai/goice/stun" 10 | ) 11 | 12 | const allocRuns = 10 13 | 14 | // wasAllocs returns true if f allocates memory. 15 | func wasAllocs(f func()) bool { 16 | return testing.AllocsPerRun(allocRuns, f) > 0 17 | } 18 | 19 | func TestBadAttrLength_Error(t *testing.T) { 20 | b := &BadAttrLength{ 21 | Attr: stun.AttrData, 22 | Expected: 100, 23 | Got: 11, 24 | } 25 | if b.Error() != "incorrect length for DATA: got 11, expected 100" { 26 | t.Error("Bad value", b.Error()) 27 | } 28 | } 29 | 30 | func loadData(tb testing.TB, name string) []byte { 31 | name = filepath.Join("testdata", name) 32 | f, err := os.Open(name) 33 | if err != nil { 34 | tb.Fatal(err) 35 | } 36 | defer func() { 37 | if errClose := f.Close(); errClose != nil { 38 | tb.Fatal(errClose) 39 | } 40 | }() 41 | v, err := ioutil.ReadAll(f) 42 | if err != nil { 43 | tb.Fatal(err) 44 | } 45 | return v 46 | } 47 | --------------------------------------------------------------------------------