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 }