Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Content-Length to determine SIP message size #1148

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 45 additions & 15 deletions layers/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ var compactSipHeadersCorrespondance = map[string]string{
// -> The SIP Response code (if it's a response)
// -> The SIP Status line (if it's a response)
// You can easily know the type of the packet with the IsResponse boolean
//
type SIP struct {
BaseLayer

Expand Down Expand Up @@ -220,6 +219,7 @@ func decodeSIP(data []byte, p gopacket.PacketBuilder) error {
func NewSIP() *SIP {
s := new(SIP)
s.Headers = make(map[string][]string)
s.contentLength = -1
return s
}

Expand Down Expand Up @@ -297,20 +297,51 @@ func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {

countLines++
}
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}

s.setBaseLayer(data, offset, df)

return nil
}

// setBaseLayer is used to set the base layer of the SIP packet.
//
// According to "RFC3261 - 20.14 - Content-Length" the Content-Length header is mandatory
// for SIP packages transported over TCP. When transporting over UDP, the message
// body length CAN be determined by the length of the UDP datagram.
//
// So by using the Content-Length header, if present we can mark the packet as truncated if we do not
// have enough data. We are also able to limit the payload to the number bytes actually specified.
// If the header is not present, we can assume that the packet is transported over UDP we can then
// use the length of the UDP datagram as payload length (in this context this is the length of data).
func (s *SIP) setBaseLayer(data []byte, offset int, df gopacket.DecodeFeedback) {
// The content-length header was not present in the packet, we use the rest of the packet as payload
if s.contentLength == -1 {
s.contentLength = int64(len(data) - offset) // we should be able to trust contentLength even if not set
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}
} else if s.contentLength == 0 {
// We have a zero Content-Length, no payload
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: []byte{}} // no payload
} else if len(data) < offset+int(s.contentLength) {
// Not enough data to fulfill the Content-Length. We set the packet as truncated
// and return what we have. The receiver of the packet will be able to determine this
// by comparing the SIP.ContentLength with the length of the SIP.Payload.
df.SetTruncated()
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}
} else {
// we have at least enough data, to fulfill the Content-Length. But we only add the number
// of bytes specified in the Content-Length header to the payload.
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset : offset+int(s.contentLength)]}
}
}

// ParseFirstLine will compute the first line of a SIP packet.
// The first line will tell us if it's a request or a response.
//
// Examples of first line of SIP Prococol :
//
// Request : INVITE [email protected] SIP/2.0
// Response : SIP/2.0 200 OK
// Response : SIP/2.0 501 Not Implemented
// Examples of first line of SIP Protocol :
//
// Request : INVITE [email protected] SIP/2.0
// Response : SIP/2.0 200 OK
// Response : SIP/2.0 501 Not Implemented
func (s *SIP) ParseFirstLine(firstLine []byte) error {

var err error
Expand Down Expand Up @@ -372,13 +403,12 @@ func (s *SIP) ParseFirstLine(firstLine []byte) error {
//
// Examples of header :
//
// CSeq: 1 REGISTER
// Via: SIP/2.0/UDP there.com:5060
// Authorization:Digest username="UserB",
// realm="MCI WorldCom SIP",
// nonce="1cec4341ae6cbe5a359ea9c8e88df84f", opaque="",
// uri="sip:ss2.wcom.com", response="71ba27c64bd01de719686aa4590d5824"
//
// CSeq: 1 REGISTER
// Via: SIP/2.0/UDP there.com:5060
// Authorization:Digest username="UserB",
// realm="MCI WorldCom SIP",
// nonce="1cec4341ae6cbe5a359ea9c8e88df84f", opaque="",
// uri="sip:ss2.wcom.com", response="71ba27c64bd01de719686aa4590d5824"
func (s *SIP) ParseHeader(header []byte) (err error) {

// Ignore empty headers
Expand Down Expand Up @@ -529,7 +559,7 @@ func (s *SIP) GetUserAgent() string {
return s.GetFirstHeader("User-Agent")
}

// GetContentLength will return the parsed integer
// GetContentLength will return the parsed integer, or the actual payload size if the Contenet-Length header is missing
// Content-Length header of the current SIP packet
func (s *SIP) GetContentLength() int64 {
return s.contentLength
Expand Down
56 changes: 49 additions & 7 deletions layers/sip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package layers

import (
"bytes"
"testing"

"github.com/google/gopacket"
Expand All @@ -26,7 +27,6 @@ import (
// Expires:1800
// User-Agent:C530 IP/42.245.00.000.000
// Content-Length:0
//
var testPacketSIPRequest = []byte{
0x00, 0x07, 0x7d, 0x41, 0x2e, 0x40, 0x00, 0xd0, 0x03, 0x75, 0xe0, 0x00, 0x08, 0x00, 0x45, 0x00,
0x01, 0xf4, 0x73, 0x74, 0x00, 0x00, 0x75, 0x11, 0xca, 0x7f, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
Expand Down Expand Up @@ -74,7 +74,6 @@ var testPacketSIPRequest = []byte{
// Contact: <sip:[email protected]:5060>;expires=1800
// P-Associated-URI: <sip:[email protected]>
// Content-Length:0
//
var testPacketSIPResponse = []byte{
0x00, 0xd0, 0x00, 0x4a, 0x2c, 0x00, 0x00, 0x07, 0x7d, 0x41, 0x2e, 0x40, 0x08, 0x00, 0x45, 0x00,
0x01, 0xc1, 0x00, 0x00, 0x40, 0x00, 0x3f, 0x11, 0x34, 0x27, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
Expand Down Expand Up @@ -147,20 +146,54 @@ var testPacketSIPCompactInvite = []byte{
0x30, 0x3e, 0x0d, 0x0a, 0x6c, 0x3a, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
}

// Fourth packet is an INVITE with a payload
// This only contains the SIP Content and Payload, for more readability of the SIP layer
var testPacketSIPOnlyInviteWithPayload = []byte(
"INVITE sip:sip.provider.com SIP/2.0\r\n" +
"Via: SIP/2.0/TCP 172.16.254.66:5060;branch=z9hG4bK.xIOVvIsyy;rport\r\n" +
"From: \"Bob\" <sip:[email protected]>\r\n" +
"To: \"Alice\" <sip:[email protected]>\r\n" +
"Call-Id: 306366781@172_16_254_66\r\n" +
"CSeq: 1 INVITE\r\n" +
"Allow: INVITE,ACK,CANCEL,BYE,OPTIONS,INFO,SUBSCRIBE,NOTIFY,REFER,UPDATE\r\n" +
"Contact: <sip:[email protected]:5060>\r\n" +
"Content-Type: application/sdp\r\n" +
"Content-Length: 100\r\n" +
"\r\n" +
"v=0\r\n" +
"o=bob 4096 1976 IN IP4 172.16.254.66\r\n" +
"s=Talk\r\n" +
"c=IN 172.16.254.66\r\n" +
"t=0 0\r\n" +
"m=audio 6000 RTP/AVP 0",
)

// Fifth packet is an INVITE with an oversized payload
// This packet is generated by appending an extra attribute to the payload of the previous packet
var testOversizedPacketSIPOnlyInviteWithPayload = append(testPacketSIPOnlyInviteWithPayload, []byte("a=This_is_beyond_the_content_length\r\n")...)

func TestSIPMain(t *testing.T) {
expectedHeaders := map[string]string{"Call-ID": "306366781@172_16_254_66", "Contact": "<sip:[email protected]:5060>"}
_TestPacketSIP(t, testPacketSIPRequest, SIPMethodRegister, false, 3, expectedHeaders, "sip:sip.provider.com")
_TestPacketSIP(t, LinkTypeEthernet, testPacketSIPRequest, SIPMethodRegister, false, 3, expectedHeaders, "sip:sip.provider.com", []byte{})

expectedHeaders = map[string]string{"Call-ID": "306366781@172_16_254_66", "Contact": "<sip:[email protected]:5060>;expires=1800"}
_TestPacketSIP(t, testPacketSIPResponse, SIPMethodRegister, true, 3, expectedHeaders, "")
_TestPacketSIP(t, LinkTypeEthernet, testPacketSIPResponse, SIPMethodRegister, true, 3, expectedHeaders, "", []byte{})

expectedHeaders = map[string]string{"Call-ID": "306366781@172_16_254_66", "Contact": "<sip:[email protected]:5060>", "f": "\"Bob\" <sip:[email protected]>"}
_TestPacketSIP(t, testPacketSIPCompactInvite, SIPMethodInvite, false, 1, expectedHeaders, "sip:sip.provider.com")
_TestPacketSIP(t, LinkTypeEthernet, testPacketSIPCompactInvite, SIPMethodInvite, false, 1, expectedHeaders, "sip:sip.provider.com", []byte{})

expectedHeaders = map[string]string{"Call-ID": "306366781@172_16_254_66", "Contact": "<sip:[email protected]:5060>", "From": "\"Bob\" <sip:[email protected]>", "Content-Type": "application/sdp", "Content-Length": "100"}
expectedPayload := []byte("v=0\r\no=bob 4096 1976 IN IP4 172.16.254.66\r\ns=Talk\r\nc=IN 172.16.254.66\r\nt=0 0\r\nm=audio 6000 RTP/AVP 0")
_TestPacketSIP(t, LayerTypeSIP, testPacketSIPOnlyInviteWithPayload, SIPMethodInvite, false, 1, expectedHeaders, "sip:sip.provider.com", expectedPayload)

expectedHeaders = map[string]string{"Call-ID": "306366781@172_16_254_66", "Contact": "<sip:[email protected]:5060>", "From": "\"Bob\" <sip:[email protected]>", "Content-Type": "application/sdp", "Content-Length": "100"}
expectedPayload = []byte("v=0\r\no=bob 4096 1976 IN IP4 172.16.254.66\r\ns=Talk\r\nc=IN 172.16.254.66\r\nt=0 0\r\nm=audio 6000 RTP/AVP 0")
_TestPacketSIP(t, LayerTypeSIP, testOversizedPacketSIPOnlyInviteWithPayload, SIPMethodInvite, false, 1, expectedHeaders, "sip:sip.provider.com", expectedPayload)
}

func _TestPacketSIP(t *testing.T, packetData []byte, methodWanted SIPMethod, isResponse bool, wantedCseq int64, expectedHeaders map[string]string, expectedRequestURI string) {
func _TestPacketSIP(t *testing.T, firstLayerDecoder gopacket.Decoder, packetData []byte, methodWanted SIPMethod, isResponse bool, wantedCseq int64, expectedHeaders map[string]string, expectedRequestURI string, expectedPayload []byte) {

p := gopacket.NewPacket(packetData, LinkTypeEthernet, gopacket.Default)
p := gopacket.NewPacket(packetData, firstLayerDecoder, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
Expand Down Expand Up @@ -195,5 +228,14 @@ func _TestPacketSIP(t *testing.T, packetData []byte, methodWanted SIPMethod, isR
if got.GetCSeq() != wantedCseq {
t.Errorf("SIP Packet should be %d. Got : %d", wantedCseq, got.GetCSeq())
}

if got.contentLength != int64(len(got.Payload())) {
t.Errorf("SIP Payload length should be %d. Got : %d", got.contentLength, len(got.Payload()))
}

// Check payload
if bytes.Compare(got.Payload(), expectedPayload) != 0 {
t.Errorf("SIP Payload should be:\n\t%v\nGot:\n\t%v", expectedPayload, got.Payload())
}
}
}