/**************************************************************************** * * 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 "RtspMsg.h" #include /* memset */ CAuthenticator::CAuthenticator() { fRealm = NULL; fNonce = NULL; fUsername = NULL; fPassword = NULL; fPasswordIsMD5 = false; } CAuthenticator::~CAuthenticator() { reset(); } void CAuthenticator::reset() { resetRealmAndNonce(); resetUsernameAndPassword(); } void CAuthenticator::setRealmAndNonce(char const* realm, char const* nonce) { resetRealmAndNonce(); assignRealmAndNonce(realm, nonce); } void CAuthenticator::setUsernameAndPassword(char const* username, char const* password, bool passwordIsMD5) { resetUsernameAndPassword(); assignUsernameAndPassword(username, password, passwordIsMD5); } void CAuthenticator::reclaimDigestResponse(char const* responseStr) { if (responseStr != NULL) { delete[](char*)responseStr; responseStr = NULL; } } void CAuthenticator::resetRealmAndNonce() { if (fRealm != NULL) { delete[] fRealm; fRealm = NULL; } if (fNonce != NULL) { delete[] fNonce; fNonce = NULL; } } void CAuthenticator::resetUsernameAndPassword() { if (fUsername != NULL) { delete[] fUsername; fUsername = NULL; } if (fPassword != NULL) { delete[] fPassword; fPassword = NULL; } fPasswordIsMD5 = false; } void CAuthenticator::assignRealmAndNonce(char const* realm, char const* nonce) { if (realm != NULL && strlen(realm) > 0) { fRealm = strMyDup(realm); } if (nonce != NULL && strlen(nonce) > 0) { fNonce = strMyDup(nonce); } } void CAuthenticator::assignUsernameAndPassword(char const* username, char const* password, bool passwordIsMD5) { if (username == NULL) username = ""; if (password == NULL) password = ""; fUsername = strMyDup(username); fPassword = strMyDup(password); fPasswordIsMD5 = passwordIsMD5; } char const* CAuthenticator::computeDigestResponse(char* cmd, char *url) { // The "response" field is computed as: // md5(md5(::)::md5(:)) // or, if "fPasswordIsMD5" is True: // md5(::md5(:)) char *pAlgorithm = NULL; char *pUsername = NULL; char *pRealm = NULL; char *pPasswd = NULL; char *pNonce = NULL; char *pNonceCount = NULL; char *pCNonce = NULL; char *pQop = NULL; char *pMethod = NULL; char *pUri = NULL; char sNullAlg[] = ""; //"MD5"; char sNullQop[] = ""; //"auth"; //char SessionKey[MD5_SESSION_KEY_LEN+1] = {0}; char Response[MD5_RESPONSE_LEN] = {0}; char SessionKey[33] = {0}; if (fPasswordIsMD5) { strncpy(SessionKey, fPassword, 32); SessionKey[32] = '\0'; // just in case } else { pUsername = unquote(fUsername); pRealm = unquote(fRealm); pPasswd = unquote(fPassword); pNonce = unquote(fNonce); pCNonce = unquote(sNullAlg); pAlgorithm = unquote(sNullAlg); //calculate session key DigestCalcHA1(pAlgorithm, pUsername, pRealm, pPasswd, pNonce, pCNonce, SessionKey); } pQop = unquote(sNullQop); pMethod = cmd; pUri = url; DigestCalcResponse(SessionKey, pNonce, pNonceCount, pCNonce, pQop, pMethod, pUri, (char*)"", Response); char const* result = strMyDup(unquote(Response)); return result; } /************************************** * * CRtspHdr * **************************************/ CRtspHdr::CRtspHdr(const CString& strKey) : m_strKey(strKey) { //Empty } CRtspHdr::CRtspHdr(const CString& strKey, const CString& strVal) : m_strKey(strKey), m_strVal(strVal) { //Empty } const CString& CRtspHdr::GetKey(void) const { return m_strKey; } const CString& CRtspHdr::GetVal(void) const { return m_strVal; } void CRtspHdr::SetVal(const CString& strVal) { m_strVal = strVal; } /************************************** * * CRtspMsg * **************************************/ CRtspMsg::CRtspMsg(void) : m_nRtspVer(0), m_nSeq(0), m_nBufLen(0), m_pbuf(NULL) { //Empty } CRtspMsg::CRtspMsg(const CRtspMsg& other) { CRtspHdrList::ConstIterator itr(other.m_listHdrs.Begin() ); while (itr) { CRtspHdr* pHdr = *itr; m_listHdrs.InsertTail(new CRtspHdr(*pHdr) ); itr++; } m_nBufLen = other.m_nBufLen; m_pbuf = NULL; if (m_nBufLen) { m_pbuf = new Byte[m_nBufLen+1]; memcpy(m_pbuf, other.m_pbuf, m_nBufLen); } } CRtspMsg::~CRtspMsg(void) { while (!m_listHdrs.IsEmpty() ) { CRtspHdr* pHdr = m_listHdrs.RemoveHead(); delete pHdr; } delete[] m_pbuf; m_pbuf = NULL; } const CRtspMsg& CRtspMsg::operator=(const CRtspMsg& other) { m_nRtspVer = other.m_nRtspVer; m_nSeq = other.m_nSeq; while (!m_listHdrs.IsEmpty() ) { CRtspHdr* pHdr = m_listHdrs.RemoveHead(); delete pHdr; } CRtspHdrList::ConstIterator itr(other.m_listHdrs.Begin() ); while (itr) { CRtspHdr* pHdr = *itr; m_listHdrs.InsertTail(new CRtspHdr(*pHdr) ); itr++; } m_nBufLen = other.m_nBufLen; delete[] m_pbuf; m_pbuf = NULL; if (m_nBufLen) { m_pbuf = new Byte[m_nBufLen+1]; memcpy(m_pbuf, other.m_pbuf, m_nBufLen); } return *this; } CRtspMsg::operator CPByte(void) const { return m_pbuf; } Byte CRtspMsg::operator[](UINT32 nPos) const { return GetAt(nPos); } RtspMsgType CRtspMsg::GetType(void) const { return RTSP_TYPE_NONE; } size_t CRtspMsg::GetHdrLen(void) const { size_t nLen = 0; CRtspHdrList::ConstIterator itr(m_listHdrs.Begin() ); while (itr) { CRtspHdr* pHdr = *itr; nLen += (pHdr->GetKey().GetLength() + 2 + pHdr->GetVal().GetLength() + 2); itr++; } return nLen; } size_t CRtspMsg::GetBufLen(void) const { return m_nBufLen; } Byte CRtspMsg::GetAt(UINT32 nPos) const { return m_pbuf[nPos]; } void CRtspMsg::SetAt(UINT32 nPos, Byte by) { m_pbuf[nPos] = by; } void CRtspMsg::GetRtspVer(UINT32* puMajor, UINT32* puMinor) const { assert(puMajor && puMinor); *puMajor = HIWORD(m_nRtspVer); *puMinor = LOWORD(m_nRtspVer); } void CRtspMsg::SetRtspVer(UINT32 uMajor, UINT32 uMinor) { assert(uMajor < 10 && uMinor < 10); m_nRtspVer = MAKEDWORD(uMajor,uMinor); } size_t CRtspMsg::GetHdrCount(void) const { return m_listHdrs.GetCount(); } CString CRtspMsg::GetHdr(const CString& strKey) const { CString strVal; CRtspHdrList::ConstIterator itr(m_listHdrs.Begin() ); while (itr) { CRtspHdr* pHdr = *itr; if (0 == strcasecmp(strKey, pHdr->GetKey() ) ) { strVal = pHdr->GetVal(); break; } itr++; } return strVal; } CRtspHdr* CRtspMsg::GetHdr(UINT32 nIndex) const { CRtspHdrList::ConstIterator itr(m_listHdrs.Begin() ); for (UINT32 n = 0; n < nIndex; n++) { itr++; } return *itr; } void CRtspMsg::SetHdr(const CString& strKey, const CString& strVal) { CRtspHdrList::Iterator itr(m_listHdrs.Begin() ); while (itr) { CRtspHdr* pHdr = *itr; if (0 == strcasecmp(strKey, pHdr->GetKey() ) ) { pHdr->SetVal(strVal); return; } itr++; } m_listHdrs.InsertTail(new CRtspHdr(strKey, strVal) ); } void CRtspMsg::SetHdr(const CRtspHdr& hdrNew) { CRtspHdrList::Iterator itr(m_listHdrs.Begin() ); while (itr) { CRtspHdr* pHdr = *itr; if (hdrNew.GetKey() == pHdr->GetKey() ) { pHdr->SetVal(hdrNew.GetVal() ); return; } itr++; } m_listHdrs.InsertTail(new CRtspHdr(hdrNew) ); } PByte CRtspMsg::GetBuf(void) const { return m_pbuf; } void CRtspMsg::SetBuf(CPByte buf, size_t nLen) { delete[] m_pbuf; m_pbuf = NULL; m_nBufLen = nLen; if (m_nBufLen) { m_pbuf = new Byte[m_nBufLen+1]; memcpy(m_pbuf, buf, m_nBufLen); } } /************************************** * * CRtspRequestMsg * **************************************/ // These correspond with enum RtspVerb and must be sorted static CPCHAR s_pVerbs[] = { "-NONE-", "ANNOUNCE", "DESCRIBE", "GET_PARAMETER", "OPTIONS", "PAUSE", "PLAY", "RECORD", "REDIRECT", "SETUP", "SET_PARAMETER", "TEARDOWN", NULL }; static const UINT32 s_nVerbs = sizeof(s_pVerbs)/sizeof(CPCHAR) - 1; CRtspRequestMsg::CRtspRequestMsg(void) : CRtspMsg() { m_verb = VERB_NONE; //Empty } CRtspRequestMsg::CRtspRequestMsg(const CRtspRequestMsg& other) : CRtspMsg(other) { m_verb = other.m_verb; m_strUrl = other.m_strUrl; UrlParam = other.UrlParam; } CRtspRequestMsg::~CRtspRequestMsg( void ) { //Empty } const CRtspRequestMsg& CRtspRequestMsg::operator=(const CRtspRequestMsg& other) { m_verb = other.m_verb; UrlParam = other.UrlParam; m_strUrl = other.m_strUrl; m_nRtspVer = other.m_nRtspVer; m_nSeq = other.m_nSeq; while (!m_listHdrs.IsEmpty() ) { CRtspHdr* pHdr = m_listHdrs.RemoveHead(); delete pHdr; } CRtspHdrList::ConstIterator itr(other.m_listHdrs.Begin() ); while (itr) { CRtspHdr* pHdr = *itr; m_listHdrs.InsertTail(new CRtspHdr(*pHdr) ); itr++; } m_nBufLen = other.m_nBufLen; delete[] m_pbuf; m_pbuf = NULL; if (m_nBufLen) { m_pbuf = new Byte[m_nBufLen+1]; memcpy(m_pbuf, other.m_pbuf, m_nBufLen); } return *this; } RtspMsgType CRtspRequestMsg::GetType( void ) const { return RTSP_TYPE_REQUEST; } RtspVerb CRtspRequestMsg::GetVerb( void ) const { return m_verb; } CPCHAR CRtspRequestMsg::GetVerbStr( void ) const { return s_pVerbs[m_verb]; } void CRtspRequestMsg::SetVerb( RtspVerb verb ) { assert( verb > VERB_NONE && verb < VERB_LAST ); m_verb = verb; } void CRtspRequestMsg::GetFileName(char filename[64]) { char* url = (char*)(CPCHAR)m_strUrl; char* temp; filename[0] = 0; temp = rindex(url,'/'); if(temp == NULL) { return; } if(strlen(temp) <= 1) { *temp = 0; temp = rindex(url,'/'); if(temp == NULL) { return; } } temp ++; snprintf(filename,64,"%s",temp); } void CRtspRequestMsg::SetVerb( CPCHAR szVerb ) { m_verb = VERB_NONE; int hi = s_nVerbs; int lo = -1; int mid; while( hi-lo > 1 ) { mid = (hi+lo)/2; if( strcmp( szVerb, s_pVerbs[mid] ) <= 0 ) hi = mid; else lo = mid; } if( 0 == strcmp( szVerb, s_pVerbs[hi] ) ) { m_verb = (RtspVerb)hi; } //assert(VERB_NONE == m_verb); } CPCHAR CRtspRequestMsg::GetUrl( void ) const { return (CPCHAR)m_strUrl; } void CRtspRequestMsg::SetUrl( const CString& strUrl ) { m_strUrl = strUrl; } void CRtspRequestMsg::ParserUri(void) { //For example: URL=rtsp://192.168.1.245:554/DevAor=100@192.168.1.245/Type=1/BeginTime=2011-3-16.13:06:12/EndTime=2011-3-16.13:06:23/StreamID=1 //URL=rtsp://172.30.12.93:8554/DevAor=44011188001310000001/StreamID=1 char* p = (char*)(CPCHAR)m_strUrl; if ( ('\0' == *p) || (strcmp(p, "rtsp://") <= 0) ) { printf("%s: url=<%s>, no \"rtsp://\" protocol header!\n", __FUNCTION__, p); return; } p += 7; //7 = strlen("rtsp://"); while (*p != '\0') { if (*p == '/') { p++; char buf1[64+1] = {0}, buf2[64+1] = {0}, buf3[64+1] = {0}, buf4[64+1] = {0}, buf5[64+1] = {0}, buf6[64+1] = {0}, buf7[64+1] = {0}, buf8[64+1] = {0}; sscanf(p, "%64[^/]/%64[^/]/%64[^/]/%64[^/]/%64[^/]/%64[^/]/%64[^/]/%64[^/]/", buf1, buf2, buf3, buf4, buf5, buf6, buf7, buf8); if (strlen(buf1) > 0) //The URL is right if 4th parameter is no empty. { UrlParam.LineTo(buf1); UrlParam.LineTo(buf2); UrlParam.LineTo(buf3); UrlParam.LineTo(buf4); UrlParam.LineTo(buf5); UrlParam.LineTo(buf6); UrlParam.LineTo(buf7); UrlParam.LineTo(buf8); } else { printf("%s: url=<%s>, some errors!\n", __FUNCTION__, (char*)(CPCHAR)m_strUrl); } break; } p++; } } //Transfer protocol SOCKETTYPE_E CRtspRequestMsg::ParserClientStreamTransType(void) { CString Transport = GetHdr("Transport"); char* p = strstr( (char *)(CPCHAR)Transport, "RTP/AVP/TCP"); if(p != NULL) { return SOCKETTYPE_TCPSERVER; } //UDP return SOCKETTYPE_UDP; } bool CRtspRequestMsg::ParserClientAddr(char * ClientAddr) { if(ClientAddr == NULL) { return false; } CString Transport = GetHdr("Transport"); char* p = strstr( (char *)(CPCHAR)Transport, "destination="); if(p == NULL) { return false; } p += strlen("destination="); char * temp = strchr(p,';'); if(temp == NULL) { return false; } if(temp - p < 7 || temp - p > 15)//"1.1.1.1 255.255.255.255" { return false; } memcpy(ClientAddr,p,temp - p); ClientAddr[temp - p] = 0; return true; } UINT16 CRtspRequestMsg::ParserClientPort(void) { CString Transport = GetHdr("Transport"); UINT16 port = 0; char* p = strstr( (char*)(CPCHAR)Transport, "client_port"); if (p != NULL) { p += 11; //11 = strlen(client_port=) while ( (*p != '\0') && ( (*p == ' ') || (*p == '=') ) ) p++; port = (*p != '\0') ? atoi(p) : 0; } return port; } bool CRtspRequestMsg::ParserClientMulticast(void) { CString Transport = GetHdr("Transport"); char* p = strstr( (char *)(CPCHAR)Transport, "multicast"); if(p != NULL) { return true; } return false; } UINT16 CRtspRequestMsg::ParserClientMulticastPort(void) { CString Transport = GetHdr("Transport"); UINT16 port = 0; char* p = strstr( (char*)(CPCHAR)Transport, "port"); if (p != NULL) { p += 11; //11 = strlen(client_port=) while ( (*p != '\0') && ( (*p == ' ') || (*p == '=') ) ) p++; port = (*p != '\0') ? atoi(p) : 0; } return port; } /************************************** * * CRtspResponseMsg * **************************************/ struct StatusMapEntry {UINT32 nCode; CPCHAR szName;}; // These must be sorted static StatusMapEntry s_mapStatus[] = { { 100, "Continue" }, { 200, "OK" }, { 201, "Created" }, { 250, "Low on Storage Space" }, { 300, "Multiple Choices" }, { 301, "Moved Permanently" }, { 302, "Moved Temporarily" }, { 303, "See Other" }, { 304, "Not Modified" }, { 305, "Use Proxy" }, { 400, "Bad Request" }, { 401, "Unauthorized" }, { 402, "Payment Required" }, { 403, "Forbidden" }, { 404, "Not Found" }, { 405, "Method Not Allowed" }, { 406, "Not Acceptable" }, { 407, "Proxy Authentication Required" }, { 408, "Request Time-out" }, { 410, "Gone" }, { 411, "Length Required" }, { 412, "Precondition Failed" }, { 413, "Request Entity Too Large" }, { 414, "Request-URI Too Large" }, { 415, "Unsupported Media Type" }, { 451, "Parameter Not Understood" }, { 452, "Conference Not Found" }, { 453, "Not Enough Bandwidth" }, { 454, "Session Not Found" }, { 455, "Method Not Valid in This State" }, { 456, "Header Field Not Valid for Resource" }, { 457, "Invalid Range" }, { 458, "Parameter Is Read-Only" }, { 459, "Aggregate operation not allowed" }, { 460, "Only aggregate operation allowed" }, { 461, "Unsupported transport" }, { 462, "Destination unreachable" }, { 500, "Internal Server Error" }, { 501, "Not Implemented" }, { 502, "Bad Gateway" }, { 503, "Service Unavailable" }, { 504, "Gateway Time-out" }, { 505, "RTSP Version not supported" }, { 551, "Option not supported" }, { 0, NULL } }; static const UINT32 s_nStatusEntries = sizeof(s_mapStatus)/sizeof(StatusMapEntry) - 1; CRtspResponseMsg::CRtspResponseMsg(void) : CRtspMsg(), m_nCode(0) { //Empty } CRtspResponseMsg::CRtspResponseMsg(const CRtspResponseMsg& other) : CRtspMsg(other) { m_nCode = other.m_nCode; m_strStatusMsg = other.m_strStatusMsg; } CRtspResponseMsg::~CRtspResponseMsg( void ) { //Empty } const CRtspResponseMsg& CRtspResponseMsg::operator=(const CRtspResponseMsg& other) { m_nCode = other.m_nCode; m_strStatusMsg = other.m_strStatusMsg; return *this; } RtspMsgType CRtspResponseMsg::GetType(void) const { return RTSP_TYPE_RESPONSE; } UINT32 CRtspResponseMsg::GetStatusCode(void) const { return m_nCode; } const CString& CRtspResponseMsg::GetStatusMsg(void) const { return m_strStatusMsg; } void CRtspResponseMsg::SetStatus( UINT32 nCode, CPCHAR szMsg /* = NULL */ ) { assert( nCode >= 100 && nCode <= 999 ); m_nCode = nCode; if( !szMsg ) { szMsg = "Unknown"; int hi = s_nStatusEntries; int lo = -1; int mid; while( hi-lo > 1 ) { mid = (hi+lo)/2; if( nCode <= s_mapStatus[mid].nCode ) hi = mid; else lo = mid; } if( nCode == s_mapStatus[hi].nCode ) { szMsg = s_mapStatus[hi].szName; } } m_strStatusMsg = szMsg; }