package stun
import (
"errors"
"net"
"net/url"
"strconv"
)
var (
ErrUnknownType = errors .New ("Unknown" )
ErrSchemeType = errors .New ("unknown scheme type" )
ErrSTUNQuery = errors .New ("queries not supported in stun address" )
ErrInvalidQuery = errors .New ("invalid query" )
ErrHost = errors .New ("invalid hostname" )
ErrPort = errors .New ("invalid port" )
ErrProtoType = errors .New ("invalid transport protocol type" )
)
type SchemeType int
const (
SchemeTypeUnknown SchemeType = iota
SchemeTypeSTUN
SchemeTypeSTUNS
SchemeTypeTURN
SchemeTypeTURNS
)
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 SchemeTypeUnknown
}
}
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()
}
}
type ProtoType int
const (
ProtoTypeUnknown ProtoType = iota
ProtoTypeUDP
ProtoTypeTCP
)
func NewProtoType (raw string ) ProtoType {
switch raw {
case "udp" :
return ProtoTypeUDP
case "tcp" :
return ProtoTypeTCP
default :
return ProtoTypeUnknown
}
}
func (t ProtoType ) String () string {
switch t {
case ProtoTypeUDP :
return "udp"
case ProtoTypeTCP :
return "tcp"
default :
return ErrUnknownType .Error()
}
}
type URI struct {
Scheme SchemeType
Host string
Port int
Username string
Password string
Proto ProtoType
}
func ParseURI (raw string ) (*URI , error ) {
rawParts , err := url .Parse (raw )
if err != nil {
return nil , err
}
var u URI
u .Scheme = NewSchemeType (rawParts .Scheme )
if u .Scheme == SchemeTypeUnknown {
return nil , ErrSchemeType
}
var rawPort string
if u .Host , rawPort , err = net .SplitHostPort (rawParts .Opaque ); err != nil {
var e *net .AddrError
if errors .As (err , &e ) {
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 ParseURI (nextRawURL )
case u .Scheme == SchemeTypeSTUNS || u .Scheme == SchemeTypeTURNS :
nextRawURL += ":5349"
if rawParts .RawQuery != "" {
nextRawURL += "?" + rawParts .RawQuery
}
return ParseURI (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 == ProtoTypeUnknown {
u .Proto = ProtoTypeUDP
}
case SchemeTypeTURNS :
proto , err := parseProto (rawParts .RawQuery )
if err != nil {
return nil , err
}
u .Proto = proto
if u .Proto == ProtoTypeUnknown {
u .Proto = ProtoTypeTCP
}
case SchemeTypeUnknown :
}
return &u , nil
}
func parseProto(raw string ) (ProtoType , error ) {
qArgs , err := url .ParseQuery (raw )
if err != nil || len (qArgs ) > 1 {
return ProtoTypeUnknown , ErrInvalidQuery
}
var proto ProtoType
if rawProto := qArgs .Get ("transport" ); rawProto != "" {
if proto = NewProtoType (rawProto ); proto == ProtoType (0 ) {
return ProtoTypeUnknown , ErrProtoType
}
return proto , nil
}
if len (qArgs ) > 0 {
return ProtoTypeUnknown , ErrInvalidQuery
}
return proto , nil
}
func (u URI ) 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
}
func (u URI ) IsSecure () bool {
return u .Scheme == SchemeTypeSTUNS || u .Scheme == SchemeTypeTURNS
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .