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

3 years ago
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
}