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

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 ""
}