func From(e *endpoint, tag base.String) *base.FromHeader { header := &base.FromHeader{ DisplayName: &e.displayName, Address: &base.SipUri{ User: &e.username, Host: e.host, UriParams: base.NewParams().Add("transport", &e.transport), }, Params: base.NewParams(), } if tag.String() != "" { header.Params.Add("tag", &tag) } return header }
func Via(e *endpoint, branch string) *base.ViaHeader { return &base.ViaHeader{ &base.ViaHop{ ProtocolName: "SIP", ProtocolVersion: "2.0", Transport: e.transport.String(), Host: e.host, Port: &e.port, Params: base.NewParams().Add("branch", base.String{branch}), }, } }
// Parse a string representation of a Via header, returning a slice of at most one ViaHeader. // Note that although Via headers may contain a comma-separated list, RFC 3261 makes it clear that // these should not be treated as separate logical Via headers, but as multiple values on a single // Via header. func parseViaHeader(headerName string, headerText string) ( headers []base.SipHeader, err error) { sections := strings.Split(headerText, ",") var via base.ViaHeader = base.ViaHeader{} for _, section := range sections { var hop base.ViaHop parts := strings.Split(section, "/") if len(parts) < 3 { err = fmt.Errorf("not enough protocol parts in via header: '%s'", parts) return } parts[2] = strings.Join(parts[2:], "/") // The transport part ends when whitespace is reached, but may also start with // whitespace. // So the end of the transport part is the first whitespace char following the // first non-whitespace char. initialSpaces := len(parts[2]) - len(strings.TrimLeft(parts[2], c_ABNF_WS)) sentByIdx := strings.IndexAny(parts[2][initialSpaces:], c_ABNF_WS) + initialSpaces + 1 if sentByIdx == 0 { err = fmt.Errorf("expected whitespace after sent-protocol part "+ "in via header '%s'", section) return } else if sentByIdx == 1 { err = fmt.Errorf("empty transport field in via header '%s'", section) return } hop.ProtocolName = strings.TrimSpace(parts[0]) hop.ProtocolVersion = strings.TrimSpace(parts[1]) hop.Transport = strings.TrimSpace(parts[2][:sentByIdx-1]) if len(hop.ProtocolName) == 0 { err = fmt.Errorf("no protocol name provided in via header '%s'", section) } else if len(hop.ProtocolVersion) == 0 { err = fmt.Errorf("no version provided in via header '%s'", section) } else if len(hop.Transport) == 0 { err = fmt.Errorf("no transport provided in via header '%s'", section) } if err != nil { return } viaBody := parts[2][sentByIdx:] paramsIdx := strings.Index(viaBody, ";") var host string var port *uint16 if paramsIdx == -1 { // There are no header parameters, so the rest of the Via body is part of the host[:post]. host, port, err = parseHostPort(viaBody) hop.Host = host hop.Port = port if err != nil { return } hop.Params = base.NewParams() } else { host, port, err = parseHostPort(viaBody[:paramsIdx]) if err != nil { return } hop.Host = host hop.Port = port hop.Params, _, err = parseParams(viaBody[paramsIdx:], ';', ';', 0, true, true) } via = append(via, &hop) } headers = []base.SipHeader{&via} return }
// General utility method for parsing 'key=value' parameters. // Takes a string (source), ensures that it begins with the 'start' character provided, // and then parses successive key/value pairs separated with 'sep', // until either 'end' is reached or there are no characters remaining. // A map of keys to values will be returned, along with the number of characters consumed. // Provide 0 for start or end to indicate that there is no starting/ending delimiter. // If quoteValues is true, values can be enclosed in double-quotes which will be validated by the // parser and omitted from the returned map. // If permitSingletons is true, keys with no values are permitted. // These will result in a nil value in the returned map. func parseParams(source string, start uint8, sep uint8, end uint8, quoteValues bool, permitSingletons bool) ( params base.Params, consumed int, err error) { params = base.NewParams() if len(source) == 0 { // Key-value section is completely empty; return defaults. return } // Ensure the starting character is correct. if start != 0 { if source[0] != start { err = fmt.Errorf("expected %c at start of key-value section; got %c. section was %s", start, source[0], source) return } consumed++ } // Statefully parse the given string one character at a time. var buffer bytes.Buffer var key string parsingKey := true // false implies we are parsing a value inQuotes := false parseLoop: for ; consumed < len(source); consumed++ { switch source[consumed] { case end: if inQuotes { // We read an end character, but since we're inside quotations we should // treat it as a literal part of the value. buffer.WriteString(string(end)) continue } break parseLoop case sep: if inQuotes { // We read a separator character, but since we're inside quotations // we should treat it as a literal part of the value. buffer.WriteString(string(sep)) continue } if parsingKey && permitSingletons { params.Add(buffer.String(), base.NoString{}) } else if parsingKey { err = fmt.Errorf("Singleton param '%s' when parsing params which disallow singletons: \"%s\"", buffer.String(), source) return } else { value := buffer.String() params.Add(key, base.String{value}) } buffer.Reset() parsingKey = true case '"': if !quoteValues { // We hit a quote character, but since quoting is turned off we treat it as a literal. buffer.WriteString("\"") continue } if parsingKey { // Quotes are never allowed in keys. err = fmt.Errorf("Unexpected '\"' in parameter key in params \"%s\"", source) return } if !inQuotes && buffer.Len() != 0 { // We hit an initial quote midway through a value; that's not allowed. err = fmt.Errorf("unexpected '\"' in params \"%s\"", source) return } if inQuotes && consumed != len(source)-1 && source[consumed+1] != sep { // We hit an end-quote midway through a value; that's not allowed. err = fmt.Errorf("unexpected character %c after quoted param in \"%s\"", source[consumed+1], source) return } inQuotes = !inQuotes case '=': if buffer.Len() == 0 { err = fmt.Errorf("Key of length 0 in params \"%s\"", source) return } if !parsingKey { err = fmt.Errorf("Unexpected '=' char in value token: \"%s\"", source) return } key = buffer.String() buffer.Reset() parsingKey = false default: if !inQuotes && strings.Contains(c_ABNF_WS, string(source[consumed])) { // Skip unquoted whitespace. continue } buffer.WriteString(string(source[consumed])) } } // The param string has ended. Check that it ended in a valid place, and then store off the // contents of the buffer. if inQuotes { err = fmt.Errorf("Unclosed quotes in parameter string: %s", source) } else if parsingKey && permitSingletons { params.Add(buffer.String(), base.NoString{}) } else if parsingKey { err = fmt.Errorf("Singleton param '%s' when parsing params which disallow singletons: \"%s\"", buffer.String(), source) } else { value := buffer.String() params.Add(key, base.String{value}) } return }
// ParseSipUri converts a string representation of a SIP or SIPS URI into a SipUri object. func ParseSipUri(uriStr string) (uri base.SipUri, err error) { // Store off the original URI in case we need to print it in an error. uriStrCopy := uriStr // URI should start 'sip' or 'sips'. Check the first 3 chars. if strings.ToLower(uriStr[:3]) != "sip" { err = fmt.Errorf("invalid SIP uri protocol name in '%s'", uriStrCopy) return } uriStr = uriStr[3:] if strings.ToLower(uriStr[0:1]) == "s" { // URI started 'sips', so it's encrypted. uri.IsEncrypted = true uriStr = uriStr[1:] } // The 'sip' or 'sips' protocol name should be followed by a ':' character. if uriStr[0] != ':' { err = fmt.Errorf("no ':' after protocol name in SIP uri '%s'", uriStrCopy) return } uriStr = uriStr[1:] // SIP URIs may contain a user-info part, ending in a '@'. // This is the only place '@' may occur, so we can use it to check for the // existence of a user-info part. uri.User = base.NoString{} uri.Password = base.NoString{} endOfUserInfoPart := strings.Index(uriStr, "@") if endOfUserInfoPart != -1 { // A user-info part is present. These take the form: // user [ ":" password ] "@" endOfUsernamePart := strings.Index(uriStr, ":") if endOfUsernamePart > endOfUserInfoPart { endOfUsernamePart = -1 } if endOfUsernamePart == -1 { // No password component; the whole of the user-info part before // the '@' is a username. user := uriStr[:endOfUserInfoPart] uri.User = base.String{user} } else { user := uriStr[:endOfUsernamePart] pwd := uriStr[endOfUsernamePart+1 : endOfUserInfoPart] uri.User = base.String{user} uri.Password = base.String{pwd} } uriStr = uriStr[endOfUserInfoPart+1:] } // A ';' indicates the beginning of a URI params section, and the end of the URI itself. endOfUriPart := strings.Index(uriStr, ";") if endOfUriPart == -1 { // There are no URI parameters, but there might be header parameters (introduced by '?'). endOfUriPart = strings.Index(uriStr, "?") } if endOfUriPart == -1 { // There are no parameters at all. The URI ends after the host[:port] part. endOfUriPart = len(uriStr) } uri.Host, uri.Port, err = parseHostPort(uriStr[:endOfUriPart]) uriStr = uriStr[endOfUriPart:] if err != nil { return } else if len(uriStr) == 0 { uri.UriParams = base.NewParams() uri.Headers = base.NewParams() return } // Now parse any URI parameters. // These are key-value pairs separated by ';'. // They end at the end of the URI, or at the start of any URI headers // which may be present (denoted by an initial '?'). var uriParams base.Params var n int if uriStr[0] == ';' { uriParams, n, err = parseParams(uriStr, ';', ';', '?', true, true) if err != nil { return } } else { uriParams, n = base.NewParams(), 0 } uri.UriParams = uriParams uriStr = uriStr[n:] // Finally parse any URI headers. // These are key-value pairs, starting with a '?' and separated by '&'. var headers base.Params headers, n, err = parseParams(uriStr, '?', '&', 0, true, false) if err != nil { return } uri.Headers = headers uriStr = uriStr[n:] if len(uriStr) > 0 { err = fmt.Errorf("internal error: parse of SIP uri ended early! '%s'", uriStrCopy) return // Defensive return } return }
// parseAddressValue parses an address - such as from a From, To, or // Contact header. It returns: // - a MaybeString containing the display name (or not) // - a parsed SipUri object // - a map containing any header parameters present // - the error object // See RFC 3261 section 20.10 for details on parsing an address. // Note that this method will not accept a comma-separated list of addresses; // addresses in that form should be handled by parseAddressValues. func parseAddressValue(addressText string) ( displayName base.MaybeString, uri base.Uri, headerParams base.Params, err error) { headerParams = base.NewParams() if len(addressText) == 0 { err = fmt.Errorf("address-type header has empty body") return } addressTextCopy := addressText addressText = strings.TrimSpace(addressText) firstAngleBracket := findUnescaped(addressText, '<', quotes_delim) firstSpace := findAnyUnescaped(addressText, c_ABNF_WS, quotes_delim, angles_delim) displayName = base.NoString{} if firstAngleBracket != -1 && firstSpace != -1 && firstSpace < firstAngleBracket { // There is a display name present. Let's parse it. if addressText[0] == '"' { // The display name is within quotations. addressText = addressText[1:] nextQuote := strings.Index(addressText, "\"") if nextQuote == -1 { // Unclosed quotes - parse error. err = fmt.Errorf("Unclosed quotes in header text: %s", addressTextCopy) return } nameField := addressText[:nextQuote] displayName = base.String{nameField} addressText = addressText[nextQuote+1:] } else { // The display name is unquoted, so match until the next whitespace // character. nameField := addressText[:firstSpace] displayName = base.String{nameField} addressText = addressText[firstSpace+1:] } } // Work out where the SIP URI starts and ends. addressText = strings.TrimSpace(addressText) var endOfUri int var startOfParams int if addressText[0] != '<' { switch displayName.(type) { case base.String: // The address must be in <angle brackets> if a display name is // present, so this is an invalid address line. err = fmt.Errorf("Invalid character '%c' following display "+ "name in address line; expected '<': %s", addressText[0], addressTextCopy) return } endOfUri = strings.Index(addressText, ";") if endOfUri == -1 { endOfUri = len(addressText) } startOfParams = endOfUri } else { addressText = addressText[1:] endOfUri = strings.Index(addressText, ">") if endOfUri == 0 { err = fmt.Errorf("'<' without closing '>' in address %s", addressTextCopy) return } startOfParams = endOfUri + 1 } // Now parse the SIP URI. uri, err = ParseUri(addressText[:endOfUri]) if err != nil { return } if startOfParams >= len(addressText) { return } // Finally, parse any header parameters and then return. addressText = addressText[startOfParams:] headerParams, _, err = parseParams(addressText, ';', ';', ',', true, true) return }
func TestMassUDP(t *testing.T) { NUM_MSGS := 10000 from, _ := NewManager("udp") to, _ := NewManager("udp") to.Listen(fmt.Sprintf("%s:%d", alice.host, alice.port)) receiver := to.GetChannel() receivedIDs := make([]int, 0) result := make(chan bool) go func() { recvloop: for { select { case msg, ok := <-receiver: if !ok { break recvloop } id, _ := strconv.ParseInt(msg.GetBody(), 10, 32) receivedIDs = append(receivedIDs, int(id)) if len(receivedIDs) >= NUM_MSGS { break recvloop } case <-time.After(time.Second / 10): break recvloop } } if !(len(receivedIDs) == NUM_MSGS) { t.Errorf("Error - received an unexpected number of messages: %d", len(receivedIDs)) } else { seenSet := make(map[int]bool) for _, val := range receivedIDs { if _, match := seenSet[val]; match { t.Errorf("Duplicate message received: %d", val) } seenSet[val] = true } if len(seenSet) != NUM_MSGS { t.Errorf("Unexpected number of messages received: %d", len(seenSet)) } } result <- true }() go func() { uri := base.SipUri{User: base.String{"alice"}, Host: "127.0.0.1", Port: nil, UriParams: base.NewParams(), Headers: base.NewParams()} for ii := 1; ii <= NUM_MSGS; ii++ { from.Send(fmt.Sprintf("%s:%d", alice.host, alice.port), base.NewRequest(base.ACK, &uri, "SIP/2.0", []base.SipHeader{base.ContentLength(len(fmt.Sprintf("%d", ii)))}, fmt.Sprintf("%d", ii))) if ii%100 == 0 { <-time.After(time.Millisecond) } } }() _ = <-result return }