コード例 #1
0
ファイル: headers.go プロジェクト: cema-sp/gossip-demo
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
}
コード例 #2
0
ファイル: headers.go プロジェクト: cema-sp/gossip-demo
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}),
		},
	}
}
コード例 #3
0
ファイル: parser.go プロジェクト: hinike/gossip
// 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
}
コード例 #4
0
ファイル: parser.go プロジェクト: hinike/gossip
// 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
}
コード例 #5
0
ファイル: parser.go プロジェクト: hinike/gossip
// 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
}
コード例 #6
0
ファイル: parser.go プロジェクト: hinike/gossip
// 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
}
コード例 #7
0
ファイル: transport_fv_test.go プロジェクト: hinike/gossip
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
}