vendor.github.com.pion.ice.v2.url.go Maven / Gradle / Ivy
package ice
import (
"net"
"net/url"
"strconv"
)
// SchemeType indicates the type of server used in the ice.URL structure.
type SchemeType int
// Unknown defines default public constant to use for "enum" like struct
// comparisons when no value was defined.
const Unknown = iota
const (
// SchemeTypeSTUN indicates the URL represents a STUN server.
SchemeTypeSTUN SchemeType = iota + 1
// SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server.
SchemeTypeSTUNS
// SchemeTypeTURN indicates the URL represents a TURN server.
SchemeTypeTURN
// SchemeTypeTURNS indicates the URL represents a TURNS (secure) server.
SchemeTypeTURNS
)
// NewSchemeType defines a procedure for creating a new SchemeType from a raw
// string naming the scheme type.
func NewSchemeType(raw string) SchemeType {
switch raw {
case "stun":
return SchemeTypeSTUN
case "stuns":
return SchemeTypeSTUNS
case "turn":
return SchemeTypeTURN
case "turns":
return SchemeTypeTURNS
default:
return SchemeType(Unknown)
}
}
func (t SchemeType) String() string {
switch t {
case SchemeTypeSTUN:
return "stun"
case SchemeTypeSTUNS:
return "stuns"
case SchemeTypeTURN:
return "turn"
case SchemeTypeTURNS:
return "turns"
default:
return ErrUnknownType.Error()
}
}
// ProtoType indicates the transport protocol type that is used in the ice.URL
// structure.
type ProtoType int
const (
// ProtoTypeUDP indicates the URL uses a UDP transport.
ProtoTypeUDP ProtoType = iota + 1
// ProtoTypeTCP indicates the URL uses a TCP transport.
ProtoTypeTCP
)
// NewProtoType defines a procedure for creating a new ProtoType from a raw
// string naming the transport protocol type.
func NewProtoType(raw string) ProtoType {
switch raw {
case "udp":
return ProtoTypeUDP
case "tcp":
return ProtoTypeTCP
default:
return ProtoType(Unknown)
}
}
func (t ProtoType) String() string {
switch t {
case ProtoTypeUDP:
return "udp"
case ProtoTypeTCP:
return "tcp"
default:
return ErrUnknownType.Error()
}
}
// URL represents a STUN (rfc7064) or TURN (rfc7065) URL
type URL struct {
Scheme SchemeType
Host string
Port int
Username string
Password string
Proto ProtoType
}
// ParseURL parses a STUN or TURN urls following the ABNF syntax described in
// https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065
// respectively.
func ParseURL(raw string) (*URL, error) { //nolint:gocognit
rawParts, err := url.Parse(raw)
if err != nil {
return nil, err
}
var u URL
u.Scheme = NewSchemeType(rawParts.Scheme)
if u.Scheme == SchemeType(Unknown) {
return nil, ErrSchemeType
}
var rawPort string
if u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque); err != nil {
if e, ok := err.(*net.AddrError); ok {
if e.Err == "missing port in address" {
nextRawURL := u.Scheme.String() + ":" + rawParts.Opaque
switch {
case u.Scheme == SchemeTypeSTUN || u.Scheme == SchemeTypeTURN:
nextRawURL += ":3478"
if rawParts.RawQuery != "" {
nextRawURL += "?" + rawParts.RawQuery
}
return ParseURL(nextRawURL)
case u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS:
nextRawURL += ":5349"
if rawParts.RawQuery != "" {
nextRawURL += "?" + rawParts.RawQuery
}
return ParseURL(nextRawURL)
}
}
}
return nil, err
}
if u.Host == "" {
return nil, ErrHost
}
if u.Port, err = strconv.Atoi(rawPort); err != nil {
return nil, ErrPort
}
switch u.Scheme {
case SchemeTypeSTUN:
qArgs, err := url.ParseQuery(rawParts.RawQuery)
if err != nil || len(qArgs) > 0 {
return nil, ErrSTUNQuery
}
u.Proto = ProtoTypeUDP
case SchemeTypeSTUNS:
qArgs, err := url.ParseQuery(rawParts.RawQuery)
if err != nil || len(qArgs) > 0 {
return nil, ErrSTUNQuery
}
u.Proto = ProtoTypeTCP
case SchemeTypeTURN:
proto, err := parseProto(rawParts.RawQuery)
if err != nil {
return nil, err
}
u.Proto = proto
if u.Proto == ProtoType(Unknown) {
u.Proto = ProtoTypeUDP
}
case SchemeTypeTURNS:
proto, err := parseProto(rawParts.RawQuery)
if err != nil {
return nil, err
}
u.Proto = proto
if u.Proto == ProtoType(Unknown) {
u.Proto = ProtoTypeTCP
}
}
return &u, nil
}
func parseProto(raw string) (ProtoType, error) {
qArgs, err := url.ParseQuery(raw)
if err != nil || len(qArgs) > 1 {
return ProtoType(Unknown), ErrInvalidQuery
}
var proto ProtoType
if rawProto := qArgs.Get("transport"); rawProto != "" {
if proto = NewProtoType(rawProto); proto == ProtoType(0) {
return ProtoType(Unknown), ErrProtoType
}
return proto, nil
}
if len(qArgs) > 0 {
return ProtoType(Unknown), ErrInvalidQuery
}
return proto, nil
}
func (u URL) String() string {
rawURL := u.Scheme.String() + ":" + net.JoinHostPort(u.Host, strconv.Itoa(u.Port))
if u.Scheme == SchemeTypeTURN || u.Scheme == SchemeTypeTURNS {
rawURL += "?transport=" + u.Proto.String()
}
return rawURL
}
// IsSecure returns whether the this URL's scheme describes secure scheme or not.
func (u URL) IsSecure() bool {
return u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS
}