void parseTcpSocketAddress(const StaticString &address, string &host, unsigned short &port) { if (getSocketAddressType(address) != SAT_TCP) { throw ArgumentException("Not a valid TCP socket address"); } StaticString hostAndPort(address.data() + sizeof("tcp://") - 1, address.size() - sizeof("tcp://") + 1); if (hostAndPort.empty()) { throw ArgumentException("Not a valid TCP socket address"); } if (hostAndPort[0] == '[') { // IPv6 address, e.g.: // [::1]:3000 const char *hostEnd = (const char *) memchr(hostAndPort.data(), ']', hostAndPort.size()); if (hostEnd == NULL || hostAndPort.size() <= string::size_type(hostEnd - hostAndPort.data()) + 3) { throw ArgumentException("Not a valid TCP socket address"); } const char *sep = hostEnd + 1; host.assign(hostAndPort.data() + 1, hostEnd - hostAndPort.data() - 1); port = stringToUint(StaticString( sep + 1, hostAndPort.data() + hostAndPort.size() - sep - 1 )); } else { // IPv4 address, e.g.: // 127.0.0.1:3000 const char *sep = (const char *) memchr(hostAndPort.data(), ':', hostAndPort.size()); if (sep == NULL || hostAndPort.size() <= string::size_type(sep - hostAndPort.data()) + 2) { throw ArgumentException("Not a valid TCP socket address"); } host.assign(hostAndPort.data(), sep - hostAndPort.data()); port = stringToUint(StaticString( sep + 1, hostAndPort.data() + hostAndPort.size() - sep - 1 )); } }
void Url::parseString(const char* urlString, UtlBoolean isAddrSpec) { // If isAddrSpec: // userinfo@hostport;uriParameters?headerParameters // If !isAddrSpec: // DisplayName<userinfo@hostport;urlParameters?headerParameters>;fieldParameters # ifdef TIME_PARSE OsTimeLog timeLog; LOG_TIME("start "); # endif // Try to catch when a name-addr is passed but we are expecting an // addr-spec -- many name-addr's start with '<' or '"'. if (isAddrSpec && (urlString[0] == '<' || urlString[0] == '"')) { OsSysLog::add(FAC_SIP, PRI_ERR, "Url::parseString Invalid addr-spec found (probably name-addr format): '%s'", urlString); } int workingOffset = 0; // begin at the beginning... size_t afterAngleBrackets = UTL_NOT_FOUND; if (isAddrSpec) { mAngleBracketsIncluded = FALSE; } else // ! addr-spec { // Is there a display name on the front? mDisplayName.remove(0); LOG_TIME("display <"); RegEx displayName(DisplayName); if (displayName.SearchAt(urlString, workingOffset)) { LOG_TIME("display > "); switch (displayName.Matches() /* number of substrings that matched */) { case 2: // matched unquoted sequence of tokens displayName.MatchString(&mDisplayName, 1); break; case 3: // matched a double quoted string // see performance note on DisplayName mDisplayName.append("\""); displayName.MatchString(&mDisplayName, 2); mDisplayName.append("\""); break; default: assert(false); } // does not include whitespace or the '<' workingOffset = displayName.AfterMatch(0); } // Are there angle brackets around the URI? LOG_TIME("angles < "); RegEx angleBrackets(AngleBrackets); if (angleBrackets.SearchAt(urlString, workingOffset)) { LOG_TIME("angles > "); // yes, there are angle brackets workingOffset = angleBrackets.MatchStart(1); // inside the angle brackets afterAngleBrackets = angleBrackets.AfterMatch(0); // following the '>' /* * Note: We do not set mAngleBracketsIncluded just because we saw them * That is only used for explicit control from the outside. * The local knowledge of whether or not there are angle brackets * is whether or not afterAngleBrackets == UTL_NOT_FOUND */ } } /* * AMBIGUITY - there is a potential ambiguity when parsing real URLs. * * Consider the url 'foo:333' - it could be: * scheme 'foo' host '333' ('333' is a valid local host name - bad idea, but legal) * or host 'foo' port '333' (and scheme 'sip' is implied) * * Now make it worse by using 'sips' as a hostname: * 'sips:333' * scheme 'sips' host '333' * or host 'sips' port '333' (and scheme 'sip' is implied) * * We resolve the first case by treating anything left of the colon as a scheme if * it is one of the supported schemes. Otherwise, we set the scheme to the * default (sip) and go on so that it will be parsed as a hostname. This does not * do the right thing for the (scheme 'sips' host '333') case, but they get what * they deserve. */ // Parse the scheme (aka url type) LOG_TIME("scheme < "); RegEx supportedScheme(SupportedScheme); if ( (supportedScheme.SearchAt(urlString,workingOffset)) && (supportedScheme.MatchStart(0) == workingOffset) ) { LOG_TIME("scheme > "); // the scheme name matches one of the supported schemes mScheme = static_cast<Scheme>(supportedScheme.Matches()-1); workingOffset = supportedScheme.AfterMatch(0); // past the ':' } else { /* * It did not match one of the supported scheme names * so proceed on the assumption that it's a host and "sip:" is implied * Leave the workingOffset where it is (before the token). * The code below, through the parsing of host and port * treats this as an implicit 'sip:' url; if it parses ok * up to that point, it resets the scheme to SipsUrlScheme */ mScheme = UnknownUrlScheme; } // skip over any '//' following the scheme for the ones we know use that switch (mScheme) { case FileUrlScheme: case FtpUrlScheme: case HttpUrlScheme: case HttpsUrlScheme: case RtspUrlScheme: if (0==strncmp("//", urlString+workingOffset, 2)) { workingOffset += 2; } break; case UnknownUrlScheme: case SipUrlScheme: case SipsUrlScheme: case MailtoUrlScheme: default: break; } if (FileUrlScheme != mScheme) // no user part in file urls { // Parse the username and password LOG_TIME("userpass < "); RegEx usernameAndPassword(UsernameAndPassword); if ( (usernameAndPassword.SearchAt(urlString, workingOffset)) && usernameAndPassword.MatchStart(0) == workingOffset ) { LOG_TIME("userpass > "); usernameAndPassword.MatchString(&mUserId, 1); usernameAndPassword.MatchString(&mPassword, 2); workingOffset = usernameAndPassword.AfterMatch(0); } else { // username and password are optional, so not finding them is ok // leave workingOffset where it is } } // Parse the hostname and port LOG_TIME("hostport < "); RegEx hostAndPort(HostAndPort); if ( (hostAndPort.SearchAt(urlString,workingOffset)) && (hostAndPort.MatchStart(0) == workingOffset) ) { LOG_TIME("hostport > "); hostAndPort.MatchString(&mHostAddress,1); UtlString portStr; if (hostAndPort.MatchString(&portStr,2)) { mHostPort = atoi(portStr.data()); } workingOffset = hostAndPort.AfterMatch(0); if (UnknownUrlScheme == mScheme) { /* * Resolve AMBIGUITY * Since we were able to parse this as a host and port, it is now safe to * set the scheme to the implied 'sip:'. */ mScheme = SipUrlScheme; } } else { if (FileUrlScheme != mScheme) // no host is ok in a file URL { /* * This is not a file URL, so not having a recognized host name is invalid. * * Since we may have been called from a constructor, there is no way to * return an error, but at this point we know this is bad, so instead * we just log an error and set the scheme to the unknown url type and * clear any components that might have been set. */ OsSysLog::add(FAC_SIP, PRI_ERR, "Url::parseString no valid host found at char %d in '%s', " "isAddrSpec = %d", workingOffset, urlString, isAddrSpec ); mScheme = UnknownUrlScheme; mDisplayName.remove(0); mUserId.remove(0); mPassword.remove(0); } } // Next is a path if http, https, or ftp, // OR url parameters if sip or sips. // There can be no Url parameters for http, https, or ftp // because semicolon is a valid part of the path value switch ( mScheme ) { case FileUrlScheme: case FtpUrlScheme: case HttpUrlScheme: case HttpsUrlScheme: case RtspUrlScheme: { // this is an http, https, or ftp URL, so get the path LOG_TIME("path < "); RegEx urlPath(UrlPath); if ( (urlPath.SearchAt(urlString, workingOffset)) && (urlPath.MatchStart(0) == workingOffset) ) { LOG_TIME("path > "); urlPath.MatchString(&mPath,1); workingOffset = urlPath.AfterMatch(1); } } break; case SipUrlScheme: case SipsUrlScheme: { // it may have url parameters of the form ";" param "=" value ... // if it meets the right conditions: if ( isAddrSpec // in addr-spec, any param is a url param || afterAngleBrackets != UTL_NOT_FOUND // inside angle brackets there may be a url param ) { LOG_TIME("urlparm < "); RegEx urlParams(UrlParams); if ( (urlParams.SearchAt(urlString, workingOffset)) && (urlParams.MatchStart(0) == workingOffset) ) { LOG_TIME("urlparm > "); urlParams.MatchString(&mRawUrlParameters, 1); workingOffset = urlParams.AfterMatch(1); // actual parsing of the parameters is in parseUrlParameters // so that it only happens if someone asks for them. } } } break; case MailtoUrlScheme: default: // no path component break; } if (UnknownUrlScheme != mScheme) { // Parse any header or query parameters LOG_TIME("hdrparm < "); RegEx headerOrQueryParams(HeaderOrQueryParams); if( (headerOrQueryParams.SearchAt(urlString, workingOffset)) && (headerOrQueryParams.MatchStart(0) == workingOffset) ) { LOG_TIME("hdrparm > "); headerOrQueryParams.MatchString(&mRawHeaderOrQueryParameters, 1); workingOffset = headerOrQueryParams.AfterMatch(0); // actual parsing of the parameters is in parseHeaderOrQueryParameters // so that it only happens if someone asks for them. } // Parse the field parameters if (!isAddrSpec) // can't have field parameters in an addrspec { if (afterAngleBrackets != UTL_NOT_FOUND) { workingOffset = afterAngleBrackets; } LOG_TIME("fldparm < "); RegEx fieldParameters(FieldParams); if ( (fieldParameters.SearchAt(urlString, workingOffset)) && (fieldParameters.MatchStart(0) == workingOffset) ) { LOG_TIME("fldparm > "); fieldParameters.MatchString(&mRawFieldParameters, 1); // actual parsing of the parameters is in parseFieldParameters // so that it only happens if someone asks for them. } } } # ifdef TIME_PARSE UtlString timeDump; timeLog.getLogString(timeDump); printf("\n%s\n", timeDump.data()); # endif }
std::string ConnectionSettings::getFullAddress() const { return hostAndPort().toString(); }