// SIP Via Address Library
|
|
|
|
package sip
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"github.com/jart/gosip/util"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
ViaBadHeader = errors.New("Bad Via header")
|
|
ViaProtoBlank = errors.New("Via Proto blank")
|
|
)
|
|
|
|
// Example: SIP/2.0/UDP 1.2.3.4:5060;branch=z9hG4bK556f77e6.
|
|
type Via struct {
|
|
Version string // protocol version e.g. "2.0"
|
|
Proto string // transport type "UDP"
|
|
Host string // name or ip of egress interface
|
|
Port uint16 // network port number
|
|
Params Params // params like branch, received, rport, etc.
|
|
Next *Via // pointer to next via header if any
|
|
}
|
|
|
|
// Parses a single SIP Via header, provided the part that comes after "Via: ".
|
|
//
|
|
// Via headers are goofy; they're like a URI without a scheme, user, and pass.
|
|
// They tell about the network interfaces a packet traversed to reach us.
|
|
//
|
|
// EBNF: http://sofia-sip.sourceforge.net/refdocs/sip/group__sip__via.html
|
|
func ParseVia(s string) (via *Via, err error) {
|
|
if s[0:4] == "SIP/" {
|
|
s = s[4:]
|
|
if n1 := strings.Index(s, "/"); n1 > 0 {
|
|
if n2 := strings.Index(s, " "); n2 >= n1+3 {
|
|
via = new(Via)
|
|
via.Version = s[0:n1]
|
|
via.Proto = s[n1+1 : n2]
|
|
s, via.Host, via.Port, err = extractHostPort(s[n2+1:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if s != "" && s[0] == ';' {
|
|
via.Params = parseParams(s[1:])
|
|
s = ""
|
|
}
|
|
return via, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, ViaBadHeader
|
|
}
|
|
|
|
func (via *Via) Append(b *bytes.Buffer) error {
|
|
if via.Version == "" {
|
|
via.Version = "2.0"
|
|
}
|
|
if via.Proto == "" {
|
|
via.Proto = "UDP"
|
|
}
|
|
if via.Port == 0 {
|
|
via.Port = 5060
|
|
}
|
|
if via.Host == "" {
|
|
return ViaProtoBlank
|
|
}
|
|
b.WriteString("SIP/")
|
|
b.WriteString(via.Version)
|
|
b.WriteString("/")
|
|
b.WriteString(via.Proto)
|
|
b.WriteString(" ")
|
|
b.WriteString(via.Host)
|
|
if via.Port != 5060 {
|
|
b.WriteString(":")
|
|
b.WriteString(strconv.Itoa(int(via.Port)))
|
|
}
|
|
via.Params.Append(b)
|
|
return nil
|
|
}
|
|
|
|
// Copy returns a deep copy of via.
|
|
func (via *Via) Copy() *Via {
|
|
if via == nil {
|
|
return nil
|
|
}
|
|
res := new(Via)
|
|
res.Version = via.Version
|
|
res.Proto = via.Proto
|
|
res.Host = via.Host
|
|
res.Port = via.Port
|
|
res.Params = via.Params.Copy()
|
|
res.Next = via.Next.Copy()
|
|
return res
|
|
}
|
|
|
|
// Branch mutates via with a newly generated branch ID.
|
|
func (via *Via) Branch() *Via {
|
|
via.Params["branch"] = util.GenerateBranch()
|
|
return via
|
|
}
|
|
|
|
// Detach returns a shallow copy of via with Next set to nil.
|
|
func (via *Via) Detach() *Via {
|
|
res := new(Via)
|
|
*res = *via
|
|
res.Next = nil
|
|
return res
|
|
}
|
|
|
|
// Last returns pointer to last via in linked list.
|
|
func (via *Via) Last() *Via {
|
|
if via != nil {
|
|
for ; via.Next != nil; via = via.Next {
|
|
}
|
|
}
|
|
return via
|
|
}
|
|
|
|
func (via *Via) CompareHostPort(other *Via) bool {
|
|
if via != nil && other != nil {
|
|
if via.Host == other.Host &&
|
|
via.Port == other.Port {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (via *Via) CompareBranch(other *Via) bool {
|
|
if via != nil && other != nil {
|
|
if b1, ok := via.Params["branch"]; ok {
|
|
if b2, ok := other.Params["branch"]; ok {
|
|
if b1 == b2 {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|