You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
445 lines
9.9 KiB
Go
445 lines
9.9 KiB
Go
package sipasm
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
uuid "github.com/satori/go.uuid"
|
|
"gitlab.com/ics_cinnamon/voicegateway/icspacketparser"
|
|
)
|
|
|
|
const (
|
|
MAX_HEADER_OVERLAP int = 100
|
|
MAX_CSEQ uint32 = 4294967294
|
|
)
|
|
|
|
const (
|
|
SDP_SENDRECV = 0
|
|
SDP_SENDONLY = 1
|
|
SDP_RECVONLY = 2
|
|
)
|
|
|
|
///////////////////////////////
|
|
type headerOverlap struct {
|
|
length int
|
|
headers [MAX_HEADER_OVERLAP]string
|
|
}
|
|
|
|
func NewHeaderOverlap() *headerOverlap {
|
|
var h headerOverlap
|
|
h.length = 0
|
|
|
|
return &h
|
|
}
|
|
|
|
func (h *headerOverlap) AddHeader(msg string) {
|
|
h.headers[h.length] = msg
|
|
h.length++
|
|
}
|
|
|
|
// if idx is less than 0, then returns last header
|
|
func (h headerOverlap) GetHeader(idx int) string {
|
|
if idx < 0 {
|
|
return h.headers[h.length]
|
|
} else {
|
|
return h.headers[idx]
|
|
}
|
|
}
|
|
|
|
func (h headerOverlap) GetLength() int {
|
|
return h.length
|
|
}
|
|
|
|
/////////////////////////////////
|
|
type SIPMessage struct {
|
|
Method string
|
|
header map[ICSSIP_HEADER_TYPE]*headerOverlap
|
|
|
|
HeaderString string
|
|
BodyString string
|
|
|
|
branch string
|
|
tag string
|
|
|
|
recvedSIP *icspacketparser.SIP
|
|
|
|
SDP *SDPMessage
|
|
}
|
|
|
|
func NewSIPMessage(methodType ICSSIP_METHOD_TYPE, value string) *SIPMessage {
|
|
if methodType >= ICSSIP_METHOD_MAX {
|
|
return nil
|
|
}
|
|
|
|
var sipmessage SIPMessage
|
|
|
|
sipmessage.header = make(map[ICSSIP_HEADER_TYPE]*headerOverlap)
|
|
for iter := 0; iter < MAX_HEADER_OVERLAP; iter++ {
|
|
sipmessage.header[ICSSIP_HEADER_TYPE(iter)] = NewHeaderOverlap()
|
|
}
|
|
|
|
msg := fmt.Sprintf("%s SIP/2.0", value)
|
|
sipmessage.Method = fmt.Sprintf(ICSSIP_METHOD_STRING[methodType], msg)
|
|
|
|
return &sipmessage
|
|
}
|
|
|
|
func NewSIPResponse(sip *icspacketparser.SIP, statusCode int) *SIPMessage {
|
|
var sipmessage SIPMessage
|
|
|
|
sipmessage.header = make(map[ICSSIP_HEADER_TYPE]*headerOverlap)
|
|
for iter := 0; iter < MAX_HEADER_OVERLAP; iter++ {
|
|
sipmessage.header[ICSSIP_HEADER_TYPE(iter)] = NewHeaderOverlap()
|
|
}
|
|
|
|
sipmessage.recvedSIP = sip
|
|
|
|
msg := ICSSIP_STATUSCODE_STRING[ICSSIP_STATUSCODE_TYPE(statusCode)]
|
|
allow := "REGISTER,OPTIONS,INVITE,ACK,CANCEL,BYE,NOTIFY,PRACK,REFER,INFO,SUBSCRIBE,UPDATE"
|
|
|
|
//msg := fmt.Sprintf("%s", ICSSIP_STATUSCODE_STRING[ICSSIP_STATUSCODE_TYPE(statusCode)])
|
|
sipmessage.Method = fmt.Sprintf(ICSSIP_METHOD_STRING[ICSSIP_METHOD_SIP20], msg)
|
|
for iter := 0; iter < len(sip.Via); iter++ {
|
|
sipmessage.AddSIPHeader(ICSSIP_HEADER_VIA, sip.Via[iter])
|
|
}
|
|
//maxforwards := fmt.Sprintf("%d", sip.MaxForwards)
|
|
//sipmessage.AddSIPHeader(ICSSIP_HEADER_MAX_FORWARDS, maxforwards)
|
|
sipmessage.AddSIPHeader(ICSSIP_HEADER_FROM, sip.From)
|
|
var toTag string
|
|
if statusCode != 100 && !strings.Contains(sip.To, ";tag") {
|
|
toTag = fmt.Sprintf("%s;tag=%s", sip.To, GenerateTag())
|
|
sipmessage.AddSIPHeader(ICSSIP_HEADER_TO, toTag)
|
|
//fmt.Printf("##### sip.to: %s, toTag: %s\n", sip.To, toTag)
|
|
} else {
|
|
sipmessage.AddSIPHeader(ICSSIP_HEADER_TO, sip.To)
|
|
}
|
|
sipmessage.AddSIPHeader(ICSSIP_HEADER_CALL_ID, sip.CallID)
|
|
sipmessage.AddSIPHeader(ICSSIP_HEADER_CSEQ, sip.Cseq)
|
|
sipmessage.AddSIPHeader(ICSSIP_HEADER_ALLOW, allow)
|
|
|
|
return &sipmessage
|
|
}
|
|
|
|
/////////////////////////////////
|
|
//SIP operator
|
|
func (s *SIPMessage) AddSIPHeader(headerType ICSSIP_HEADER_TYPE, value ...interface{}) string {
|
|
if headerType >= ICSSIP_HEADER_MAX {
|
|
return ""
|
|
}
|
|
|
|
var msg string
|
|
if len(value) <= 0 {
|
|
msg = fmt.Sprint(ICSSIP_HEADER_STRING[headerType])
|
|
} else {
|
|
msg = fmt.Sprintf(ICSSIP_HEADER_STRING[headerType], value...)
|
|
//fmt.Printf(ICSSIP_HEADER_STRING[headerType], value...)
|
|
}
|
|
//fmt.Printf(">>>>%s, %s\n", ICSSIP_HEADER_STRING[headerType], value...)
|
|
|
|
h := s.header[headerType]
|
|
h.AddHeader(msg)
|
|
|
|
return h.GetHeader(-1)
|
|
}
|
|
|
|
func (s SIPMessage) SearchSIPHeader(headerType ICSSIP_HEADER_TYPE) *headerOverlap {
|
|
if headerType < ICSSIP_HEADER_TYPE(0) {
|
|
return nil
|
|
}
|
|
|
|
h := s.header[headerType]
|
|
|
|
length := h.GetLength()
|
|
for iter := 0; iter < length; iter++ {
|
|
f := h.GetHeader(iter)
|
|
if strings.Contains(ICSSIP_HEADER_STRING[headerType], f) {
|
|
return h
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s SIPMessage) SearchSIPHeaderWithString(headerName string) *headerOverlap {
|
|
if len(headerName) <= 0 {
|
|
return nil
|
|
}
|
|
|
|
MSG := strings.ToUpper(headerName)
|
|
htype := ICSSIP_HEADER_STRING2TYPE[MSG]
|
|
h := s.header[htype]
|
|
|
|
length := h.GetLength()
|
|
for iter := 0; iter < length; iter++ {
|
|
f := h.GetHeader(iter)
|
|
if strings.Contains(ICSSIP_HEADER_STRING[htype], f) {
|
|
return h
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
//if idxOfOverlap is less than 0, then deletes whole the HeaderOverlap for that headerType
|
|
func (s *SIPMessage) DelSIPHeader(headerType ICSSIP_HEADER_TYPE, idxOfOverlap int) bool {
|
|
if headerType < ICSSIP_HEADER_TYPE(0) {
|
|
return false
|
|
}
|
|
|
|
if idxOfOverlap < 0 {
|
|
h := s.SearchSIPHeader(headerType)
|
|
if h == nil {
|
|
return false
|
|
}
|
|
length := h.GetLength()
|
|
for iter := 0; iter < length; iter++ {
|
|
h.headers[iter] = ""
|
|
}
|
|
h.length = 0
|
|
} else {
|
|
h := s.SearchSIPHeader(headerType)
|
|
if h == nil {
|
|
return false
|
|
}
|
|
h.headers[idxOfOverlap] = ""
|
|
h.length--
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
//replaces a header to point the headerType to value. it replace only first(0th) overlapped header in the HeaderOverlap
|
|
//DO NOT USE for VIA header
|
|
func (s *SIPMessage) ReplaceSIPHeader(headerType ICSSIP_HEADER_TYPE, value string) string {
|
|
if headerType < ICSSIP_HEADER_TYPE(0) {
|
|
return ""
|
|
}
|
|
|
|
if len(value) <= 0 {
|
|
return ""
|
|
}
|
|
|
|
h := s.SearchSIPHeader(headerType)
|
|
h.headers[0] = value
|
|
|
|
return h.headers[0]
|
|
}
|
|
|
|
func (s *SIPMessage) GetSIPHeader(headerType ICSSIP_HEADER_TYPE) (headers *[MAX_HEADER_OVERLAP]string) {
|
|
if headerType < ICSSIP_HEADER_TYPE(0) {
|
|
return headers
|
|
}
|
|
|
|
h := s.header[headerType].headers
|
|
return &h
|
|
}
|
|
|
|
func (s SIPMessage) String() (message string) {
|
|
if len(s.Method) == 0 {
|
|
return ""
|
|
}
|
|
|
|
message = s.Method
|
|
for iter := 0; iter < int(ICSSIP_HEADER_MAX); iter++ {
|
|
h := s.header[ICSSIP_HEADER_TYPE(iter)]
|
|
length := h.GetLength()
|
|
for iter2 := 0; iter2 < length; iter2++ {
|
|
msg := h.GetHeader(iter2)
|
|
if len(msg) != 0 {
|
|
message += msg
|
|
}
|
|
}
|
|
}
|
|
|
|
if s.SDP != nil {
|
|
message += s.SDP.String()
|
|
}
|
|
|
|
/* TODO: add payload to message
|
|
if len(s.Header[ICSSIP_HEADER_CONTENT_TYPE]) != 0 {
|
|
}
|
|
*/
|
|
|
|
return message
|
|
}
|
|
|
|
//////////////////////////////////
|
|
// SDP operator
|
|
type SDPMessage struct {
|
|
connection string
|
|
origin string
|
|
sessionName string
|
|
media []*SDPMedia
|
|
}
|
|
|
|
type SDPMedia struct {
|
|
mediaType ICSSDP_MEDIA_TYPE
|
|
port int
|
|
rtpmap []*RTPMap
|
|
}
|
|
|
|
type RTPMap struct {
|
|
payloadNum int
|
|
ptime int
|
|
fmtp string
|
|
sendrecv string
|
|
}
|
|
|
|
//only audio media allowed
|
|
func NewSDPMessage(ip string, port int, audFormats []string, sendrecv int) *SDPMessage {
|
|
var sdpMessage SDPMessage
|
|
|
|
sessID := time.Now().Local().Unix()
|
|
sessVer := sessID + 1
|
|
oLine := fmt.Sprintf("AudiocodesGW-AGENT %d %d IN IP4 %s", sessID, sessVer, ip)
|
|
connection := fmt.Sprintf("IN IP4 %s", ip)
|
|
|
|
sdpMessage = SDPMessage{connection: connection,
|
|
origin: oLine,
|
|
sessionName: ""}
|
|
|
|
sdpMessage.media = make([]*SDPMedia, 1)
|
|
sdpMessage.media[0] = NewSDPMedia(ICSSDP_MEDIA_AUDIO, port, audFormats, sendrecv)
|
|
|
|
return &sdpMessage
|
|
}
|
|
|
|
func (s SDPMessage) String() string {
|
|
sdp := fmt.Sprintf("v=0\r\no=%s\r\ns=%s\r\nc=%s\r\nt=0 0\r\n%s", s.origin, s.sessionName, s.connection, s.media[0].String())
|
|
|
|
return sdp
|
|
}
|
|
|
|
func NewSDPMedia(audvid ICSSDP_MEDIA_TYPE, port int, audFormats []string, sendrecv int) *SDPMedia {
|
|
sdpMedia := SDPMedia{mediaType: audvid,
|
|
port: port}
|
|
|
|
fmtLen := len(audFormats)
|
|
sdpMedia.rtpmap = make([]*RTPMap, fmtLen+1)
|
|
|
|
strSendrecv := "sendrecv"
|
|
if sendrecv == SDP_SENDONLY {
|
|
strSendrecv = "sendonly"
|
|
} else if sendrecv == SDP_RECVONLY {
|
|
strSendrecv = "recvonly"
|
|
}
|
|
|
|
var iter int
|
|
for iter = 0; iter < fmtLen; iter++ {
|
|
format := strings.ToUpper(audFormats[iter])
|
|
//fmt.Println("set rtpmap>>>", format)
|
|
switch format {
|
|
case "PCMU":
|
|
sdpMedia.rtpmap[iter] = NewRTPMap(0, 20, "", strSendrecv)
|
|
case "PCMA":
|
|
sdpMedia.rtpmap[iter] = NewRTPMap(8, 20, "", strSendrecv)
|
|
case "G729":
|
|
fallthrough
|
|
case "G.729":
|
|
sdpMedia.rtpmap[iter] = NewRTPMap(18, 20, "annexb=no", strSendrecv)
|
|
default:
|
|
}
|
|
}
|
|
//telephone event
|
|
sdpMedia.rtpmap[iter] = NewRTPMap(101, 20, "0-15", "sendrecv")
|
|
|
|
return &sdpMedia
|
|
}
|
|
|
|
func (m SDPMedia) String() string {
|
|
var media string
|
|
|
|
var payloadNum string
|
|
var rtpmap string
|
|
plen := len(m.rtpmap)
|
|
for iter := 0; iter < plen; iter++ {
|
|
payloadNum += fmt.Sprintf("%d ", m.rtpmap[iter].payloadNum)
|
|
rtpmap += m.rtpmap[iter].String()
|
|
}
|
|
media = fmt.Sprintf("m=audio %d RTP/AVP %s\r\n%s", m.port, payloadNum, rtpmap)
|
|
|
|
return media
|
|
}
|
|
|
|
func NewRTPMap(payloadNum int, ptime int, fmtp string, sendrecv string) *RTPMap {
|
|
rtpMap := RTPMap{
|
|
payloadNum: payloadNum,
|
|
ptime: ptime,
|
|
fmtp: fmtp,
|
|
sendrecv: sendrecv,
|
|
}
|
|
|
|
return &rtpMap
|
|
}
|
|
|
|
func (r RTPMap) String() string {
|
|
var name string
|
|
switch r.payloadNum {
|
|
case 0:
|
|
name = "PCMU"
|
|
case 8:
|
|
name = "PCMA"
|
|
case 18:
|
|
name = "G729"
|
|
case 101:
|
|
name = "telephone-event"
|
|
}
|
|
|
|
var rtpmap string
|
|
if r.payloadNum == 101 {
|
|
rtpmap = fmt.Sprintf("a=rtpmap:%d %s/8000\r\na=fmtp:%d %s\r\n",
|
|
r.payloadNum, name, r.payloadNum, r.fmtp)
|
|
} else {
|
|
if len(r.fmtp) > 0 {
|
|
rtpmap = fmt.Sprintf("a=rtpmap:%d %s/8000\r\na=fmtp:%d %s\r\na=ptime:%d\r\na=%s\r\n",
|
|
r.payloadNum, name, r.payloadNum, r.fmtp, r.ptime, r.sendrecv)
|
|
} else {
|
|
rtpmap = fmt.Sprintf("a=rtpmap:%d %s/8000\r\na=ptime:%d\r\na=%s\r\n",
|
|
r.payloadNum, name, r.ptime, r.sendrecv)
|
|
}
|
|
}
|
|
return rtpmap
|
|
}
|
|
|
|
//////////////////////////////////
|
|
func GenerateCallID(postfix string) (callid string) {
|
|
uuid := uuid.NewV4()
|
|
callid = fmt.Sprintf("%s%s", uuid.String(), postfix)
|
|
|
|
return callid
|
|
}
|
|
|
|
func GenerateBranch() (branch string) {
|
|
uuid := uuid.NewV4()
|
|
branch = base64.StdEncoding.EncodeToString([]byte(uuid.String()))
|
|
|
|
b := []byte(branch)
|
|
length := len(b)
|
|
for iter := 0; iter < length; iter++ {
|
|
if b[iter] == 0x3d {
|
|
b[iter] = '-'
|
|
}
|
|
}
|
|
branch = string(b)
|
|
|
|
return branch
|
|
}
|
|
|
|
func GenerateTag() (tag string) {
|
|
now := fmt.Sprintf("%d", time.Now().UnixNano())
|
|
t := base64.StdEncoding.EncodeToString([]byte(now))
|
|
|
|
b := []byte(t)
|
|
length := len(b)
|
|
for iter := 0; iter < length; iter++ {
|
|
if b[iter] == 0x3d {
|
|
b[iter] = '-'
|
|
}
|
|
}
|
|
tag = string(b)
|
|
|
|
return tag
|
|
}
|