diff --git a/example/echo/echo_test.go b/example/echo/echo_test.go index 0a6195a..4598ee8 100755 --- a/example/echo/echo_test.go +++ b/example/echo/echo_test.go @@ -204,7 +204,7 @@ func TestCallToEchoApp(t *testing.T) { Port: uint16(laddr.Port), }, }, - UserAgent: "gosip/1.o", + UserAgent: []byte("gosip/1.o"), Payload: sdp.New(rtpaddr, sdp.ULAWCodec, sdp.DTMFCodec), } diff --git a/example/options/options_test.go b/example/options/options_test.go index 511d024..a4a5e9c 100755 --- a/example/options/options_test.go +++ b/example/options/options_test.go @@ -26,8 +26,8 @@ func TestOptions(t *testing.T) { CallID: util.GenerateCallID(), Method: "OPTIONS", CSeqMethod: "OPTIONS", - Accept: "application/sdp", - UserAgent: "pokémon/1.o", + Accept: []byte("application/sdp"), + UserAgent: []byte("pokémon/1.o"), Request: &sip.URI{ Scheme: "sip", User: "echo", @@ -84,7 +84,7 @@ func TestOptions(t *testing.T) { if !msg.IsResponse() || msg.Status != 200 || msg.Phrase != "OK" { t.Error("Not OK :[") } - if options.CallID != msg.CallID { + if !bytes.Equal(options.CallID, msg.CallID) { t.Error("CallID didnt match") } if options.CSeq != msg.CSeq || options.CSeqMethod != msg.CSeqMethod { diff --git a/example/rawsip/rawsip_test.go b/example/rawsip/rawsip_test.go index be703c3..7537c49 100755 --- a/example/rawsip/rawsip_test.go +++ b/example/rawsip/rawsip_test.go @@ -41,7 +41,7 @@ func TestRawSIPOptions(t *testing.T) { "Max-Forwards: 70\r\n" + "To: \r\n" + "From: ;tag=" + fromtag + "\r\n" + - "Call-ID: " + callid + "\r\n" + + "Call-ID: " + string(callid) + "\r\n" + "CSeq: " + strconv.Itoa(cseq) + " OPTIONS\r\n" + "Contact: \r\n" + "User-Agent: pokémon/1.o\r\n" + diff --git a/sip/dialog.go b/sip/dialog.go index b6007a3..0c306cf 100755 --- a/sip/dialog.go +++ b/sip/dialog.go @@ -248,7 +248,7 @@ func (dls *dialogState) handleMessage(msg *Msg) bool { dls.errChan <- errors.New("Remote UA is using a strange SIP version") return false } - if msg.CallID != dls.request.CallID { + if !bytes.Equal(msg.CallID, dls.request.CallID) { log.Printf("Received message doesn't match dialog") return dls.send(NewResponse(msg, StatusCallTransactionDoesNotExist)) } diff --git a/sip/messages.go b/sip/messages.go index 5963d43..3f23907 100644 --- a/sip/messages.go +++ b/sip/messages.go @@ -20,7 +20,7 @@ func NewRequest(tp *Transport, method string, to, from *Addr) *Msg { CallID: util.GenerateCallID(), CSeq: util.GenerateCSeq(), CSeqMethod: method, - UserAgent: GosipUA, + UserAgent: []byte(GosipUA), } } @@ -35,8 +35,8 @@ func NewResponse(msg *Msg, status int) *Msg { CSeq: msg.CSeq, CSeqMethod: msg.CSeqMethod, RecordRoute: msg.RecordRoute, - UserAgent: GosipUA, - Allow: GosipAllow, + UserAgent: []byte(GosipUA), + Allow: []byte(GosipAllow), } } @@ -54,7 +54,7 @@ func NewAck(msg, invite *Msg) *Msg { Route: msg.RecordRoute.Reversed(), Authorization: invite.Authorization, ProxyAuthorization: invite.ProxyAuthorization, - UserAgent: GosipUA, + UserAgent: []byte(GosipUA), } } diff --git a/sip/msg.go b/sip/msg.go index 1144d3e..5a4fdcc 100755 --- a/sip/msg.go +++ b/sip/msg.go @@ -32,52 +32,56 @@ type Msg struct { Route *Addr // Used for goose routing and loose routing RecordRoute *Addr // Used for loose routing Contact *Addr // Where we send response packets or nil - CallID string // Identifies call from invite to bye + CallID []byte // Identifies call from INVITE to BYE; never empty, only absent CSeq int // Counter for network packet ordering CSeqMethod string // Helps with matching to orig message MaxForwards int // 0 has context specific meaning - UserAgent string + UserAgent []byte // Name of the SIP stack // All the other RFC 3261 headers in plus some extras. - Accept string - AcceptContact string - AcceptEncoding string - AcceptLanguage string - AlertInfo string - Allow string - AllowEvents string - AuthenticationInfo string - Authorization string - CallInfo string - ContentDisposition string - ContentEncoding string - ContentLanguage string - Date string - ErrorInfo string - Event string + // + // These byte slices will be nil if absent, in which case the header never + // appeared. If they are empty, then the header did appear, but without a + // value. + Accept []byte + AcceptContact []byte + AcceptEncoding []byte + AcceptLanguage []byte + AlertInfo []byte + Allow []byte + AllowEvents []byte + AuthenticationInfo []byte + Authorization []byte + CallInfo []byte + ContentDisposition []byte + ContentEncoding []byte + ContentLanguage []byte + Date []byte + ErrorInfo []byte + Event []byte Expires int // Seconds registration should expire. - InReplyTo string - MIMEVersion string + InReplyTo []byte + MIMEVersion []byte MinExpires int // Registrars need this when responding - Organization string + Organization []byte PAssertedIdentity *Addr // P-Asserted-Identity or nil (used for PSTN ANI) - Priority string - ProxyAuthenticate string - ProxyAuthorization string - ProxyRequire string - ReferTo string - ReferredBy string + Priority []byte + ProxyAuthenticate []byte + ProxyAuthorization []byte + ProxyRequire []byte + ReferTo []byte + ReferredBy []byte RemotePartyID *Addr // Evil twin of P-Asserted-Identity. - ReplyTo string - Require string - RetryAfter string - Server string - Subject string - Supported string - Timestamp string - Unsupported string - WWWAuthenticate string - Warning string + ReplyTo []byte + Require []byte + RetryAfter []byte + Server []byte + Subject []byte + Supported []byte + Timestamp []byte + Unsupported []byte + WWWAuthenticate []byte + Warning []byte // Extension headers. Headers Headers @@ -139,14 +143,15 @@ func (msg *Msg) Append(b *bytes.Buffer) { msg.appendVersion(b) b.WriteString("\r\n") } else { - if msg.Phrase == "" { - msg.Phrase = Phrase(msg.Status) - } msg.appendVersion(b) b.WriteString(" ") b.WriteString(strconv.Itoa(msg.Status)) b.WriteString(" ") - b.WriteString(msg.Phrase) + if msg.Phrase == "" { + b.WriteString(Phrase(msg.Status)) + } else { + b.WriteString(msg.Phrase) + } b.WriteString("\r\n") } @@ -183,7 +188,7 @@ func (msg *Msg) Append(b *bytes.Buffer) { } b.WriteString("Call-ID: ") - b.WriteString(msg.CallID) + b.Write(msg.CallID) b.WriteString("\r\n") b.WriteString("CSeq: ") @@ -192,9 +197,9 @@ func (msg *Msg) Append(b *bytes.Buffer) { b.WriteString(msg.CSeqMethod) b.WriteString("\r\n") - if msg.UserAgent != "" { + if msg.UserAgent != nil { b.WriteString("User-Agent: ") - b.WriteString(msg.UserAgent) + b.Write(msg.UserAgent) b.WriteString("\r\n") } @@ -208,93 +213,93 @@ func (msg *Msg) Append(b *bytes.Buffer) { } } - if msg.Accept != "" { + if msg.Accept != nil { b.WriteString("Accept: ") - b.WriteString(msg.Accept) + b.Write(msg.Accept) b.WriteString("\r\n") } - if msg.AcceptEncoding != "" { + if msg.AcceptEncoding != nil { b.WriteString("Accept-Encoding: ") - b.WriteString(msg.AcceptEncoding) + b.Write(msg.AcceptEncoding) b.WriteString("\r\n") } - if msg.AcceptLanguage != "" { + if msg.AcceptLanguage != nil { b.WriteString("Accept-Language: ") - b.WriteString(msg.AcceptLanguage) + b.Write(msg.AcceptLanguage) b.WriteString("\r\n") } - if msg.AlertInfo != "" { + if msg.AlertInfo != nil { b.WriteString("Alert-Info: ") - b.WriteString(msg.AlertInfo) + b.Write(msg.AlertInfo) b.WriteString("\r\n") } - if msg.Allow != "" { + if msg.Allow != nil { b.WriteString("Allow: ") - b.WriteString(msg.Allow) + b.Write(msg.Allow) b.WriteString("\r\n") } - if msg.AllowEvents != "" { + if msg.AllowEvents != nil { b.WriteString("Allow-Events: ") - b.WriteString(msg.AllowEvents) + b.Write(msg.AllowEvents) b.WriteString("\r\n") } - if msg.AuthenticationInfo != "" { + if msg.AuthenticationInfo != nil { b.WriteString("Authentication-Info: ") - b.WriteString(msg.AuthenticationInfo) + b.Write(msg.AuthenticationInfo) b.WriteString("\r\n") } - if msg.Authorization != "" { + if msg.Authorization != nil { b.WriteString("Authorization: ") - b.WriteString(msg.Authorization) + b.Write(msg.Authorization) b.WriteString("\r\n") } - if msg.CallInfo != "" { + if msg.CallInfo != nil { b.WriteString("Call-Info: ") - b.WriteString(msg.CallInfo) + b.Write(msg.CallInfo) b.WriteString("\r\n") } - if msg.ContentDisposition != "" { + if msg.ContentDisposition != nil { b.WriteString("Content-Disposition: ") - b.WriteString(msg.ContentDisposition) + b.Write(msg.ContentDisposition) b.WriteString("\r\n") } - if msg.ContentEncoding != "" { + if msg.ContentEncoding != nil { b.WriteString("Content-Encoding: ") - b.WriteString(msg.ContentEncoding) + b.Write(msg.ContentEncoding) b.WriteString("\r\n") } - if msg.ContentLanguage != "" { + if msg.ContentLanguage != nil { b.WriteString("Content-Language: ") - b.WriteString(msg.ContentLanguage) + b.Write(msg.ContentLanguage) b.WriteString("\r\n") } - if msg.Date != "" { + if msg.Date != nil { b.WriteString("Date: ") - b.WriteString(msg.Date) + b.Write(msg.Date) b.WriteString("\r\n") } - if msg.ErrorInfo != "" { + if msg.ErrorInfo != nil { b.WriteString("Error-Info: ") - b.WriteString(msg.ErrorInfo) + b.Write(msg.ErrorInfo) b.WriteString("\r\n") } - if msg.Event != "" { + if msg.Event != nil { b.WriteString("Event: ") - b.WriteString(msg.Event) + b.Write(msg.Event) b.WriteString("\r\n") } @@ -305,15 +310,15 @@ func (msg *Msg) Append(b *bytes.Buffer) { b.WriteString("\r\n") } - if msg.InReplyTo != "" { + if msg.InReplyTo != nil { b.WriteString("In-Reply-To: ") - b.WriteString(msg.InReplyTo) + b.Write(msg.InReplyTo) b.WriteString("\r\n") } - if msg.MIMEVersion != "" { + if msg.MIMEVersion != nil { b.WriteString("MIME-Version: ") - b.WriteString(msg.MIMEVersion) + b.Write(msg.MIMEVersion) b.WriteString("\r\n") } @@ -323,9 +328,9 @@ func (msg *Msg) Append(b *bytes.Buffer) { b.WriteString("\r\n") } - if msg.Organization != "" { + if msg.Organization != nil { b.WriteString("Organization: ") - b.WriteString(msg.Organization) + b.Write(msg.Organization) b.WriteString("\r\n") } @@ -335,39 +340,39 @@ func (msg *Msg) Append(b *bytes.Buffer) { b.WriteString("\r\n") } - if msg.Priority != "" { + if msg.Priority != nil { b.WriteString("Priority: ") - b.WriteString(msg.Priority) + b.Write(msg.Priority) b.WriteString("\r\n") } - if msg.ProxyAuthenticate != "" { + if msg.ProxyAuthenticate != nil { b.WriteString("Proxy-Authenticate: ") - b.WriteString(msg.ProxyAuthenticate) + b.Write(msg.ProxyAuthenticate) b.WriteString("\r\n") } - if msg.ProxyAuthorization != "" { + if msg.ProxyAuthorization != nil { b.WriteString("Proxy-Authorization: ") - b.WriteString(msg.ProxyAuthorization) + b.Write(msg.ProxyAuthorization) b.WriteString("\r\n") } - if msg.ProxyRequire != "" { + if msg.ProxyRequire != nil { b.WriteString("Proxy-Require: ") - b.WriteString(msg.ProxyRequire) + b.Write(msg.ProxyRequire) b.WriteString("\r\n") } - if msg.ReferTo != "" { + if msg.ReferTo != nil { b.WriteString("Refer-To: ") - b.WriteString(msg.ReferTo) + b.Write(msg.ReferTo) b.WriteString("\r\n") } - if msg.ReferredBy != "" { + if msg.ReferredBy != nil { b.WriteString("Referred-By: ") - b.WriteString(msg.ReferredBy) + b.Write(msg.ReferredBy) b.WriteString("\r\n") } @@ -377,63 +382,63 @@ func (msg *Msg) Append(b *bytes.Buffer) { b.WriteString("\r\n") } - if msg.ReplyTo != "" { + if msg.ReplyTo != nil { b.WriteString("Reply-To: ") - b.WriteString(msg.ReplyTo) + b.Write(msg.ReplyTo) b.WriteString("\r\n") } - if msg.Require != "" { + if msg.Require != nil { b.WriteString("Require: ") - b.WriteString(msg.Require) + b.Write(msg.Require) b.WriteString("\r\n") } - if msg.RetryAfter != "" { + if msg.RetryAfter != nil { b.WriteString("RetryAfter: ") - b.WriteString(msg.RetryAfter) + b.Write(msg.RetryAfter) b.WriteString("\r\n") } - if msg.Server != "" { + if msg.Server != nil { b.WriteString("Server: ") - b.WriteString(msg.Server) + b.Write(msg.Server) b.WriteString("\r\n") } - if msg.Subject != "" { + if msg.Subject != nil { b.WriteString("Subject: ") - b.WriteString(msg.Subject) + b.Write(msg.Subject) b.WriteString("\r\n") } - if msg.Supported != "" { + if msg.Supported != nil { b.WriteString("Supported: ") - b.WriteString(msg.Supported) + b.Write(msg.Supported) b.WriteString("\r\n") } - if msg.Timestamp != "" { + if msg.Timestamp != nil { b.WriteString("Timestamp: ") - b.WriteString(msg.Timestamp) + b.Write(msg.Timestamp) b.WriteString("\r\n") } - if msg.Unsupported != "" { + if msg.Unsupported != nil { b.WriteString("Unsupported: ") - b.WriteString(msg.Unsupported) + b.Write(msg.Unsupported) b.WriteString("\r\n") } - if msg.Warning != "" { + if msg.Warning != nil { b.WriteString("Warning: ") - b.WriteString(msg.Warning) + b.Write(msg.Warning) b.WriteString("\r\n") } - if msg.WWWAuthenticate != "" { + if msg.WWWAuthenticate != nil { b.WriteString("WWW-Authenticate: ") - b.WriteString(msg.WWWAuthenticate) + b.Write(msg.WWWAuthenticate) b.WriteString("\r\n") } diff --git a/sip/msg_parse.go b/sip/msg_parse.go index ca785b8..d2a3f5f 100644 --- a/sip/msg_parse.go +++ b/sip/msg_parse.go @@ -61,7 +61,7 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) { ctype := "" var name string var hex byte - var value *string + var value *[]byte var via *Via var addrp **Addr var addr *Addr @@ -9591,7 +9591,7 @@ tr403: { b := data[mark:p - 1] if value != nil { - *value = string(b) + *value = b } else { if msg.Headers == nil { msg.Headers = Headers{} @@ -11779,7 +11779,7 @@ tr553: tr557: //line sip.rl:205 - msg.CallID = string(data[mark:p]) + msg.CallID = data[mark:p] goto st381 tr643: diff --git a/sip/msg_parse.rl b/sip/msg_parse.rl index 623a441..4672893 100644 --- a/sip/msg_parse.rl +++ b/sip/msg_parse.rl @@ -38,7 +38,7 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) { ctype := "" var name string var hex byte - var value *string + var value *[]byte var via *Via var addrp **Addr var addr *Addr diff --git a/sip/msg_test.go b/sip/msg_test.go index b263689..db189ea 100755 --- a/sip/msg_test.go +++ b/sip/msg_test.go @@ -92,12 +92,12 @@ var msgTests = []msgTest{ VersionMajor: 2, Status: 200, Phrase: "OK", - Warning: "Morning and evening\r\n" + + Warning: []byte("Morning and evening\r\n" + " Maids heard the goblins cry:\r\n" + " “Come buy our orchard fruits,\r\n" + " Come buy, come buy:\r\n" + " Apples and quinces,\r\n" + - " Lemons and oranges", + " Lemons and oranges"), }, }, @@ -116,12 +116,12 @@ var msgTests = []msgTest{ VersionMajor: 2, Status: 200, Phrase: "OK", - Warning: "Morning and evening\r\n" + + Warning: []byte("Morning and evening\r\n" + " Maids heard the goblins cry:\r\n" + " “Come buy our orchard fruits,\r\n" + " Come buy, come buy:\r\n" + " Apples and quinces,\r\n" + - " Lemons and oranges", + " Lemons and oranges"), Headers: sip.Headers{ "X-LOL": "omfg", }, @@ -459,7 +459,7 @@ var msgTests = []msgTest{ VersionMajor: 2, Status: 200, Phrase: "OK", - Warning: "Maids heard the goblins cry", + Warning: []byte("Maids heard the goblins cry"), Via: &sip.Via{ Protocol: "SIP", Version: "2.0", @@ -635,7 +635,7 @@ var msgTests = []msgTest{ Method: "OPTIONS", CSeqMethod: "OPTIONS", MaxForwards: 60, - CallID: "e71a163e-c440-474d-a4ec-5cd85a0309c6", + CallID: []byte("e71a163e-c440-474d-a4ec-5cd85a0309c6"), CSeq: 36612, Request: &sip.URI{ Scheme: "sip", @@ -673,8 +673,8 @@ var msgTests = []msgTest{ Port: 42367, }, }, - UserAgent: "ghoul/0.1", - Accept: "application/sdp", + UserAgent: []byte("ghoul/0.1"), + Accept: []byte("application/sdp"), }, }, @@ -708,12 +708,12 @@ var msgTests = []msgTest{ VersionMajor: 2, Status: 200, Phrase: "OK", - CallID: "99042736-d40b-4d96-a81b-867321443ff5", + CallID: []byte("99042736-d40b-4d96-a81b-867321443ff5"), CSeq: 16378, CSeqMethod: "INVITE", - Server: "Asterisk PBX 10.11.1", - Allow: "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH", - Supported: "replaces, timer", + Server: []byte("Asterisk PBX 10.11.1"), + Allow: []byte("INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH"), + Supported: []byte("replaces, timer"), Via: &sip.Via{ Protocol: "SIP", Version: "2.0", @@ -804,7 +804,7 @@ var msgTests = []msgTest{ VersionMajor: 2, Status: 200, Phrase: "OK", - CallID: "042736d4-0bd9-4681-ab86-7321443ff58a", + CallID: []byte("042736d4-0bd9-4681-ab86-7321443ff58a"), CSeq: 31109, CSeqMethod: "INVITE", Via: &sip.Via{ @@ -905,7 +905,7 @@ var msgTests = []msgTest{ Method: "INVITE", CSeqMethod: "INVITE", MaxForwards: 70, - CallID: "87704115-03b8-122e-08b5-001bfcce6bdf", + CallID: []byte("87704115-03b8-122e-08b5-001bfcce6bdf"), CSeq: 133097268, Request: &sip.URI{ Scheme: "sip", @@ -947,11 +947,11 @@ var msgTests = []msgTest{ // }, // }, }, - UserAgent: "tube/0.1", - Allow: "INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE, INFO", - AllowEvents: "talk", - ContentDisposition: "session", - Supported: "timer, 100rel", + UserAgent: []byte("tube/0.1"), + Allow: []byte("INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE, INFO"), + AllowEvents: []byte("talk"), + ContentDisposition: []byte("session"), + Supported: []byte("timer, 100rel"), Payload: &sip.MiscPayload{ T: "application/sdp-lol", D: []byte("v=0\r\n" + @@ -1034,9 +1034,10 @@ var msgTests = []msgTest{ Param: &sip.Param{"tag", "98asjd8", nil}, }, MaxForwards: 68, - CallID: "wsinv.ndaksdj@192.0.2.1", + CallID: []byte("wsinv.ndaksdj@192.0.2.1"), CSeq: 9, CSeqMethod: "INVITE", + Subject: []byte{}, Via: &sip.Via{ Protocol: "SIP", Version: "2.0", @@ -1058,7 +1059,6 @@ var msgTests = []msgTest{ }, }, }, - Subject: "", Headers: sip.Headers{ "NewFangledHeader": "newfangled value\r\n" + " continued newfangled value", @@ -1131,7 +1131,7 @@ var msgTests = []msgTest{ msg: sip.Msg{ VersionMajor: 2, Method: "!interesting-Method0123456789_*+`.%indeed'~", - CallID: "intmeth.word%ZK-!.*_+'@word`~)(><:\\/\"][?}{", + CallID: []byte("intmeth.word%ZK-!.*_+'@word`~)(><:\\/\"][?}{"), CSeq: 139122385, CSeqMethod: "!interesting-Method0123456789_*+`.%indeed'~", MaxForwards: 255, diff --git a/sip/route.go b/sip/route.go index 84df40e..1ba34db 100644 --- a/sip/route.go +++ b/sip/route.go @@ -27,7 +27,7 @@ func PopulateMessage(via *Via, contact *Addr, msg *Msg) { msg.From = msg.Contact.Copy() msg.From.Uri.Param = nil } - if msg.CallID == "" { + if msg.CallID == nil { msg.CallID = util.GenerateCallID() } if msg.CSeq == 0 { @@ -39,8 +39,8 @@ func PopulateMessage(via *Via, contact *Addr, msg *Msg) { if msg.MaxForwards == 0 { msg.MaxForwards = 70 } - if msg.UserAgent == "" { - msg.UserAgent = GosipUA + if msg.UserAgent == nil { + msg.UserAgent = []byte(GosipUA) } if msg.Via.Param.Get("branch") == nil { msg.Via.Param = &Param{"branch", util.GenerateBranch(), msg.Via.Param} diff --git a/sip/sip.rl b/sip/sip.rl index 365d19f..ee98cea 100644 --- a/sip/sip.rl +++ b/sip/sip.rl @@ -162,7 +162,7 @@ action name { action value {{ b := data[mark:p - 1] if value != nil { - *value = string(b) + *value = b } else { if msg.Headers == nil { msg.Headers = Headers{} @@ -203,7 +203,7 @@ action Addr { } action CallID { - msg.CallID = string(data[mark:p]) + msg.CallID = data[mark:p] } action ContentLength { diff --git a/util/util.go b/util/util.go index e6ce0ac..def66b2 100755 --- a/util/util.go +++ b/util/util.go @@ -1,6 +1,7 @@ package util import ( + "bytes" "encoding/hex" "math/rand" "net" @@ -70,13 +71,21 @@ func GenerateBranch() string { return "z9hG4bK-" + GenerateTag() } -// Generates a secure UUID4, e.g.f47ac10b-58cc-4372-a567-0e02b2c3d479 -func GenerateCallID() string { +// Generates a secure UUID4, e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479 +func GenerateCallID() []byte { lol := randomBytes(15) digs := hex.EncodeToString(lol) - uuid4 := digs[0:8] + "-" + digs[8:12] + "-4" + digs[12:15] + - "-a" + digs[15:18] + "-" + digs[18:] - return uuid4 + var b bytes.Buffer + b.WriteString(digs[0:8]) + b.WriteByte('-') + b.WriteString(digs[8:12]) + b.WriteString("-4") + b.WriteString(digs[12:15]) + b.WriteString("-a") + b.WriteString(digs[15:18]) + b.WriteByte('-') + b.WriteString(digs[18:]) + return b.Bytes() } // Generates a random ID for an SDP.