📄 rtspclient.cpp
字号:
char const* response = authenticator->computeDigestResponse(cmd, url); unsigned authBufSize = strlen(authFmt) + strlen(authenticator->username()) + strlen(authenticator->realm()) + strlen(authenticator->nonce()) + strlen(url) + strlen(response); authenticatorStr = new char[authBufSize]; sprintf(authenticatorStr, authFmt, authenticator->username(), authenticator->realm(), authenticator->nonce(), url, response); authenticator->reclaimDigestResponse(response); } else { // Basic authentication char* const authFmt = "Authorization: Basic %s\r\n"; char* usernamePassword = new char[strlen(authenticator->username()) + strlen(authenticator->password()) + 2]; sprintf(usernamePassword, "%s:%s", authenticator->username(), authenticator->password()); char* response = base64Encode(usernamePassword); unsigned authBufSize = strlen(authFmt) + strlen(response); authenticatorStr = new char[authBufSize]; sprintf(authenticatorStr, authFmt, response); delete[] response; delete[] usernamePassword; } return authenticatorStr; } return strDup("");}void RTSPClient::checkForAuthenticationFailure(unsigned responseCode, char*& nextLineStart, Authenticator* authenticator) { if (responseCode == 401 && authenticator != NULL) { // We have an authentication failure, so fill in "authenticator" // using the contents of a following "WWW-Authenticate:" line. // (Once we compute a 'response' for "authenticator", 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); Boolean foundAuthenticateHeader = False; if (sscanf(lineStart, "WWW-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) { authenticator->setRealmAndNonce(realm, nonce); foundAuthenticateHeader = True; } else if (sscanf(lineStart, "WWW-Authenticate: Basic realm=\"%[^\"]\"", realm) == 1) { authenticator->setRealmAndNonce(realm, NULL); // Basic authentication foundAuthenticateHeader = True; } delete[] realm; delete[] nonce; if (foundAuthenticateHeader) break; } }}Boolean RTSPClient::sendRequest(char const* requestString, char const* tag, Boolean base64EncodeIfOverHTTP) { if (fVerbosityLevel >= 1) { envir() << "Sending request: " << requestString << "\n"; } char* newRequestString = NULL; if (fTunnelOverHTTPPortNum != 0 && base64EncodeIfOverHTTP) { requestString = newRequestString = base64Encode(requestString); if (fVerbosityLevel >= 1) { envir() << "\tThe request was base-64 encoded to: " << requestString << "\n\n"; } } Boolean result = send(fOutputSocketNum, requestString, strlen(requestString), 0) >= 0; delete[] newRequestString; if (!result) { if (tag == NULL) tag = ""; char const* errFmt = "%s send() failed: "; unsigned const errLength = strlen(errFmt) + strlen(tag); char* err = new char[errLength]; sprintf(err, errFmt, tag); envir().setResultErrMsg(err); delete[] err; } return result;}Boolean RTSPClient::getResponse(char const* tag, unsigned& bytesRead, unsigned& responseCode, char*& firstLine, char*& nextLineStart, Boolean checkFor200Response) { do { char* readBuf = fResponseBuffer; bytesRead = getResponse1(readBuf, fResponseBufferSize); if (bytesRead == 0) { envir().setResultErrMsg("Failed to read response: "); break; } if (fVerbosityLevel >= 1) { envir() << "Received " << tag << " response: " << readBuf << "\n"; } firstLine = readBuf; nextLineStart = getLine(firstLine); if (!parseResponseCode(firstLine, responseCode)) break; if (responseCode != 200 && checkFor200Response) { envir().setResultMsg(tag, ": cannot handle response: ", firstLine); break; } return True; } while (0); // An error occurred: return False;}unsigned RTSPClient::getResponse1(char*& responseBuffer, unsigned responseBufferSize) { struct sockaddr_in fromAddress; if (responseBufferSize == 0) return 0; // just in case... responseBuffer[0] = '\0'; // ditto // Begin by reading and checking the first byte of the response. // If it's '$', then there's an interleaved RTP (or RTCP)-over-TCP // packet here. We need to read and discard it first. Boolean success = False; while (1) { unsigned char firstByte; if (readSocket(envir(), fInputSocketNum, &firstByte, 1, fromAddress) != 1) break; if (firstByte != '$') { // Normal case: This is the start of a regular response; use it: responseBuffer[0] = firstByte; success = True; break; } else { // This is an interleaved packet; read and discard it: unsigned char streamChannelId; if (readSocket(envir(), fInputSocketNum, &streamChannelId, 1, fromAddress) != 1) break; unsigned short size; if (readSocketExact(envir(), fInputSocketNum, (unsigned char*)&size, 2, fromAddress) != 2) break; size = ntohs(size); if (fVerbosityLevel >= 1) { envir() << "Discarding interleaved RTP or RTCP packet (" << size << " bytes, channel id " << streamChannelId << ")\n"; } unsigned char* tmpBuffer = new unsigned char[size]; if (tmpBuffer == NULL) break; unsigned bytesRead = 0; unsigned bytesToRead = size; unsigned curBytesRead; while ((curBytesRead = readSocket(envir(), fInputSocketNum, &tmpBuffer[bytesRead], bytesToRead, fromAddress)) > 0) { bytesRead += curBytesRead; if (bytesRead >= size) break; bytesToRead -= curBytesRead; } delete[] tmpBuffer; if (bytesRead != size) break; success = True; } } if (!success) return 0; // 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 = 1; // because we've already read the first byte while (bytesRead < (int)responseBufferSize) { unsigned bytesReadNow = readSocket(envir(), fInputSocketNum, (unsigned char*)(responseBuffer+bytesRead), 1, fromAddress); if (bytesReadNow == 0) { envir().setResultMsg("RTSP 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; } } } } envir().setResultMsg("We received a response not ending with <CR><LF><CR><LF>"); return 0;}Boolean RTSPClient::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;}Boolean RTSPClient::parseTransportResponse(char const* line, char*& serverAddressStr, portNumBits& serverPortNum, unsigned char& rtpChannelId, unsigned char& rtcpChannelId) { // Initialize the return parameters to 'not found' values: serverAddressStr = NULL; serverPortNum = 0; rtpChannelId = rtcpChannelId = 0xFF; char* foundServerAddressStr = NULL; Boolean foundServerPortNum = False; Boolean foundChannelIds = False; unsigned rtpCid, rtcpCid; // First, check for "Transport:" if (_strncasecmp(line, "Transport: ", 11) != 0) return False; line += 11; // Then, run through each of the fields, looking for ones we handle: char const* fields = line; char* field = strDupSize(fields); while (sscanf(fields, "%[^;]", field) == 1) { if (sscanf(field, "server_port=%hu", &serverPortNum) == 1) { foundServerPortNum = True; } else if (_strncasecmp(field, "source=", 7) == 0) { delete[] foundServerAddressStr; foundServerAddressStr = strDup(field+7); } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) { rtpChannelId = (unsigned char)rtpCid; rtcpChannelId = (unsigned char)rtcpCid; foundChannelIds = True; } fields += strlen(field); while (fields[0] == ';') ++fields; // skip over all leading ';' chars if (fields[0] == '\0') break; } delete[] field; if (foundServerPortNum || foundChannelIds) { serverAddressStr = foundServerAddressStr; return True; } delete[] foundServerAddressStr; return False;}Boolean RTSPClient::parseRTPInfoHeader(char const* line, unsigned& trackId, u_int16_t& seqNum, u_int32_t& timestamp) { if (_strncasecmp(line, "RTP-Info: ", 10) != 0) return False; line += 10; char const* fields = line; char* field = strDupSize(fields); while (sscanf(fields, "%[^;]", field) == 1) { if (sscanf(field, "url=trackID=%u", &trackId) == 1 || sscanf(field, "url=trackid=%u", &trackId) == 1 || sscanf(field, "seq=%hu", &seqNum) == 1 || sscanf(field, "rtptime=%u", ×tamp) == 1) { } fields += strlen(field); if (fields[0] == '\0') break; ++fields; // skip over the ';' } delete[] field; return True;}Boolean RTSPClient::parseScaleHeader(char const* line, float& scale) { if (_strncasecmp(line, "Scale: ", 7) != 0) return False; line += 7; return sscanf(line, "%f", &scale) == 1;}Boolean RTSPClient::setupHTTPTunneling(char const* urlSuffix) { if (fVerbosityLevel >= 1) { envir() << "Requesting RTSP-over-HTTP tunneling (on port " << fTunnelOverHTTPPortNum << ")\n\n"; } if (urlSuffix == NULL || urlSuffix[0] == '\0') urlSuffix = "/"; char* cmd = NULL; do { // Create a 'session cookie' string, using MD5: struct { struct timeval timestamp; unsigned counter; } seedData; gettimeofday(&seedData.timestamp, NULL); static unsigned counter = 0; seedData.counter = ++counter; char sessionCookie[33]; our_MD5Data((unsigned char*)(&seedData), sizeof seedData, sessionCookie); // DSS seems to require that the 'session cookie' string be 22 bytes long: sessionCookie[23] = '\0'; // Begin by sending a HTTP "GET", to set up the server->client link: char* const getCmdFmt = "GET %s HTTP/1.0\r\n" "%s" "x-sessioncookie: %s\r\n" "Accept: application/x-rtsp-tunnelled\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n" "\r\n"; unsigned cmdSize = strlen(getCmdFmt) + strlen(urlSuffix) + fUserAgentHeaderStrSize + strlen(sessionCookie); cmd = new char[cmdSize]; sprintf(cmd, getCmdFmt, urlSuffix, fUserAgentHeaderStr, sessionCookie); if (!sendRequest(cmd, "HTTP GET", False/*don't base64-encode*/)) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("HTTP GET", bytesRead, responseCode, firstLine, nextLineStart)) break; // Next, set up a second TCP connection (to the same server & port as before) // for the HTTP-tunneled client->server link. All future output will be to // this socket. fOutputSocketNum = setupStreamSocket(envir(), 0, False /* =>blocking */); if (fOutputSocketNum < 0) break; // Connect to the remote endpoint: struct sockaddr_in remoteName; remoteName.sin_family = AF_INET; remoteName.sin_port = htons(fTunnelOverHTTPPortNum); remoteName.sin_addr.s_addr = fServerAddress; if (connect(fOutputSocketNum, (struct sockaddr*)&remoteName, sizeof remoteName) != 0) { envir().setResultErrMsg("connect() failed: "); break; } // Then, send a HTTP "POST", to set up the client->server link: char* const postCmdFmt = "POST %s HTTP/1.0\r\n" "%s" "x-sessioncookie: %s\r\n" "Content-Type: application/x-rtsp-tunnelled\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n" "Content-Length: 32767\r\n" "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n" "\r\n"; cmdSize = strlen(postCmdFmt) + strlen(urlSuffix) + fUserAgentHeaderStrSize + strlen(sessionCookie); cmd = new char[cmdSize]; sprintf(cmd, postCmdFmt, urlSuffix, fUserAgentHeaderStr, sessionCookie); if (!sendRequest(cmd, "HTTP POST", False/*don't base64-encode*/)) break; // Note that there's no response to the "POST". delete[] cmd; return True; } while (0); // An error occurred: delete[] cmd; return False;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -