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.
383 lines
8.5 KiB
Go
383 lines
8.5 KiB
Go
package icspacketparser
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gitlab.com/ics_cinnamon/voicegateway/icserror"
|
|
"gitlab.com/ics_cinnamon/voicegateway/icsnet"
|
|
)
|
|
|
|
type IcsSipMethod int
|
|
|
|
const (
|
|
ICS_SIP_METHOD_REGISTER IcsSipMethod = iota
|
|
ICS_SIP_METHOD_INVITE
|
|
ICS_SIP_METHOD_PUBLISH
|
|
ICS_SIP_METHOD_OPTIONS
|
|
ICS_SIP_METHOD_INFO
|
|
ICS_SIP_METHOD_UPDATE
|
|
ICS_SIP_METHOD_REFER
|
|
ICS_SIP_METHOD_SUBSCRIBE
|
|
ICS_SIP_METHOD_MESSAGE
|
|
ICS_SIP_METHOD_NOTIFY
|
|
ICS_SIP_METHOD_PRACK
|
|
ICS_SIP_METHOD_ACK
|
|
ICS_SIP_METHOD_BYE
|
|
ICS_SIP_METHOD_CANCEL
|
|
ICS_SIP_METHOD_SIP20 //response
|
|
ICS_SIP_METHOD_NOT_FOUND
|
|
)
|
|
|
|
func (arr IcsSipMethod) String() string {
|
|
var strSipMethod = []string{
|
|
"ICS_SIP_METHOD_REGISTER",
|
|
"ICS_SIP_METHOD_INVITE",
|
|
"ICS_SIP_METHOD_PUBLISH",
|
|
"ICS_SIP_METHOD_OPTIONS",
|
|
"ICS_SIP_METHOD_INFO",
|
|
"ICS_SIP_METHOD_UPDATE",
|
|
"ICS_SIP_METHOD_REFER",
|
|
"ICS_SIP_METHOD_SUBSCRIBE",
|
|
"ICS_SIP_METHOD_MESSAGE",
|
|
"ICS_SIP_METHOD_NOTIFY",
|
|
"ICS_SIP_METHOD_PRACK",
|
|
"ICS_SIP_METHOD_ACK",
|
|
"ICS_SIP_METHOD_BYE",
|
|
"ICS_SIP_METHOD_CANCEL",
|
|
"ICS_SIP_METHOD_SIP20",
|
|
"ICS_SIP_METHOD_NOT_FOUND",
|
|
}
|
|
|
|
return strSipMethod[int(arr)%len(strSipMethod)]
|
|
}
|
|
|
|
type SIP struct {
|
|
Headers map[string][]string
|
|
|
|
Method IcsSipMethod
|
|
Version string
|
|
Source string
|
|
|
|
Via []string
|
|
MaxForwards int64
|
|
Contact string
|
|
To string
|
|
// ToTag string
|
|
From string
|
|
// FromTag string
|
|
RecordRoute string
|
|
RequestURI string
|
|
CallID string
|
|
UserAgent string
|
|
Allow string
|
|
Supported string
|
|
Cseq string
|
|
|
|
ContentLength int64
|
|
ContentType string
|
|
|
|
ResType string
|
|
|
|
XAICall string
|
|
|
|
// direction bool
|
|
|
|
Content *SDP
|
|
|
|
SrcAddr *icsnet.IcsNetAddr
|
|
DstAddr *icsnet.IcsNetAddr
|
|
}
|
|
|
|
func NewSIP() (sip SIP) {
|
|
return sip
|
|
}
|
|
|
|
func NewSDP() (sdp SDP) {
|
|
return sdp
|
|
}
|
|
|
|
func NewSDPMedia() (sdpMedia SDPMedia) {
|
|
return sdpMedia
|
|
}
|
|
|
|
var sdpMediaType = []string{"m", "a"}
|
|
|
|
func findSdpMedia(mediaType string) bool {
|
|
for _, value := range sdpMediaType {
|
|
if value == mediaType {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (s *SIP) SipParser(byteArr []byte) (icserr *icserror.IcsError) {
|
|
strSipLine := strings.Split(string(byteArr), "\r\n")
|
|
|
|
isParserSIP := true
|
|
|
|
sdp := NewSDP()
|
|
sdpMedia := SDPMedia{}
|
|
sdpMedias := []SDPMedia{}
|
|
|
|
for line := range strSipLine {
|
|
//fmt.Println("strSipLine[line]", len(strings.TrimSpace(strSipLine[line])), isParserSIP, strings.TrimSpace(strSipLine[line]))
|
|
if line == 0 {
|
|
s.parseFirstLineHeader(strings.TrimSpace(strSipLine[line]))
|
|
} else {
|
|
if isParserSIP && len(strings.TrimSpace(strSipLine[line])) == 0 {
|
|
isParserSIP = false
|
|
} else if !isParserSIP && len(strings.TrimSpace(strSipLine[line])) == 0 {
|
|
break
|
|
} else if !isParserSIP { //parse SDP
|
|
if len(strings.TrimSpace(strSipLine[line])) > 0 {
|
|
if len(strings.TrimSpace(strSipLine[line])) == 0 {
|
|
//fmt.Println("RRRR", line)
|
|
return nil
|
|
}
|
|
|
|
splits := strings.SplitN(string(strings.TrimSpace(strSipLine[line])), "=", 2)
|
|
|
|
if len(splits) < 2 {
|
|
//fmt.Printf("RRRR22 strSipLine[line] : [%s]\n", strSipLine[line])
|
|
//fmt.Printf("RRRR22 strSipLine len: %d, line len: %d, byteArr: {%s}, SipLine:##### {%v}\n",
|
|
//len(strSipLine), line, string(byteArr), strSipLine)
|
|
break
|
|
//return nil
|
|
}
|
|
|
|
name := strings.TrimSpace(splits[0])
|
|
value := strings.TrimSpace(splits[1])
|
|
|
|
//fmt.Println("@@@@", name, value)
|
|
if findSdpMedia(name) {
|
|
sdpMedia.setSdpMediaStruct(name, value)
|
|
} else {
|
|
sdp.setSdpStruct(name, value)
|
|
}
|
|
}
|
|
} else {
|
|
s.parseSip(strings.TrimSpace(strSipLine[line]))
|
|
}
|
|
}
|
|
}
|
|
sdpMedias = append(sdpMedias, sdpMedia)
|
|
if isExistSdpMedia(sdpMedias) {
|
|
//fmt.Println("$$$$", sdpMedias)
|
|
//fmt.Printf("MediaDescription: %s", sdpMedias[0].MediaDescription)
|
|
sdp.Media = &sdpMedias
|
|
}
|
|
|
|
// fmt.Println("sdp.Version", sdp.Version)
|
|
// fmt.Println("sdp.Owner", sdp.Owner)
|
|
// fmt.Println("sdp.Session", sdp.Session)
|
|
// fmt.Println("sdp.Connection", sdp.Connection)
|
|
|
|
if isExistSdp(sdp) {
|
|
s.Content = &sdp
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isExistSdpMedia(sdpMedias []SDPMedia) bool {
|
|
for _, value := range sdpMedias {
|
|
if len(value.MediaDescription) == 0 {
|
|
return false
|
|
}
|
|
// TODO Media만 존재하고, attributes 가 없는 경우 처리 방법
|
|
// for _, val := range value.Attributes {
|
|
// fmt.Println(val)
|
|
// }
|
|
}
|
|
return true
|
|
}
|
|
|
|
func isExistSdp(sdp SDP) bool {
|
|
if sdp.Version == 0 && sdp.Owner == "" && sdp.Session == "" && sdp.Connection == "" {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *SIP) parseFirstLineHeader(strSipLine string) (icserr *icserror.IcsError) {
|
|
// fmt.Println(">>>>>>>>>>>parseFirstLineHeader", strSipLine)
|
|
splits := strings.SplitN(strSipLine, " ", 3)
|
|
|
|
if len(splits) < 3 {
|
|
return icserror.ICSERRSIPHeader
|
|
}
|
|
|
|
icsSipMethod, err := GetSipMethod(splits[0], splits[1])
|
|
if icsSipMethod == ICS_SIP_METHOD_SIP20 {
|
|
s.ResType = splits[1]
|
|
}
|
|
if err != nil {
|
|
return icserror.ICSERRSIPHeader
|
|
}
|
|
s.Method = icsSipMethod
|
|
s.Version = splits[2]
|
|
s.Source = splits[1]
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetSipMethod(strMethod, strSource string) (icsSipMethod IcsSipMethod, icserr *icserror.IcsError) {
|
|
for method := IcsSipMethod(0); method < ICS_SIP_METHOD_NOT_FOUND; method++ {
|
|
switch strings.ToUpper(strMethod) {
|
|
case "REGISTER":
|
|
return ICS_SIP_METHOD_REGISTER, nil
|
|
case "INVITE":
|
|
return ICS_SIP_METHOD_INVITE, nil
|
|
case "SIP/2.0":
|
|
return ICS_SIP_METHOD_SIP20, nil
|
|
case "PRACK":
|
|
return ICS_SIP_METHOD_PRACK, nil
|
|
case "ACK":
|
|
return ICS_SIP_METHOD_ACK, nil
|
|
case "BYE":
|
|
return ICS_SIP_METHOD_BYE, nil
|
|
case "REFER":
|
|
return ICS_SIP_METHOD_REFER, nil
|
|
case "NOTIFY":
|
|
return ICS_SIP_METHOD_NOTIFY, nil
|
|
case "CANCEL":
|
|
return ICS_SIP_METHOD_CANCEL, nil
|
|
case "INFO":
|
|
return ICS_SIP_METHOD_INFO, nil
|
|
case "OPTIONS":
|
|
return ICS_SIP_METHOD_OPTIONS, nil
|
|
case "SUBSCRIBE":
|
|
return ICS_SIP_METHOD_SUBSCRIBE, nil
|
|
case "PUBLISH":
|
|
return ICS_SIP_METHOD_PUBLISH, nil
|
|
case "UPDATE":
|
|
return ICS_SIP_METHOD_UPDATE, nil
|
|
}
|
|
}
|
|
return ICS_SIP_METHOD_NOT_FOUND, nil
|
|
}
|
|
|
|
func (s *SIP) parseSip(data string) (icserr *icserror.IcsError) {
|
|
if len(data) == 0 {
|
|
return nil
|
|
}
|
|
|
|
splits := strings.SplitN(string(data), ":", 2)
|
|
|
|
if len(splits) < 2 {
|
|
return nil
|
|
}
|
|
|
|
name := strings.TrimSpace(splits[0])
|
|
value := strings.TrimSpace(splits[1])
|
|
|
|
s.setSipStruct(name, value)
|
|
return nil
|
|
}
|
|
|
|
func (s *SIP) setSipStruct(name string, value string) (icserr *icserror.IcsError) {
|
|
switch strings.ToUpper(name) {
|
|
case "VIA":
|
|
s.Via = append(s.Via, value)
|
|
return nil
|
|
case "MAX-FORWARDS":
|
|
numValue, err := strconv.ParseInt(value, 10, 64)
|
|
if err != nil {
|
|
return icserror.ICSERRSIPHeader
|
|
}
|
|
s.MaxForwards = numValue
|
|
return nil
|
|
case "FROM":
|
|
s.From = value
|
|
|
|
// splits := strings.SplitN(string(value), "tag=", 2)
|
|
|
|
// if len(splits) < 2 {
|
|
// s.FromTag = ""
|
|
// } else {
|
|
// s.FromTag = strings.TrimSpace(splits[1])
|
|
// }
|
|
|
|
return nil
|
|
case "TO":
|
|
s.To = value
|
|
return nil
|
|
case "CALL-ID":
|
|
s.CallID = value
|
|
return nil
|
|
case "CSEQ":
|
|
// splits := strings.SplitN(string(value), " ", 2)
|
|
// numValue, err := strconv.ParseInt(strings.TrimSpace(splits[0]), 10, 64)
|
|
// if err != nil {
|
|
// return icserror.ICSERRSIPHeader
|
|
// }
|
|
// s.Cseq = numValue
|
|
s.Cseq = strings.ToUpper(value)
|
|
return nil
|
|
case "CONTACT":
|
|
s.Contact = value
|
|
return nil
|
|
case "SUPPORTED":
|
|
s.Supported = value
|
|
return nil
|
|
case "ALLOW":
|
|
s.Allow = value
|
|
return nil
|
|
case "USER-AGENT":
|
|
s.UserAgent = value
|
|
return nil
|
|
case "CONTENT-TYPE":
|
|
s.ContentType = value
|
|
return nil
|
|
case "CONTENT-LENGTH":
|
|
numValue, err := strconv.ParseInt(value, 10, 64)
|
|
if err != nil {
|
|
return icserror.ICSERRSIPHeader
|
|
}
|
|
s.ContentLength = numValue
|
|
return nil
|
|
case "X-AICALL":
|
|
s.XAICall = value
|
|
default:
|
|
values := strings.Split(value, ";")
|
|
|
|
// 헤더 값에 값을 없을 경우 초기화.. map이라서??
|
|
if len(s.Headers) == 0 {
|
|
s.Headers = map[string][]string{}
|
|
}
|
|
for _, fieldValue := range values {
|
|
s.Headers[name] = append(s.Headers[name], strings.TrimSpace(fieldValue))
|
|
}
|
|
//fmt.Println("SIP~~~~~~~~~~~~~~~~~whoAU~~~~~~~~~~~~~~~~~" + value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//implm interface Packeter
|
|
func (s *SIP) GetPacketData(addr []*icsnet.IcsNetAddr, packet []byte) *icserror.IcsError {
|
|
s.SrcAddr = addr[0]
|
|
s.DstAddr = addr[1]
|
|
return s.SipParser(packet)
|
|
|
|
//fmt.Println("SIP GetPacketData()", s.SrcAddr, s.DstAddr)
|
|
}
|
|
|
|
func (s *SIP) GetCallID() string {
|
|
return s.CallID
|
|
}
|
|
|
|
func (s *SIP) GetURI() string {
|
|
if s.Method != ICS_SIP_METHOD_SIP20 {
|
|
uris := strings.SplitN(s.Source, ";", 2)
|
|
if len(uris[0]) > 0 {
|
|
agenturis := strings.SplitN(uris[0], ":", 3)
|
|
//fmt.Println(">>>", uris, s.Method)
|
|
return agenturis[1]
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|