📄 sipclient.cpp
字号:
// "*fWorkingAuthenticator" using the contents of a following // "Proxy-Authenticate:" line. (Once we compute a 'response' for // "fWorkingAuthenticator", it can be used in a subsequent request // - that will hopefully succeed.) char* lineStart; while (1) { lineStart = nextLineStart; if (lineStart == NULL) break; nextLineStart = getLine(lineStart); if (lineStart[0] == '\0') break; // this is a blank line char* realm = strDupSize(lineStart); char* nonce = strDupSize(lineStart); // ##### Check for the format of "Proxy-Authenticate:" lines from // ##### known server types. // ##### This is a crock! We should make the parsing more general Boolean foundAuthenticateHeader = False; if ( // Asterisk ##### sscanf(lineStart, "Proxy-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2 || // Cisco ATA ##### sscanf(lineStart, "Proxy-Authenticate: Digest algorithm=MD5,domain=\"%*[^\"]\",nonce=\"%[^\"]\", realm=\"%[^\"]\"", nonce, realm) == 2) { fWorkingAuthenticator->setRealmAndNonce(realm, nonce); foundAuthenticateHeader = True; } delete[] realm; delete[] nonce; if (foundAuthenticateHeader) break; } } envir().setResultMsg("cannot handle INVITE response: ", firstLine); break; } // Skip every subsequent header line, until we see a blank line. // While doing so, check for "To:" and "Content-Length:" lines. // The remaining data is assumed to be the SDP descriptor that we want. // We should really do some more checking on the headers here - e.g., to // check for "Content-type: application/sdp", "CSeq", etc. ##### int contentLength = -1; char* lineStart; while (1) { lineStart = nextLineStart; if (lineStart == NULL) break; nextLineStart = getLine(lineStart); if (lineStart[0] == '\0') break; // this is a blank line char* toTagStr = strDupSize(lineStart); if (sscanf(lineStart, "To:%*[^;]; tag=%s", toTagStr) == 1) { delete[] (char*)fToTagStr; fToTagStr = strDup(toTagStr); fToTagStrSize = strlen(fToTagStr); } delete[] toTagStr; if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1 || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) { if (contentLength < 0) { envir().setResultMsg("Bad \"Content-length:\" header: \"", lineStart, "\""); break; } } } // We're now at the end of the response header lines if (lineStart == NULL) { envir().setResultMsg("no content following header lines: ", readBuf); break; } // Use the remaining data as the SDP descr, but first, check // the "Content-length:" header (if any) that we saw. We may need to // read more data, or we may have extraneous data in the buffer. char* bodyStart = nextLineStart; if (contentLength >= 0) { // We saw a "Content-length:" header unsigned numBodyBytes = &readBuf[bytesRead] - bodyStart; if (contentLength > (int)numBodyBytes) { // We need to read more data. First, make sure we have enough // space for it: unsigned numExtraBytesNeeded = contentLength - numBodyBytes;#ifdef USING_TCP // THIS CODE WORKS ONLY FOR TCP: ##### unsigned remainingBufferSize = readBufSize - (bytesRead + (readBuf - readBuffer)); if (numExtraBytesNeeded > remainingBufferSize) { char tmpBuf[200]; sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-length:\" %d (need a buffer size of >= %d bytes\n", readBufSize, contentLength, readBufSize + numExtraBytesNeeded - remainingBufferSize); envir().setResultMsg(tmpBuf); break; } // Keep reading more data until we have enough: if (fVerbosityLevel >= 1) { envir() << "Need to read " << numExtraBytesNeeded << " extra bytes\n"; } while (numExtraBytesNeeded > 0) { char* ptr = &readBuf[bytesRead]; unsigned bytesRead2; struct sockaddr_in fromAddr; Boolean readSuccess = fOurSocket->handleRead((unsigned char*)ptr, numExtraBytesNeeded, bytesRead2, fromAddr); if (!readSuccess) break; ptr[bytesRead2] = '\0'; if (fVerbosityLevel >= 1) { envir() << "Read " << bytesRead2 << " extra bytes: " << ptr << "\n"; } bytesRead += bytesRead2; numExtraBytesNeeded -= bytesRead2; }#endif if (numExtraBytesNeeded > 0) break; // one of the reads failed } bodyStart[contentLength] = '\0'; // trims any extra data } } while (0); return responseCode;}char* SIPClient::inviteWithPassword(char const* url, char const* username, char const* password) { delete[] (char*)fUserName; fUserName = strDup(username); fUserNameSize = strlen(fUserName); Authenticator authenticator; authenticator.setUsernameAndPassword(username, password); char* inviteResult = invite(url, &authenticator); if (inviteResult != NULL) { // We are already authorized return inviteResult; } // The "realm" and "nonce" fields should have been filled in: if (authenticator.realm() == NULL || authenticator.nonce() == NULL) { // We haven't been given enough information to try again, so fail: return NULL; } // Try again (but with the same CallId): inviteResult = invite1(&authenticator); if (inviteResult != NULL) { // The authenticator worked, so use it in future requests: fValidAuthenticator = authenticator; } return inviteResult;}Boolean SIPClient::sendACK() { char* cmd = NULL; do { char* const cmdFmt = "ACK %s SIP/2.0\r\n" "From: %s <sip:%s@%s>;tag=%u\r\n" "Via: SIP/2.0/UDP %s:%u\r\n" "To: %s;tag=%s\r\n" "Call-ID: %u@%s\r\n" "CSeq: %d ACK\r\n" "Content-length: 0\r\n\r\n"; unsigned cmdSize = strlen(cmdFmt) + fURLSize + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */ + fOurAddressStrSize + 5 /* max port len */ + fURLSize + fToTagStrSize + 20 + fOurAddressStrSize + 20; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, fURL, fUserName, fUserName, fOurAddressStr, fFromTag, fOurAddressStr, fOurPortNum, fURL, fToTagStr, fCallId, fOurAddressStr, fCSeq /* note: it's the same as before; not incremented */); if (!sendRequest(cmd, strlen(cmd))) { envir().setResultErrMsg("ACK send() failed: "); break; } delete[] cmd; return True; } while (0); delete[] cmd; return False;}Boolean SIPClient::sendBYE() { // NOTE: This should really be retransmitted, for reliability ##### char* cmd = NULL; do { char* const cmdFmt = "BYE %s SIP/2.0\r\n" "From: %s <sip:%s@%s>;tag=%u\r\n" "Via: SIP/2.0/UDP %s:%u\r\n" "To: %s;tag=%s\r\n" "Call-ID: %u@%s\r\n" "CSeq: %d ACK\r\n" "Content-length: 0\r\n\r\n"; unsigned cmdSize = strlen(cmdFmt) + fURLSize + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */ + fOurAddressStrSize + 5 /* max port len */ + fURLSize + fToTagStrSize + 20 + fOurAddressStrSize + 20; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, fURL, fUserName, fUserName, fOurAddressStr, fFromTag, fOurAddressStr, fOurPortNum, fURL, fToTagStr, fCallId, fOurAddressStr, ++fCSeq); if (!sendRequest(cmd, strlen(cmd))) { envir().setResultErrMsg("BYE send() failed: "); break; } delete[] cmd; return True; } while (0); delete[] cmd; return False;}Boolean SIPClient::processURL(char const* url) { do { // If we don't already have a server address/port, then // get these by parsing the URL: if (fServerAddress.s_addr == 0) { NetAddress destAddress; if (!parseSIPURL(envir(), url, destAddress, fServerPortNum)) break; fServerAddress.s_addr = *(unsigned*)(destAddress.data()); if (fOurSocket != NULL) { fOurSocket->changeDestinationParameters(fServerAddress, fServerPortNum, 255); } } return True; } while (0); fInviteStatusCode = 1; return False;}Boolean SIPClient::parseSIPURL(UsageEnvironment& env, char const* url, NetAddress& address, portNumBits& portNum) { do { // Parse the URL as "sip:<username>@<address>:<port>/<etc>" // (with ":<port>" and "/<etc>" optional) // Also, skip over any "<username>[:<password>]@" preceding <address> char const* prefix = "sip:"; unsigned const prefixLength = 4; if (_strncasecmp(url, prefix, prefixLength) != 0) { env.setResultMsg("URL is not of the form \"", prefix, "\""); break; } unsigned const parseBufferSize = 100; char parseBuffer[parseBufferSize]; unsigned addressStartIndex = prefixLength; while (url[addressStartIndex] != '\0' && url[addressStartIndex++] != '@') {} char const* from = &url[addressStartIndex]; // Skip over any "<username>[:<password>]@" char const* from1 = from; while (*from1 != '\0' && *from1 != '/') { if (*from1 == '@') { from = ++from1; break; } ++from1; } char* to = &parseBuffer[0]; unsigned i; for (i = 0; i < parseBufferSize; ++i) { if (*from == '\0' || *from == ':' || *from == '/') { // We've completed parsing the address *to = '\0'; break; } *to++ = *from++; } if (i == parseBufferSize) { env.setResultMsg("URL is too long"); break; } NetAddressList addresses(parseBuffer); if (addresses.numAddresses() == 0) { env.setResultMsg("Failed to find network address for \"", parseBuffer, "\""); break; } address = *(addresses.firstAddress()); portNum = 5060; // default value char nextChar = *from; if (nextChar == ':') { int portNumInt; if (sscanf(++from, "%d", &portNumInt) != 1) { env.setResultMsg("No port number follows ':'"); break; } if (portNumInt < 1 || portNumInt > 65535) { env.setResultMsg("Bad port number"); break; } portNum = (portNumBits)portNumInt; } return True; } while (0); return False;}Boolean SIPClient::parseSIPURLUsernamePassword(char const* url, char*& username, char*& password) { username = password = NULL; // by default do { // Parse the URL as "sip:<username>[:<password>]@<whatever>" char const* prefix = "sip:"; unsigned const prefixLength = 4; if (_strncasecmp(url, prefix, prefixLength) != 0) break; // Look for the ':' and '@': unsigned usernameIndex = prefixLength; unsigned colonIndex = 0, atIndex = 0; for (unsigned i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) { if (url[i] == ':' && colonIndex == 0) { colonIndex = i; } else if (url[i] == '@') { atIndex = i; break; // we're done } } if (atIndex == 0) break; // no '@' found char* urlCopy = strDup(url); urlCopy[atIndex] = '\0'; if (colonIndex > 0) { urlCopy[colonIndex] = '\0'; password = strDup(&urlCopy[colonIndex+1]); } else { password = strDup(""); } username = strDup(&urlCopy[usernameIndex]); delete[] urlCopy; return True; } while (0); return False;}char*SIPClient::createAuthenticatorString(Authenticator const* authenticator, char const* cmd, char const* url) { if (authenticator != NULL && authenticator->realm() != NULL && authenticator->nonce() != NULL && authenticator->username() != NULL && authenticator->password() != NULL) { // We've been provided a filled-in authenticator, so use it: char* const authFmt = "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", response=\"%s\", uri=\"%s\"\r\n"; char const* response = authenticator->computeDigestResponse(cmd, url); unsigned authBufSize = strlen(authFmt) + strlen(authenticator->username()) + strlen(authenticator->realm()) + strlen(authenticator->nonce()) + strlen(url) + strlen(response); char* authenticatorStr = new char[authBufSize]; sprintf(authenticatorStr, authFmt, authenticator->username(), authenticator->realm(), authenticator->nonce(), response, url); authenticator->reclaimDigestResponse(response); return authenticatorStr; } return strDup("");}Boolean SIPClient::sendRequest(char const* requestString, unsigned requestLength) { if (fVerbosityLevel >= 1) { envir() << "Sending request: " << requestString << "\n"; } // NOTE: We should really check that "requestLength" is not ##### // too large for UDP (see RFC 3261, section 18.1.1) ##### return fOurSocket->output(envir(), 255, (unsigned char*)requestString, requestLength);}unsigned SIPClient::getResponse(char*& responseBuffer, unsigned responseBufferSize) { if (responseBufferSize == 0) return 0; // just in case... responseBuffer[0] = '\0'; // ditto // Keep reading data from the socket until we see "\r\n\r\n" (except // at the start), or until we fill up our buffer. // Don't read any more than this. char* p = responseBuffer; Boolean haveSeenNonCRLF = False; int bytesRead = 0; while (bytesRead < (int)responseBufferSize) { unsigned bytesReadNow; struct sockaddr_in fromAddr; unsigned char* toPosn = (unsigned char*)(responseBuffer+bytesRead); Boolean readSuccess = fOurSocket->handleRead(toPosn, responseBufferSize-bytesRead, bytesReadNow, fromAddr); if (!readSuccess || bytesReadNow == 0) { envir().setResultMsg("SIP response was truncated"); break; } bytesRead += bytesReadNow; // Check whether we have "\r\n\r\n": char* lastToCheck = responseBuffer+bytesRead-4; if (lastToCheck < responseBuffer) continue; for (; p <= lastToCheck; ++p) { if (haveSeenNonCRLF) { if (*p == '\r' && *(p+1) == '\n' && *(p+2) == '\r' && *(p+3) == '\n') { responseBuffer[bytesRead] = '\0'; // Before returning, trim any \r or \n from the start: while (*responseBuffer == '\r' || *responseBuffer == '\n') { ++responseBuffer; --bytesRead; } return bytesRead; } } else { if (*p != '\r' && *p != '\n') { haveSeenNonCRLF = True; } } } } return 0;}Boolean SIPClient::parseResponseCode(char const* line, unsigned& responseCode) { if (sscanf(line, "%*s%u", &responseCode) != 1) { envir().setResultMsg("no response code in line: \"", line, "\""); return False; } return True;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -