/**************************************************************************** * * Copyright (C) 2000-2001 RealNetworks, Inc. All rights reserved. * * This program is free software. It may be distributed under the terms * in the file LICENSE, found in the top level of the source distribution. * */ #include #include #include #include #include "RtspProt.h" /************************************** * * RTSP protocol class * **************************************/ const char* s_szHeaders[] = { // From s6.2 "Accept", "Accept-Encoding", "Accept-Language", "Authorization", "From", "If-Modified-Since", "Range", "Referer", "User-Agent", NULL }; CRtspProtocol::CRtspProtocol(CRtspProtocolResponse* pResponse) : m_pResponse(pResponse), m_CSeqSend(0), m_CSeqRecv(0) { if (NULL == m_pResponse) { printf("%s: m_pResponse is null pointer! \n", __FUNCTION__); exit(1); } } CRtspProtocol::~CRtspProtocol(void) { RequestTagListQueue.clear(); } int CRtspProtocol::Encoder(const CRtspMsg* pRtspMsg, char* &pDataStr, int &DataLen) { //Check input parameters exception if (NULL == pRtspMsg) { DBGPrint(M_RtspStack, FATAL_LEVEL, "%s: pRtspMsg is null pointer!", __FUNCTION__); return -1; } pDataStr = NULL; //Check request or response switch (pRtspMsg->GetType() ) { case RTSP_TYPE_REQUEST: { //printf("can't be request here\n"); CRtspRequestMsg* pReqMsg = (CRtspRequestMsg *)pRtspMsg; CString StrCSeq = pReqMsg->GetHdr("CSeq"); if (StrCSeq.IsEmpty() ) { char ClipBuf[16+1]; snprintf(ClipBuf, 16, "%d", (int)GetNextCseq() ); pReqMsg->SetHdr("CSeq", ClipBuf); } CPCHAR pVerb = pReqMsg->GetVerbStr(); CPCHAR pUrl = pReqMsg->GetUrl(); size_t nHdrLen = pReqMsg->GetHdrLen(); size_t nBufLen = pReqMsg->GetBufLen(); // SP SP "RTSP/1.0" CRLF // CRLF size_t len = FUDGE + strlen(pVerb) + 1 + strlen(pUrl)+ 1 + 8 + 2 + nHdrLen + 2 + nBufLen; char* pBuf = new char[len+1]; char* p = pBuf; if (NULL == pBuf) { DBGPrint(M_RtspStack, FATAL_LEVEL, "%s: new pBuf failed!", __FUNCTION__); exit(1); } p += sprintf(pBuf, "%s %s RTSP/1.0\r\n", pVerb, pUrl); for (int i=0; i<(int)pReqMsg->GetHdrCount(); i++) { CRtspHdr* pHdr = pReqMsg->GetHdr(i); p += sprintf(p, "%s: %s\r\n", (CPCHAR)pHdr->GetKey(), (CPCHAR)pHdr->GetVal() ); } p += sprintf(p, "\r\n"); if (nBufLen) { memcpy(p, pRtspMsg->GetBuf(), nBufLen); p += nBufLen; } *p = '\0'; pDataStr = pBuf; } break; case RTSP_TYPE_RESPONSE: { CRtspResponseMsg* pRespMsg = (CRtspResponseMsg*)pRtspMsg; UINT32 nCode = pRespMsg->GetStatusCode(); CPCHAR pReason = pRespMsg->GetStatusMsg(); size_t nHdrLen = pRespMsg->GetHdrLen(); size_t nBufLen = pRespMsg->GetBufLen(); // "RTSP/1.0" SP SP CRLF // CRLF // (or terminating NULL from sprintf() if no buffer) size_t nResponseLen = 8 + 1 + 3 + 1 + strlen(pReason) + 2 + nHdrLen + 2 + nBufLen; if (0 == nBufLen) nResponseLen++; char* pBuf = new char[nResponseLen+1]; char* p = pBuf; if (NULL == pBuf) { DBGPrint(M_RtspStack, FATAL_LEVEL, "%s: new pBuf failed!", __FUNCTION__); exit(1); } p += sprintf(pBuf, "RTSP/1.0 %d %s\r\n", (int)nCode, pReason); for (int i=0; i<(int)pRespMsg->GetHdrCount(); i++) { CRtspHdr* pHdr = pRespMsg->GetHdr(i); p += sprintf(p, "%s: %s\r\n", (CPCHAR)pHdr->GetKey(), (CPCHAR)pHdr->GetVal() ); } p += sprintf(p, "\r\n"); if (nBufLen) { memcpy(p, pRespMsg->GetBuf(), nBufLen); p += nBufLen; } *p = '\0'; pDataStr = pBuf; //printf("%s\n",pBuf); } break; default: DBGPrint(M_RtspStack, ERROR_LEVEL, "%s: Invalid rtsp message type<%d>!", __FUNCTION__, pRtspMsg->GetType() ); return -1; } DataLen = strlen(pDataStr); return 0; } //srcip srcport belong to client int CRtspProtocol::DecoderAndDrvie(int sockfd, const char* pDataBuf, int DataLen, CRtspMsg* &pRtspMsg, char SrcIP[], UINT16 SrcPort, int &Code, const char* &pNextPacketPos) { pRtspMsg = NULL; Code = 0; pNextPacketPos = NULL; //Check input parameters exception if (NULL == pDataBuf) { DBGPrint(M_RtspStack, ERROR_LEVEL, "%s: pDataBuf is null pointer!", __FUNCTION__); Code = 400; return -1; } //Init parser state ReadState mState = stCmd; char ALine[A_LINE_MAX_LEN+1]; char* pPoll = (char*)pDataBuf; int BodyLen = 0; while (pPoll - pDataBuf < DataLen) { memset(ALine, 0, sizeof(ALine)); //Skip CRLF if (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) == 0) { pPoll += strlen(RTSP_CRLF); } int len; //Find Next CRLF for getting a line char* pNextLineWithCRLF = strstr(pPoll, RTSP_CRLF); if (NULL == pNextLineWithCRLF) { len = pDataBuf+DataLen-pPoll; } else { len = pNextLineWithCRLF - pPoll; } if (len > 0) { if (len > A_LINE_MAX_LEN) { DBGPrint(M_RtspStack, ERROR_LEVEL, "A line too length, len(%d) > %d in \n%s\n", len, A_LINE_MAX_LEN, pPoll); Code = 406; return -1; } strncpy(ALine, pPoll, len); ALine[len] = '\0'; //Check parser state switch (mState) { case stCmd: if (HandleReadCmd(ALine, pRtspMsg, Code) != 0) { return -1; } pRtspMsg->SetSockFd(sockfd); pRtspMsg->SetRemoteHost(SrcIP, SrcPort); mState = stHdr; break; case stHdr: if (HandleReadHdr(ALine, pRtspMsg, Code) != 0) { return -1; } break; case stBody: if (HandleReadBody(pPoll, BodyLen, pRtspMsg, Code) != 0) { return -1; } mState = stDispatch; pNextPacketPos = pPoll + BodyLen; break; default: //Note: This case should not be happened. DBGPrint(M_RtspStack, ERROR_LEVEL, "No proto read state, mState=%d!", mState); Code = 500; return -1; } } else { //Empty line indicates end of headers. Note: Body may not be present. CString StrBodyLen = pRtspMsg->GetHdr("Content-Length"); BodyLen = atoi(StrBodyLen); if (0 == BodyLen) { mState = stDispatch; //Skip CRLF while (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) == 0) { pPoll += strlen(RTSP_CRLF); } pNextPacketPos = pPoll; } else { mState = stBody; } } //Parser end and break into next step if (stDispatch == mState) { if (DispatchMessage(pRtspMsg, Code) != 0) { return -1; } //Quit while loop break; } pPoll = pNextLineWithCRLF; } return 0; } int CRtspProtocol::DecoderAndDrvie(const char* pDataBuf, int DataLen, CRtspMsg* &pRtspMsg, int &Code, char SrcIP[], UINT16 SrcPort) { pRtspMsg = NULL; Code = 0; //Check input parameters exception if (NULL == pDataBuf) { Code = 400; return -1; } //Init parser state ReadState mState = stCmd; char ALine[A_LINE_MAX_LEN+1]; char* pPoll = (char*)pDataBuf; int BodyLen = 0; /* ¡À¡À??1???BT??????¡ã2¨¦¨¨¡À??¨²GET_PARAMETER¨º¡À¡ê???¨®|??¨¢D¡ã¨¹¡ê?¡ã¨¹?¨¢?2??¨®D/r/n¡ê?¦Ì????a??3?¡ä¨ª¡ê??¦Ì¨ª3¡À¨¤¨¤¡ê?¡ê --yinxiaogui 2014/3/7 13:59:24 * "RTSP/1.0 200 OK\r\nCSeq: 6\r\nDate: Fri, Mar 07 2014 12:04:00 GMT\r\nContent-Base: rtsp://" */ while (pPoll - pDataBuf < DataLen) { //Skip CRLF if (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) == 0) { pPoll += strlen(RTSP_CRLF); } int len; //Find Next CRLF for getting a line char* pNextLineWithCRLF = strstr(pPoll, RTSP_CRLF); if (NULL == pNextLineWithCRLF) { len = pDataBuf+DataLen-pPoll; } else { len = pNextLineWithCRLF - pPoll; } if (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) != 0) { if (len > A_LINE_MAX_LEN) { Code = 406; return -1; } strncpy(ALine, pPoll, len); ALine[len] = '\0'; //Check parser state switch (mState) { case stCmd: if (HandleReadCmd(ALine, pRtspMsg, Code) != 0) { return -1; } pRtspMsg->SetRemoteHost(SrcIP, SrcPort); mState = stHdr; break; case stHdr: if (HandleReadHdr(ALine, pRtspMsg, Code) != 0) { return -1; } break; case stBody: if (HandleReadBody(pPoll, BodyLen, pRtspMsg, Code) != 0) { return -1; } mState = stDispatch; break; default: //Note: This case should not be happened. Code = 500; return -1; } } else { CString StrContentType = pRtspMsg->GetHdr("Content-Type"); if (/*StrContentType == "text/xml"*/0) { mState = stDispatch; } else //Parse sdp { //Empty line indicates end of headers. Note: Body may not be present. CString StrBodyLen = pRtspMsg->GetHdr("Content-Length"); BodyLen = atoi(StrBodyLen); if (0 == BodyLen) { mState = stDispatch; //Skip CRLF while (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) == 0) { pPoll += strlen(RTSP_CRLF); } } else mState = stBody; } } //Parser end and break into next step if (stDispatch == mState) { if (DispatchMessage(pRtspMsg, Code) != 0) { return -1; } //Quit while loop break; } if (NULL == pNextLineWithCRLF) { Code = 412; return -1; } pPoll = pNextLineWithCRLF; } return 0; } int CRtspProtocol::DecoderAndDrvie(const char* pDataBuf, int DataLen, CRtspMsg* &pRtspMsg, int &Code) { pRtspMsg = NULL; Code = 0; //Check input parameters exception if (NULL == pDataBuf) { Code = 400; return -1; } //Init parser state ReadState mState = stCmd; char ALine[A_LINE_MAX_LEN+1]; char* pPoll = (char*)pDataBuf; int BodyLen = 0; /* ±±¾©¹«½»BTÏîÄ¿ÎݲÉ豸ÔÚGET_PARAMETERʱ£¬»ØÓ¦ÏÂÁаü£¬°ü½áβûÓÐ/r/n£¬µ¼Ö½âÎö³ö´í£¬ÏµÍ³±ÀÀ£¡£ --yinxiaogui 2014/3/7 13:59:24 * "RTSP/1.0 200 OK\r\nCSeq: 6\r\nDate: Fri, Mar 07 2014 12:04:00 GMT\r\nContent-Base: rtsp://" */ while (pPoll - pDataBuf < DataLen) { //Skip CRLF if (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) == 0) { pPoll += strlen(RTSP_CRLF); } int len; //Find Next CRLF for getting a line char* pNextLineWithCRLF = strstr(pPoll, RTSP_CRLF); if (NULL == pNextLineWithCRLF) { len = pDataBuf+DataLen-pPoll; } else { len = pNextLineWithCRLF - pPoll; } if (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) != 0) { if (len > A_LINE_MAX_LEN) { Code = 406; return -1; } strncpy(ALine, pPoll, len); ALine[len] = '\0'; //Check parser state switch (mState) { case stCmd: if (HandleReadCmd(ALine, pRtspMsg, Code) != 0) { return -1; } mState = stHdr; break; case stHdr: if (HandleReadHdr(ALine, pRtspMsg, Code) != 0) { return -1; } break; case stBody: if (HandleReadBody(pPoll, BodyLen, pRtspMsg, Code) != 0) { return -1; } mState = stDispatch; break; default: //Note: This case should not be happened. Code = 500; return -1; } } else { CString StrContentType = pRtspMsg->GetHdr("Content-Type"); if (/*StrContentType == "text/xml"*/0) { mState = stDispatch; } else //Parse sdp { //Empty line indicates end of headers. Note: Body may not be present. CString StrBodyLen = pRtspMsg->GetHdr("Content-Length"); BodyLen = atoi(StrBodyLen); if (0 == BodyLen) { mState = stDispatch; //Skip CRLF while (strncmp(pPoll, RTSP_CRLF, strlen(RTSP_CRLF) ) == 0) { pPoll += strlen(RTSP_CRLF); } } else { mState = stBody; } } } //Parser end and break into next step if (stDispatch == mState) { if (DispatchMessage(pRtspMsg, Code) != 0) { return -1; } //Quit while loop break; } if (NULL == pNextLineWithCRLF) { Code = 412; return -1; } pPoll = pNextLineWithCRLF; } return 0; } int CRtspProtocol::HandleReadCmd(char ALine[], CRtspMsg* &pRtspMsg, int &Code) { if ( (0 == strncmp(ALine, "RTSP", 4) ) && (ALine[4] == '/') && isdigit(ALine[5]) && (ALine[6] == '.') && isdigit(ALine[7]) && (ALine[8] == ' ') ) { // Response: RTSP/#.# if (!isdigit(ALine[9]) || !isdigit(ALine[10]) || !isdigit(ALine[11]) || (ALine[12] != ' ') || (ALine[13] == ' ') || (ALine[13] == '\0') ) { Code = 406; return -1; } pRtspMsg = new CRtspResponseMsg(); ( (CRtspResponseMsg*)pRtspMsg)->SetStatus(atoi(ALine + 9) ); } else { // Request: RTSP/#.# char* p = ALine; if (*p == ' ') { DBGPrint(M_RtspStack, ERROR_LEVEL, "RTSP protocol error: bad request line to begin in \n%s\n", ALine); Code = 400; return -1; } char* pVerb = p; while (*p && *p != ' ') p++; if (*p != ' ' || *(p+1) == ' ') { DBGPrint(M_RtspStack, ERROR_LEVEL, "RTSP protocol error: bad request line to find verb in \n%s\n", ALine); Code = 400; return -1; } *p++ = '\0'; char* pUrl = p; while (*p && *p != ' ') p++; if (*p != ' ' || *(p+1) == ' ') { DBGPrint(M_RtspStack, ERROR_LEVEL, "RTSP protocol error: bad request line to find url in \n%s\n", ALine); Code = 400; return -1; } *p++ = '\0'; if (0 != strncmp(p, "RTSP", 4) || p[4] != '/' || !isdigit(p[5]) || p[6] != '.' || !isdigit(p[7]) || p[8] != '\0') { DBGPrint(M_RtspStack, ERROR_LEVEL, "RTSP protocol error: bad request line to find version in \n%s\n", ALine); Code = 400; return -1; } pRtspMsg = new CRtspRequestMsg(); ( (CRtspRequestMsg*)pRtspMsg)->SetVerb(pVerb); ( (CRtspRequestMsg*)pRtspMsg)->SetUrl(pUrl); ( (CRtspRequestMsg*)pRtspMsg)->ParserUri(); } return 0; } //ÒÆÖ² live555½âÎöÈÏÖ¤ÐÅÏ¢ bool CRtspProtocol::handleAuthenticationFailure(CRtspMsg* &pRtspMsg, char *paramsStr) { if (paramsStr == NULL) return false; // There was no "WWW-Authenticate:" header; we can't proceed. // Fill in "fCurrentAuthenticator" with the information from the "WWW-Authenticate:" header: //bool realmHasChanged = false; // by default //bool isStale = false; // by default char realm[64] = {0}; char nonce[64] = {0}; char stale[32] = {0}; bool success = true; if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) { pRtspMsg->SetRealmAndNonce(realm, nonce); } else if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) { pRtspMsg->SetRealmAndNonce(realm, nonce); } else if (sscanf(paramsStr, "Basic realm=\"%[^\"]\"", realm) == 1) { pRtspMsg->SetRealmAndNonce(realm, NULL); } else { success = false; // bad "WWW-Authenticate:" header } return success; } int CRtspProtocol::HandleReadHdr(char ALine[], CRtspMsg* &pRtspMsg, int &Code) { char* p = ALine; if (*p == ':') { DBGPrint(M_RtspStack, ERROR_LEVEL, "RTSP protocol error: received header line '%s'", ALine); Code = 456; return -1; } while (*p && *p != ':') p++; if (*p != ':') { DBGPrint(M_RtspStack, ERROR_LEVEL, "RTSP protocol error: received header line '%s'", ALine); Code = 456; return -1; } // Break into key and val, skip LWS, and save it *p++ = '\0'; while (*p == ' ') p++; if (*p) { pRtspMsg->SetHdr(ALine, p); } if (strcmp(ALine, "WWW-Authenticate") == 0) { if (handleAuthenticationFailure(pRtspMsg, p) == false) { printf("%s:%d RTSP Authentication error: received header line '%s'", __FUNCTION__, __LINE__, p); Code = 403; return -1; } } return 0; } int CRtspProtocol::HandleReadBody(char* pBody, int BodyLen, CRtspMsg* &pRtspMsg, int &Code) { //Check input parameters exception if (NULL == pBody) { DBGPrint(M_RtspStack, ERROR_LEVEL, "%s: pBody is null pointer!", __FUNCTION__); Code = 400; return -1; } if (BodyLen > 0) { pRtspMsg->SetBuf( (CPByte)pBody, BodyLen); } return 0; } int CRtspProtocol::DispatchMessage(CRtspMsg* &pRtspMsg, int &Code) { //Check input parameters exception if (NULL == pRtspMsg) { DBGPrint(M_RtspStack, ERROR_LEVEL, "%s: pRtspMsg is null pointer!", __FUNCTION__); Code = 500; return -1; } switch (pRtspMsg->GetType() ) { case RTSP_TYPE_REQUEST: { CRtspRequestMsg* pReqMsg = (CRtspRequestMsg*)pRtspMsg; switch (pReqMsg->GetVerb() ) { case VERB_DESCRIBE: m_pResponse->OnDescribeRequest(pReqMsg); break; case VERB_ANNOUNCE: m_pResponse->OnAnnounceRequest(pReqMsg); break; case VERB_GETPARAM: m_pResponse->OnGetParamRequest(pReqMsg); break; case VERB_SETPARAM: m_pResponse->OnSetParamRequest(pReqMsg); break; case VERB_OPTIONS: m_pResponse->OnOptionsRequest(pReqMsg); break; case VERB_PAUSE: m_pResponse->OnPauseRequest(pReqMsg); break; case VERB_PLAY: m_pResponse->OnPlayRequest(pReqMsg); break; case VERB_RECORD: m_pResponse->OnRecordRequest(pReqMsg); break; case VERB_REDIRECT: m_pResponse->OnRedirectRequest(pReqMsg); break; case VERB_SETUP: m_pResponse->OnSetupRequest(pReqMsg); break; case VERB_TEARDOWN: m_pResponse->OnTeardownRequest(pReqMsg); break; default: DBGPrint(M_RtspStack, ERROR_LEVEL, "%s: Rtsp Verb<%d> is unknown!", __FUNCTION__, pReqMsg->GetVerb() ); Code = 501; return -1; } } break; case RTSP_TYPE_RESPONSE: { CRtspResponseMsg* pRespMsg = (CRtspResponseMsg*)pRtspMsg; const UINT32 CSeq = atoi(pRespMsg->GetHdr("CSeq") ); MyList::iterator ListItr; //Walk request tag list for (ListItr = RequestTagListQueue.begin(); ListItr != RequestTagListQueue.end(); ListItr++) { CRtspRequestTag* pReqTag = (*ListItr); if (NULL == pReqTag) { DBGPrint(M_RtspStack, ERROR_LEVEL, "Request tag list get a element: fatal error!"); Code = 500; return -1; } else { if (CSeq == pReqTag->m_cseq) { switch (pReqTag->m_verb) { case VERB_DESCRIBE: m_pResponse->OnDescribeResponse(pRespMsg); break; case VERB_ANNOUNCE: m_pResponse->OnAnnounceResponse(pRespMsg); break; case VERB_GETPARAM: m_pResponse->OnGetParamResponse(pRespMsg); break; case VERB_SETPARAM: m_pResponse->OnSetParamResponse(pRespMsg); break; case VERB_OPTIONS: m_pResponse->OnOptionsResponse(pRespMsg); break; case VERB_PAUSE: m_pResponse->OnPauseResponse(pRespMsg); break; case VERB_PLAY: m_pResponse->OnPlayResponse(pRespMsg); break; case VERB_RECORD: m_pResponse->OnRecordResponse(pRespMsg); break; case VERB_REDIRECT: m_pResponse->OnRedirectResponse(pRespMsg); break; case VERB_SETUP: m_pResponse->OnSetupResponse(pRespMsg); break; case VERB_TEARDOWN: m_pResponse->OnTeardownResponse(pRespMsg); break; default: //No implement at present DBGPrint(M_RtspStack, ERROR_LEVEL, "%s: Not implement for RTSP verb<%d>!", __FUNCTION__, pReqTag->m_verb); RequestTagListQueue.remove(pReqTag); delete pReqTag; Code = 501; return -1; } RequestTagListQueue.remove(pReqTag); delete pReqTag; break; } } } } break; default: DBGPrint(M_RtspStack, ERROR_LEVEL, "%s: Rtsp message type<%d> is unknown!", __FUNCTION__, pRtspMsg->GetType() ); Code = 500; return -1; } return 0; }