Skip to content

Commit 54ed701

Browse files
committed
pr review
1 parent 545a7e0 commit 54ed701

File tree

4 files changed

+254
-38
lines changed

4 files changed

+254
-38
lines changed

protocol/sip/helper.go

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,23 @@ import (
2020
"net"
2121
"strconv"
2222
"strings"
23+
"time"
2324

2425
"github.com/emiago/sipgo/sip"
2526
"github.com/google/uuid"
2627
"github.com/vulncheck-oss/go-exploit/output"
2728
"github.com/vulncheck-oss/go-exploit/protocol"
29+
"github.com/vulncheck-oss/go-exploit/random"
2830
)
2931

3032
const (
3133
// SIP over UDP message size should be lower than 1300 bytes.
3234
// https://datatracker.ietf.org/doc/html/rfc3261#section-18.1.1
33-
UDPMessageLength = 1300
34-
errMsgRequiredParam = "Required parameter: %s"
35-
DefaultMaxForwards = 70
36-
DefaultCSeq = 1
37-
DefaultInviteContentType = "application/sdp"
38-
DefaultInviteBodyContent = `v=0
39-
o=caller 123456 654321 IN IP4 192.168.1.100
40-
s=-
41-
c=IN IP4 192.168.1.100
42-
t=0 0
43-
m=audio 5004 RTP/AVP 0
44-
a=rtpmap:0 PCMU/8000`
35+
UDPMessageLength = 1300
36+
errMsgRequiredParam = "Required parameter: %s"
37+
DefaultMaxForwards = 70
38+
DefaultCSeq = 1
39+
DefaultInviteContentType = "application/sdp"
4540
DefaultMessageContentType = "text/plain"
4641
DefaultMessageBodyContent = "Hello, this is a test message."
4742
DefaultInfoContentType = "application/dtmf-relay"
@@ -50,26 +45,6 @@ a=rtpmap:0 PCMU/8000`
5045
DefaultRackCSeq = 1
5146
DefaultRackInviteCSeq = 314159
5247
ContentTypePidf = "application/pidf+xml"
53-
DefaultPublishBodyContent = `<?xml version="1.0" encoding="UTF-8"?>
54-
<presence xmlns="urn:ietf:params:xml:ns:pidf"
55-
entity="sip:[email protected]:5060">
56-
<tuple id="sg89ae">
57-
<status>
58-
<basic>open</basic>
59-
</status>
60-
</tuple>
61-
</presence>
62-
`
63-
DefaultNotifyBodyContent = `<?xml version="1.0"?>
64-
<presence xmlns="urn:ietf:params:xml:ns:pidf"
65-
<tuple id="sg89ae">
66-
<status>
67-
<basic>open</basic>
68-
</status>
69-
<contact>sip:[email protected]</contact>
70-
<timestamp>2023-06-15T14:23:00Z</timestamp>
71-
</tuple>
72-
</presence>`
7348
// Even though the specification requires "\r\n", it is common to see
7449
// implementations using "\n" as a line ending.
7550
// https://datatracker.ietf.org/doc/html/rfc3261#section-7.5
@@ -301,7 +276,7 @@ func NewSipRequest(
301276
}
302277
callID, err := uuid.NewRandom()
303278
if err != nil {
304-
output.PrintfFrameworkError("Generating UUID: %s", err.Error())
279+
output.PrintfFrameworkError("Could not generate UUID: %s", err.Error())
305280

306281
return nil, false
307282
}
@@ -491,19 +466,103 @@ func AddMethodHeaders(req *sip.Request, method sip.RequestMethod) bool {
491466
func NewRequestBody(method sip.RequestMethod) (string, string) {
492467
switch method {
493468
case sip.INVITE:
494-
return DefaultInviteBodyContent, DefaultInviteContentType
469+
return NewDefaultInviteBody("", "", ""), DefaultInviteContentType
495470
case sip.MESSAGE:
496471
return DefaultMessageBodyContent, DefaultMessageContentType
497472
case sip.INFO:
498473
return DefaultInfoBodyContent, DefaultInfoContentType
499474
case sip.PUBLISH:
500-
return DefaultPublishBodyContent, ContentTypePidf
475+
return NewDefaultPublishBody("", "", ""), ContentTypePidf
501476
case sip.NOTIFY:
502-
return DefaultNotifyBodyContent, ContentTypePidf
477+
return NewDefaultNotifyBody("", "", "", ""), ContentTypePidf
503478
case sip.ACK, sip.CANCEL, sip.BYE, sip.REGISTER, sip.OPTIONS, sip.SUBSCRIBE, sip.REFER, sip.PRACK, sip.UPDATE:
504479

505480
return "", ""
506481
}
507482

508483
return "", ""
509484
}
485+
486+
// Returns a default body for an INVITE request.
487+
//
488+
// Default parameters:
489+
// - host: "randomIPv4()".
490+
// - sessid: random digits.
491+
// - sessver: random digits.
492+
func NewDefaultInviteBody(host, sessid, sessver string) string {
493+
if host == "" {
494+
host = random.RandIPv4().String()
495+
}
496+
if sessid == "" {
497+
sessid = random.RandDigits(6)
498+
}
499+
if sessver == "" {
500+
sessver = random.RandDigits(6)
501+
}
502+
return fmt.Sprintf(`v=0
503+
o=caller %s %s IN IP4 %s
504+
s=-
505+
c=IN IP4 %s
506+
t=0 0
507+
m=audio 5004 RTP/AVP 0
508+
a=rtpmap:0 PCMU/8000`, sessid, sessver, host, host)
509+
}
510+
511+
// Returns a default body for a PUBLISH request.
512+
//
513+
// Default parameters:
514+
// - id: random letters.
515+
// - status: "open".
516+
// - entity: "sip:bob@randomIPv4():5060".
517+
func NewDefaultPublishBody(id, status, entity string) string {
518+
if id == "" {
519+
id = random.RandLetters(6)
520+
}
521+
if status == "" {
522+
status = "open"
523+
}
524+
if entity == "" {
525+
entity = fmt.Sprintf("sip:bob@%s:5060", random.RandIPv4())
526+
}
527+
return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
528+
<presence xmlns="urn:ietf:params:xml:ns:pidf"
529+
entity="%s">
530+
<tuple id="%s">
531+
<status>
532+
<basic>%s</basic>
533+
</status>
534+
</tuple>
535+
</presence>`, entity, id, status)
536+
}
537+
538+
// Returns a default body for a NOTIFY request.
539+
//
540+
// Default parameters:
541+
// - id: random letters.
542+
// - status: "open".
543+
// - contact: "sip:bob@randomIPv4():5060".
544+
// - ts: current UTC time in RFC 3339 format.
545+
func NewDefaultNotifyBody(id, status, contact, ts string) string {
546+
if id == "" {
547+
id = random.RandLetters(6)
548+
}
549+
if status == "" {
550+
status = "open"
551+
}
552+
if contact == "" {
553+
contact = fmt.Sprintf("sip:bob@%s:5060", random.RandIPv4())
554+
}
555+
if ts == "" {
556+
ts = time.Now().UTC().Format(time.RFC3339)
557+
}
558+
return fmt.Sprintf(`<?xml version="1.0"?>
559+
<presence xmlns="urn:ietf:params:xml:ns:pidf">
560+
<tuple id="%s">
561+
<status>
562+
<basic>%s</basic>
563+
</status>
564+
<contact>%s</contact>
565+
<timestamp>%s</timestamp>
566+
</tuple>
567+
</presence>`, id, status, contact, ts)
568+
}

protocol/sip/helper_test.go

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ func TestNewSipRequest(t *testing.T) {
305305
if req.Body() == nil {
306306
t.Fatal("got 'nil' body")
307307
}
308-
expectedBody := "o=caller 123456 654321 IN IP4 192.168.1.100"
308+
expectedBody := "o=caller"
309309
if !strings.Contains(string(req.Body()), expectedBody) {
310310
t.Fatalf("got body %s, want %s", req.Body(), expectedBody)
311311
}
@@ -652,7 +652,7 @@ func TestNewRequestBody(t *testing.T) {
652652
{
653653
"INVITE",
654654
sip.INVITE,
655-
"o=caller 123456 654321 IN IP4 192.168.1.100",
655+
"o=caller",
656656
"application/sdp",
657657
},
658658
{
@@ -699,3 +699,113 @@ func TestNewRequestBody(t *testing.T) {
699699
})
700700
}
701701
}
702+
703+
func TestNewDefaultInviteBody(t *testing.T) {
704+
t.Run("returns a valid body", func(t *testing.T) {
705+
body := NewDefaultInviteBody("", "", "")
706+
if body == "" {
707+
t.Fatal("got empty")
708+
}
709+
if !strings.Contains(body, "o=caller") {
710+
t.Fatalf("got %s, want o=caller", body)
711+
}
712+
if !strings.Contains(body, "s=-") {
713+
t.Fatalf("got %s, want s=-", body)
714+
}
715+
if !strings.Contains(body, "c=IN IP4") {
716+
t.Fatalf("got %s, want c=IN IP4", body)
717+
}
718+
expected := `t=0 0
719+
m=audio 5004 RTP/AVP 0
720+
a=rtpmap:0 PCMU/8000`
721+
if !strings.Contains(body, expected) {
722+
t.Fatalf("got %s, want %s", body, expected)
723+
}
724+
})
725+
t.Run("returns a valid body with custom parameters", func(t *testing.T) {
726+
body := NewDefaultInviteBody("host-0", "sess-0", "sessver-0")
727+
expected := `v=0
728+
o=caller sess-0 sessver-0 IN IP4 host-0
729+
s=-
730+
c=IN IP4 host-0
731+
t=0 0
732+
m=audio 5004 RTP/AVP 0
733+
a=rtpmap:0 PCMU/8000`
734+
if body != expected {
735+
t.Fatalf("got %s, want %s", body, expected)
736+
}
737+
})
738+
}
739+
740+
func TestNewDefaultPublishBody(t *testing.T) {
741+
t.Run("returns a valid body", func(t *testing.T) {
742+
body := NewDefaultPublishBody("", "", "")
743+
if body == "" {
744+
t.Fatal("got empty")
745+
}
746+
expected := `<?xml version="1.0" encoding="UTF-8"?>
747+
<presence xmlns="urn:ietf:params:xml:ns:pidf"`
748+
if !strings.Contains(body, expected) {
749+
t.Fatalf("got %s, want %s", body, expected)
750+
}
751+
if !strings.Contains(body, "<tuple id=") {
752+
t.Fatalf("got %s, want <tuple id=", body)
753+
}
754+
expected = `<status>
755+
<basic>open</basic>
756+
</status>
757+
</tuple>
758+
</presence>`
759+
if !strings.Contains(body, expected) {
760+
t.Fatalf("got %s, want %s", body, expected)
761+
}
762+
})
763+
t.Run("returns a valid body with custom parameters", func(t *testing.T) {
764+
body := NewDefaultPublishBody("id-0", "status-0", "entity-0")
765+
expected := `<?xml version="1.0" encoding="UTF-8"?>
766+
<presence xmlns="urn:ietf:params:xml:ns:pidf"
767+
entity="entity-0">
768+
<tuple id="id-0">
769+
<status>
770+
<basic>status-0</basic>
771+
</status>
772+
</tuple>
773+
</presence>`
774+
if body != expected {
775+
t.Fatalf("got %s, want %s", body, expected)
776+
}
777+
})
778+
}
779+
780+
func TestNewDefaultNotifyBody(t *testing.T) {
781+
t.Run("returns a valid body", func(t *testing.T) {
782+
body := NewDefaultNotifyBody("", "", "", "")
783+
if body == "" {
784+
t.Fatal("got empty")
785+
}
786+
expected := `<?xml version="1.0"?>
787+
<presence xmlns="urn:ietf:params:xml:ns:pidf">
788+
<tuple id="`
789+
if !strings.Contains(body, expected) {
790+
t.Fatalf("got %s, want %s", body, expected)
791+
}
792+
expected = `<status>
793+
<basic>open</basic>
794+
</status>
795+
<contact>sip:bob@`
796+
if !strings.Contains(body, expected) {
797+
t.Fatalf("got %s, want %s", body, expected)
798+
}
799+
expected = `</contact>
800+
<timestamp>`
801+
if !strings.Contains(body, expected) {
802+
t.Fatalf("got %s, want %s", body, expected)
803+
}
804+
expected = `</timestamp>
805+
</tuple>
806+
</presence>`
807+
if !strings.Contains(body, expected) {
808+
t.Fatalf("got %s, want %s", body, expected)
809+
}
810+
})
811+
}

random/random.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package random
33
import (
44
"crypto/rand"
55
"math/big"
6+
"net"
67
"strings"
78

89
"github.com/vulncheck-oss/go-exploit/output"
@@ -140,3 +141,22 @@ var CommonTLDs = []string{
140141
"org",
141142
"net",
142143
}
144+
145+
// RandIPv4 generates a random IPv4 address.
146+
func RandIPv4() net.IP {
147+
return net.IPv4(
148+
byte(RandIntRange(1, 256)),
149+
byte(RandIntRange(1, 256)),
150+
byte(RandIntRange(1, 256)),
151+
byte(RandIntRange(1, 256)),
152+
)
153+
}
154+
155+
// RandIPv6 generates a random IPv6 address.
156+
func RandIPv6() net.IP {
157+
ip := make(net.IP, net.IPv6len)
158+
for i := range net.IPv6len {
159+
ip[i] = byte(RandIntRange(1, 256))
160+
}
161+
return ip
162+
}

random/random_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package random
22

33
import (
4+
"net"
45
"strings"
56
"testing"
67
)
@@ -165,3 +166,29 @@ func Test_RandEmail(t *testing.T) {
165166
}
166167
}
167168
}
169+
170+
func TestRandIPv4(t *testing.T) {
171+
for range 100 {
172+
r := RandIPv4()
173+
parsed := net.ParseIP(r.String())
174+
if parsed == nil {
175+
t.Error("Generated IP is nil: " + r.String())
176+
}
177+
if parsed.To4() == nil {
178+
t.Error("Generated IP is not a valid IPv4 address: " + r.String())
179+
}
180+
}
181+
}
182+
183+
func TestRandIPv6(t *testing.T) {
184+
for range 100 {
185+
r := RandIPv6()
186+
parsed := net.ParseIP(r.String())
187+
if parsed == nil {
188+
t.Error("Generated IP is nil: " + r.String())
189+
}
190+
if parsed.To4() != nil {
191+
t.Error("Generated IP is not a valid IPv6 address: " + r.String())
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)