diff --git a/README.md b/README.md index 0daf743..f754631 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu * [Jerko Steiner](https://github.com/jeremija) * [Sidney San Martín](https://github.com/s4y) * [JooYoung Lim](https://github.com/DevRockstarZ) +* [Kory Miller](https://github.com/korymiller1489) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/candidate.go b/candidate.go index 0104a6e..237f58e 100644 --- a/candidate.go +++ b/candidate.go @@ -33,6 +33,8 @@ type Candidate interface { Equal(other Candidate) bool + Marshal() string + addr() net.Addr agent() *Agent context() context.Context diff --git a/candidate_base.go b/candidate_base.go index 59377f0..be0fc1b 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "net" + "strconv" + "strings" "sync/atomic" "time" @@ -357,3 +359,97 @@ func (c *candidateBase) agent() *Agent { func (c *candidateBase) context() context.Context { return c } + +// Marshal returns the string representation of the ICECandidate +func (c candidateBase) Marshal() string { + val := fmt.Sprintf("%s %d %s %d %s %d typ %s", + c.ID(), + c.Component(), + c.NetworkType().NetworkShort(), + c.Priority(), + c.Address(), + c.Port(), + c.Type()) + + if c.RelatedAddress() != nil { + val = fmt.Sprintf("%s raddr %s rport %d", + val, + c.RelatedAddress().Address, + c.RelatedAddress().Port) + } + + return val +} + +// UnmarshalCandidate creates a Candidate from its string representation +func UnmarshalCandidate(raw string) (Candidate, error) { + split := strings.Fields(raw) + if len(split) < 8 { + return nil, fmt.Errorf("attribute not long enough to be ICE candidate (%d)", len(split)) + } + + // Foundation + foundation := split[0] + + // Component + rawComponent, err := strconv.ParseUint(split[1], 10, 16) + if err != nil { + return nil, fmt.Errorf("could not parse component: %v", err) + } + component := uint16(rawComponent) + + // Protocol + protocol := split[2] + + // Priority + if _, err = strconv.ParseUint(split[3], 10, 32); err != nil { + return nil, fmt.Errorf("could not parse priority: %v", err) + } + + // Address + address := split[4] + + // Port + rawPort, err := strconv.ParseUint(split[5], 10, 16) + if err != nil { + return nil, fmt.Errorf("could not parse port: %v", err) + } + port := int(rawPort) + typ := split[7] + + relatedAddress := "" + relatedPort := 0 + if len(split) > 8 { + split = split[8:] + + if split[0] == "raddr" { + if len(split) < 4 { + return nil, fmt.Errorf("could not parse related addresses: incorrect length") + } + + // RelatedAddress + relatedAddress = split[1] + + // RelatedPort + rawRelatedPort, err := strconv.ParseUint(split[3], 10, 16) + if err != nil { + return nil, fmt.Errorf("could not parse port: %v", err) + } + relatedPort = int(rawRelatedPort) + } + + } + + switch typ { + case "host": + return NewCandidateHost(&CandidateHostConfig{foundation, protocol, address, port, uint16(component), TCPTypePassive}) + case "srflx": + return NewCandidateServerReflexive(&CandidateServerReflexiveConfig{foundation, protocol, address, port, uint16(component), relatedAddress, relatedPort}) + case "prflx": + return NewCandidatePeerReflexive(&CandidatePeerReflexiveConfig{foundation, protocol, address, port, uint16(component), relatedAddress, relatedPort}) + case "relay": + return NewCandidateRelay(&CandidateRelayConfig{foundation, protocol, address, port, uint16(component), relatedAddress, relatedPort, nil}) + } + + return nil, fmt.Errorf("Unknown candidate typ(%s)", typ) +} diff --git a/candidate_test.go b/candidate_test.go index fea3492..509f807 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -136,3 +136,80 @@ func TestCandidateLastReceived(t *testing.T) { candidate.setLastReceived(now) assert.Equal(t, candidate.LastReceived(), now) } + +func TestCandidateMarshal(t *testing.T) { + for _, test := range []struct { + candidate Candidate + marshaled string + expectError bool + }{ + {&CandidateHost{ + candidateBase{ + networkType: NetworkTypeUDP6, + candidateType: CandidateTypeHost, + address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + port: 53987, + }, + "", + }, + "1938809241 1 udp 2130706431 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host", + false, + }, + {&CandidateHost{ + candidateBase{ + networkType: NetworkTypeUDP4, + candidateType: CandidateTypeHost, + address: "10.0.75.1", + port: 53634, + }, + "", + }, + "1986380506 1 udp 2130706431 10.0.75.1 53634 typ host", + false, + }, + {&CandidateServerReflexive{ + candidateBase{ + networkType: NetworkTypeUDP4, + candidateType: CandidateTypeServerReflexive, + address: "191.228.238.68", + port: 53991, + relatedAddress: &CandidateRelatedAddress{"192.168.0.274", 53991}, + }, + }, + "4207374051 1 udp 1694498815 191.228.238.68 53991 typ srflx raddr 192.168.0.274 rport 53991", + false, + }, + {&CandidateRelay{ + candidateBase{ + networkType: NetworkTypeUDP4, + candidateType: CandidateTypeRelay, + address: "50.0.0.1", + port: 5000, + relatedAddress: &CandidateRelatedAddress{"192.168.0.1", 5001}, + }, + nil, + }, + "4207374051 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 5001", + false, + }, + + // Invalid candidates + {nil, "", true}, + {nil, "1938809241", true}, + {nil, "1986380506 99999999 udp 2122063615 10.0.75.1 53634 typ host generation 0 network-id 2", true}, + {nil, "1986380506 1 udp 99999999999 10.0.75.1 53634 typ host", true}, + {nil, "4207374051 1 udp 1685790463 191.228.238.68 99999999 typ srflx raddr 192.168.0.278 rport 53991 generation 0 network-id 3", true}, + {nil, "4207374051 1 udp 1685790463 191.228.238.68 53991 typ srflx raddr", true}, + {nil, "4207374051 1 udp 1685790463 191.228.238.68 53991 typ srflx raddr 192.168.0.278 rport 99999999 generation 0 network-id 3", true}, + } { + actualCandidate, err := UnmarshalCandidate(test.marshaled) + if test.expectError { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.True(t, test.candidate.Equal(actualCandidate)) + assert.Equal(t, test.marshaled, actualCandidate.Marshal()) + } +} diff --git a/go.sum b/go.sum index 55c8c7b..356b6cd 100644 --- a/go.sum +++ b/go.sum @@ -12,9 +12,7 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg= github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= -github.com/pion/transport v0.8.10 h1:lTiobMEw2PG6BH/mgIVqTV2mBp/mPT+IJLaN8ZxgdHk= github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8= -github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80= github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM= github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A= @@ -24,11 +22,8 @@ github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI= github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -38,13 +33,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -53,7 +45,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=