liveMedia/SIPClient.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2013 Live Networks, Inc.  All rights reserved.
00018 // A generic SIP client
00019 // Implementation
00020 
00021 #include "SIPClient.hh"
00022 #include "GroupsockHelper.hh"
00023 
00024 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00025 #define _strncasecmp _strnicmp
00026 #else
00027 #define _strncasecmp strncasecmp
00028 #endif
00029 
00031 
00032 SIPClient* SIPClient
00033 ::createNew(UsageEnvironment& env,
00034             unsigned char desiredAudioRTPPayloadFormat,
00035             char const* mimeSubtype,
00036             int verbosityLevel, char const* applicationName) {
00037   return new SIPClient(env, desiredAudioRTPPayloadFormat, mimeSubtype,
00038                        verbosityLevel, applicationName);
00039 }
00040 
00041 SIPClient::SIPClient(UsageEnvironment& env,
00042                      unsigned char desiredAudioRTPPayloadFormat,
00043                      char const* mimeSubtype,
00044                      int verbosityLevel, char const* applicationName)
00045   : Medium(env),
00046     fT1(500000 /* 500 ms */),
00047     fDesiredAudioRTPPayloadFormat(desiredAudioRTPPayloadFormat),
00048     fVerbosityLevel(verbosityLevel),
00049     fCSeq(0), fURL(NULL), fURLSize(0),
00050     fToTagStr(NULL), fToTagStrSize(0),
00051     fUserName(NULL), fUserNameSize(0),
00052     fInviteSDPDescription(NULL), fInviteSDPDescriptionReturned(NULL),
00053     fInviteCmd(NULL), fInviteCmdSize(0) {
00054   if (mimeSubtype == NULL) mimeSubtype = "";
00055   fMIMESubtype = strDup(mimeSubtype);
00056   fMIMESubtypeSize = strlen(fMIMESubtype);
00057 
00058   if (applicationName == NULL) applicationName = "";
00059   fApplicationName = strDup(applicationName);
00060   fApplicationNameSize = strlen(fApplicationName);
00061 
00062   struct in_addr ourAddress;
00063   ourAddress.s_addr = ourIPAddress(env); // hack
00064   fOurAddressStr = strDup(AddressString(ourAddress).val());
00065   fOurAddressStrSize = strlen(fOurAddressStr);
00066 
00067   fOurSocket = new Groupsock(env, ourAddress, 0, 255);
00068   if (fOurSocket == NULL) {
00069     env << "ERROR: Failed to create socket for addr "
00070         << fOurAddressStr << ": "
00071         << env.getResultMsg() << "\n";
00072   }
00073 
00074   // Now, find out our source port number.  Hack: Do this by first trying to
00075   // send a 0-length packet, so that the "getSourcePort()" call will work.
00076   fOurSocket->output(envir(), 255, (unsigned char*)"", 0);
00077   Port srcPort(0);
00078   getSourcePort(env, fOurSocket->socketNum(), srcPort);
00079   if (srcPort.num() != 0) {
00080     fOurPortNum = ntohs(srcPort.num());
00081   } else {
00082     // No luck.  Try again using a default port number:
00083     fOurPortNum = 5060;
00084     delete fOurSocket;
00085     fOurSocket = new Groupsock(env, ourAddress, fOurPortNum, 255);
00086     if (fOurSocket == NULL) {
00087       env << "ERROR: Failed to create socket for addr "
00088           << fOurAddressStr << ", port "
00089           << fOurPortNum << ": "
00090           << env.getResultMsg() << "\n";
00091     }
00092   }
00093 
00094   // Set various headers to be used in each request:
00095   char const* formatStr;
00096   unsigned headerSize;
00097 
00098   // Set the "User-Agent:" header:
00099   char const* const libName = "LIVE555 Streaming Media v";
00100   char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
00101   char const* libPrefix; char const* libSuffix;
00102   if (applicationName == NULL || applicationName[0] == '\0') {
00103     applicationName = libPrefix = libSuffix = "";
00104   } else {
00105     libPrefix = " (";
00106     libSuffix = ")";
00107   }
00108   formatStr = "User-Agent: %s%s%s%s%s\r\n";
00109   headerSize
00110     = strlen(formatStr) + fApplicationNameSize + strlen(libPrefix)
00111     + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix);
00112   fUserAgentHeaderStr = new char[headerSize];
00113   sprintf(fUserAgentHeaderStr, formatStr,
00114           applicationName, libPrefix, libName, libVersionStr, libSuffix);
00115   fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);
00116 
00117   reset();
00118 }
00119 
00120 SIPClient::~SIPClient() {
00121   reset();
00122 
00123   delete[] fUserAgentHeaderStr;
00124   delete fOurSocket;
00125   delete[] (char*)fOurAddressStr;
00126   delete[] (char*)fApplicationName;
00127   delete[] (char*)fMIMESubtype;
00128 }
00129 
00130 void SIPClient::reset() {
00131   fWorkingAuthenticator = NULL;
00132   delete[] fInviteCmd; fInviteCmd = NULL; fInviteCmdSize = 0;
00133   delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;
00134 
00135   delete[] (char*)fUserName; fUserName = strDup(fApplicationName);
00136   fUserNameSize = strlen(fUserName);
00137 
00138   fValidAuthenticator.reset();
00139 
00140   delete[] (char*)fToTagStr; fToTagStr = NULL; fToTagStrSize = 0;
00141   fServerPortNum = 0;
00142   fServerAddress.s_addr = 0;
00143   delete[] (char*)fURL; fURL = NULL; fURLSize = 0;
00144 }
00145 
00146 void SIPClient::setProxyServer(unsigned proxyServerAddress,
00147                                portNumBits proxyServerPortNum) {
00148   fServerAddress.s_addr = proxyServerAddress;
00149   fServerPortNum = proxyServerPortNum;
00150   if (fOurSocket != NULL) {
00151     fOurSocket->changeDestinationParameters(fServerAddress,
00152                                             fServerPortNum, 255);
00153   }
00154 }
00155 
00156 static char* getLine(char* startOfLine) {
00157   // returns the start of the next line, or NULL if none
00158   for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
00159     if (*ptr == '\r' || *ptr == '\n') {
00160       // We found the end of the line
00161       *ptr++ = '\0';
00162       if (*ptr == '\n') ++ptr;
00163       return ptr;
00164     }
00165   }
00166 
00167   return NULL;
00168 }
00169 
00170 char* SIPClient::invite(char const* url, Authenticator* authenticator) {
00171   // First, check whether "url" contains a username:password to be used:
00172   char* username; char* password;
00173   if (authenticator == NULL
00174       && parseSIPURLUsernamePassword(url, username, password)) {
00175     char* result = inviteWithPassword(url, username, password);
00176     delete[] username; delete[] password; // they were dynamically allocated
00177     return result;
00178   }
00179 
00180   if (!processURL(url)) return NULL;
00181 
00182   delete[] (char*)fURL; fURL = strDup(url);
00183   fURLSize = strlen(fURL);
00184 
00185   fCallId = our_random32();
00186   fFromTag = our_random32();
00187 
00188   return invite1(authenticator);
00189 }
00190 
00191 char* SIPClient::invite1(Authenticator* authenticator) {
00192   do {
00193     // Send the INVITE command:
00194 
00195     // First, construct an authenticator string:
00196     fValidAuthenticator.reset();
00197     fWorkingAuthenticator = authenticator;
00198     char* authenticatorStr
00199       = createAuthenticatorString(fWorkingAuthenticator, "INVITE", fURL);
00200 
00201     // Then, construct the SDP description to be sent in the INVITE:
00202     char* rtpmapLine;
00203     unsigned rtpmapLineSize;
00204     if (fMIMESubtypeSize > 0) {
00205       char const* const rtpmapFmt =
00206         "a=rtpmap:%u %s/8000\r\n";
00207       unsigned rtpmapFmtSize = strlen(rtpmapFmt)
00208         + 3 /* max char len */ + fMIMESubtypeSize;
00209       rtpmapLine = new char[rtpmapFmtSize];
00210       sprintf(rtpmapLine, rtpmapFmt,
00211               fDesiredAudioRTPPayloadFormat, fMIMESubtype);
00212       rtpmapLineSize = strlen(rtpmapLine);
00213     } else {
00214       // Static payload type => no "a=rtpmap:" line
00215       rtpmapLine = strDup("");
00216       rtpmapLineSize = 0;
00217     }
00218     char const* const inviteSDPFmt =
00219       "v=0\r\n"
00220       "o=- %u %u IN IP4 %s\r\n"
00221       "s=%s session\r\n"
00222       "c=IN IP4 %s\r\n"
00223       "t=0 0\r\n"
00224       "m=audio %u RTP/AVP %u\r\n"
00225       "%s";
00226     unsigned inviteSDPFmtSize = strlen(inviteSDPFmt)
00227       + 20 /* max int len */ + 20 + fOurAddressStrSize
00228       + fApplicationNameSize
00229       + fOurAddressStrSize
00230       + 5 /* max short len */ + 3 /* max char len */
00231       + rtpmapLineSize;
00232     delete[] fInviteSDPDescription;
00233     fInviteSDPDescription = new char[inviteSDPFmtSize];
00234     sprintf(fInviteSDPDescription, inviteSDPFmt,
00235             fCallId, fCSeq, fOurAddressStr,
00236             fApplicationName,
00237             fOurAddressStr,
00238             fClientStartPortNum, fDesiredAudioRTPPayloadFormat,
00239             rtpmapLine);
00240     unsigned inviteSDPSize = strlen(fInviteSDPDescription);
00241     delete[] rtpmapLine;
00242 
00243     char const* const cmdFmt =
00244       "INVITE %s SIP/2.0\r\n"
00245       "From: %s <sip:%s@%s>;tag=%u\r\n"
00246       "Via: SIP/2.0/UDP %s:%u\r\n"
00247       "Max-Forwards: 70\r\n"
00248       "To: %s\r\n"
00249       "Contact: sip:%s@%s:%u\r\n"
00250       "Call-ID: %u@%s\r\n"
00251       "CSeq: %d INVITE\r\n"
00252       "Content-Type: application/sdp\r\n"
00253       "%s" /* Proxy-Authorization: line (if any) */
00254       "%s" /* User-Agent: line */
00255       "Content-Length: %d\r\n\r\n"
00256       "%s";
00257     unsigned inviteCmdSize = strlen(cmdFmt)
00258       + fURLSize
00259       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00260       + fOurAddressStrSize + 5 /* max port len */
00261       + fURLSize
00262       + fUserNameSize + fOurAddressStrSize + 5
00263       + 20 + fOurAddressStrSize
00264       + 20
00265       + strlen(authenticatorStr)
00266       + fUserAgentHeaderStrSize
00267       + 20
00268       + inviteSDPSize;
00269     delete[] fInviteCmd; fInviteCmd = new char[inviteCmdSize];
00270     sprintf(fInviteCmd, cmdFmt,
00271             fURL,
00272             fUserName, fUserName, fOurAddressStr, fFromTag,
00273             fOurAddressStr, fOurPortNum,
00274             fURL,
00275             fUserName, fOurAddressStr, fOurPortNum,
00276             fCallId, fOurAddressStr,
00277             ++fCSeq,
00278             authenticatorStr,
00279             fUserAgentHeaderStr,
00280             inviteSDPSize,
00281             fInviteSDPDescription);
00282     fInviteCmdSize = strlen(fInviteCmd);
00283     delete[] authenticatorStr;
00284 
00285     // Before sending the "INVITE", arrange to handle any response packets,
00286     // and set up timers:
00287     fInviteClientState = Calling;
00288     fEventLoopStopFlag = 0;
00289     TaskScheduler& sched = envir().taskScheduler(); // abbrev.
00290     sched.turnOnBackgroundReadHandling(fOurSocket->socketNum(),
00291                                        &inviteResponseHandler, this);
00292     fTimerALen = 1*fT1; // initially
00293     fTimerACount = 0; // initially
00294     fTimerA = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
00295     fTimerB = sched.scheduleDelayedTask(64*fT1, timerBHandler, this);
00296     fTimerD = NULL; // for now
00297 
00298     if (!sendINVITE()) break;
00299 
00300     // Enter the event loop, to handle response packets, and timeouts:
00301     envir().taskScheduler().doEventLoop(&fEventLoopStopFlag);
00302 
00303     // We're finished with this "INVITE".
00304     // Turn off response handling and timers:
00305     sched.turnOffBackgroundReadHandling(fOurSocket->socketNum());
00306     sched.unscheduleDelayedTask(fTimerA);
00307     sched.unscheduleDelayedTask(fTimerB);
00308     sched.unscheduleDelayedTask(fTimerD);
00309 
00310     // NOTE: We return the SDP description that we used in the "INVITE",
00311     // not the one that we got from the server.
00312     // ##### Later: match the codecs in the response (offer, answer) #####
00313     if (fInviteSDPDescription != NULL) {
00314       return strDup(fInviteSDPDescription);
00315     }
00316   } while (0);
00317 
00318   return NULL;
00319 }
00320 
00321 void SIPClient::inviteResponseHandler(void* clientData, int /*mask*/) {
00322   SIPClient* client = (SIPClient*)clientData;
00323   unsigned responseCode = client->getResponseCode();
00324   client->doInviteStateMachine(responseCode);
00325 }
00326 
00327 // Special 'response codes' that represent timers expiring:
00328 unsigned const timerAFires = 0xAAAAAAAA;
00329 unsigned const timerBFires = 0xBBBBBBBB;
00330 unsigned const timerDFires = 0xDDDDDDDD;
00331 
00332 void SIPClient::timerAHandler(void* clientData) {
00333   SIPClient* client = (SIPClient*)clientData;
00334   if (client->fVerbosityLevel >= 1) {
00335     client->envir() << "RETRANSMISSION " << ++client->fTimerACount
00336                     << ", after " << client->fTimerALen/1000000.0
00337                     << " additional seconds\n";
00338   }
00339   client->doInviteStateMachine(timerAFires);
00340 }
00341 
00342 void SIPClient::timerBHandler(void* clientData) {
00343   SIPClient* client = (SIPClient*)clientData;
00344   if (client->fVerbosityLevel >= 1) {
00345     client->envir() << "RETRANSMISSION TIMEOUT, after "
00346                     << 64*client->fT1/1000000.0 << " seconds\n";
00347     fflush(stderr);
00348   }
00349   client->doInviteStateMachine(timerBFires);
00350 }
00351 
00352 void SIPClient::timerDHandler(void* clientData) {
00353   SIPClient* client = (SIPClient*)clientData;
00354   if (client->fVerbosityLevel >= 1) {
00355     client->envir() << "TIMER D EXPIRED\n";
00356   }
00357   client->doInviteStateMachine(timerDFires);
00358 }
00359 
00360 void SIPClient::doInviteStateMachine(unsigned responseCode) {
00361   // Implement the state transition diagram (RFC 3261, Figure 5)
00362   TaskScheduler& sched = envir().taskScheduler(); // abbrev.
00363   switch (fInviteClientState) {
00364     case Calling: {
00365       if (responseCode == timerAFires) {
00366         // Restart timer A (with double the timeout interval):
00367         fTimerALen *= 2;
00368         fTimerA
00369           = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
00370 
00371         fInviteClientState = Calling;
00372         if (!sendINVITE()) doInviteStateTerminated(0);
00373       } else {
00374         // Turn off timers A & B before moving to a new state:
00375         sched.unscheduleDelayedTask(fTimerA);
00376         sched.unscheduleDelayedTask(fTimerB);
00377 
00378         if (responseCode == timerBFires) {
00379           envir().setResultMsg("No response from server");
00380           doInviteStateTerminated(0);
00381         } else if (responseCode >= 100 && responseCode <= 199) {
00382           fInviteClientState = Proceeding;
00383         } else if (responseCode >= 200 && responseCode <= 299) {
00384           doInviteStateTerminated(responseCode);
00385         } else if (responseCode >= 400 && responseCode <= 499) {
00386           doInviteStateTerminated(responseCode);
00387               // this isn't what the spec says, but it seems right...
00388         } else if (responseCode >= 300 && responseCode <= 699) {
00389           fInviteClientState = Completed;
00390           fTimerD
00391             = sched.scheduleDelayedTask(32000000, timerDHandler, this);
00392           if (!sendACK()) doInviteStateTerminated(0);
00393         }
00394       }
00395       break;
00396     }
00397 
00398     case Proceeding: {
00399       if (responseCode >= 100 && responseCode <= 199) {
00400         fInviteClientState = Proceeding;
00401       } else if (responseCode >= 200 && responseCode <= 299) {
00402         doInviteStateTerminated(responseCode);
00403       } else if (responseCode >= 400 && responseCode <= 499) {
00404         doInviteStateTerminated(responseCode);
00405             // this isn't what the spec says, but it seems right...
00406       } else if (responseCode >= 300 && responseCode <= 699) {
00407         fInviteClientState = Completed;
00408         fTimerD = sched.scheduleDelayedTask(32000000, timerDHandler, this);
00409         if (!sendACK()) doInviteStateTerminated(0);
00410       }
00411       break;
00412     }
00413 
00414     case Completed: {
00415       if (responseCode == timerDFires) {
00416         envir().setResultMsg("Transaction terminated");
00417         doInviteStateTerminated(0);
00418       } else if (responseCode >= 300 && responseCode <= 699) {
00419         fInviteClientState = Completed;
00420         if (!sendACK()) doInviteStateTerminated(0);
00421       }
00422       break;
00423     }
00424 
00425     case Terminated: {
00426         doInviteStateTerminated(responseCode);
00427         break;
00428     }
00429   }
00430 }
00431 
00432 void SIPClient::doInviteStateTerminated(unsigned responseCode) {
00433   fInviteClientState = Terminated; // FWIW...
00434   if (responseCode < 200 || responseCode > 299) {
00435     // We failed, so return NULL;
00436     delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;
00437     delete[] fInviteSDPDescriptionReturned; fInviteSDPDescriptionReturned = NULL;
00438   }
00439 
00440   // Unblock the event loop:
00441   fEventLoopStopFlag = ~0;
00442 }
00443 
00444 Boolean SIPClient::sendINVITE() {
00445   if (!sendRequest(fInviteCmd, fInviteCmdSize)) {
00446     envir().setResultErrMsg("INVITE send() failed: ");
00447     return False;
00448   }
00449   return True;
00450 }
00451 
00452 unsigned SIPClient::getResponseCode() {
00453   unsigned responseCode = 0;
00454   do {
00455     // Get the response from the server:
00456     unsigned const readBufSize = 10000;
00457     char readBuffer[readBufSize+1]; char* readBuf = readBuffer;
00458 
00459     char* firstLine = NULL;
00460     char* nextLineStart = NULL;
00461     unsigned bytesRead = getResponse(readBuf, readBufSize);
00462     if (bytesRead == 0) break;
00463     if (fVerbosityLevel >= 1) {
00464       envir() << "Received INVITE response: " << readBuf << "\n";
00465     }
00466 
00467     // Inspect the first line to get the response code:
00468     firstLine = readBuf;
00469     nextLineStart = getLine(firstLine);
00470     if (!parseResponseCode(firstLine, responseCode)) break;
00471 
00472     if (responseCode != 200) {
00473       if (responseCode >= 400 && responseCode <= 499
00474           && fWorkingAuthenticator != NULL) {
00475         // We have an authentication failure, so fill in
00476         // "*fWorkingAuthenticator" using the contents of a following
00477         // "Proxy-Authenticate:" line.  (Once we compute a 'response' for
00478         // "fWorkingAuthenticator", it can be used in a subsequent request
00479         // - that will hopefully succeed.)
00480         char* lineStart;
00481         while (1) {
00482           lineStart = nextLineStart;
00483           if (lineStart == NULL) break;
00484 
00485           nextLineStart = getLine(lineStart);
00486           if (lineStart[0] == '\0') break; // this is a blank line
00487 
00488           char* realm = strDupSize(lineStart);
00489           char* nonce = strDupSize(lineStart);
00490           // ##### Check for the format of "Proxy-Authenticate:" lines from
00491           // ##### known server types.
00492           // ##### This is a crock! We should make the parsing more general
00493           Boolean foundAuthenticateHeader = False;
00494           if (
00495               // Asterisk #####
00496               sscanf(lineStart, "Proxy-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"",
00497                      realm, nonce) == 2 ||
00498               // Cisco ATA #####
00499               sscanf(lineStart, "Proxy-Authenticate: Digest algorithm=MD5,domain=\"%*[^\"]\",nonce=\"%[^\"]\", realm=\"%[^\"]\"",
00500                      nonce, realm) == 2) {
00501             fWorkingAuthenticator->setRealmAndNonce(realm, nonce);
00502             foundAuthenticateHeader = True;
00503           }
00504           delete[] realm; delete[] nonce;
00505           if (foundAuthenticateHeader) break;
00506         }
00507       }
00508       envir().setResultMsg("cannot handle INVITE response: ", firstLine);
00509       break;
00510     }
00511 
00512     // Skip every subsequent header line, until we see a blank line.
00513     // While doing so, check for "To:" and "Content-Length:" lines.
00514     // The remaining data is assumed to be the SDP descriptor that we want.
00515     // We should really do some more checking on the headers here - e.g., to
00516     // check for "Content-type: application/sdp", "CSeq", etc. #####
00517     int contentLength = -1;
00518     char* lineStart;
00519     while (1) {
00520       lineStart = nextLineStart;
00521       if (lineStart == NULL) break;
00522 
00523       nextLineStart = getLine(lineStart);
00524       if (lineStart[0] == '\0') break; // this is a blank line
00525 
00526       char* toTagStr = strDupSize(lineStart);
00527       if (sscanf(lineStart, "To:%*[^;]; tag=%s", toTagStr) == 1) {
00528         delete[] (char*)fToTagStr; fToTagStr = strDup(toTagStr);
00529         fToTagStrSize = strlen(fToTagStr);
00530       }
00531       delete[] toTagStr;
00532 
00533       if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1
00534           || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) {
00535         if (contentLength < 0) {
00536           envir().setResultMsg("Bad \"Content-Length:\" header: \"",
00537                                lineStart, "\"");
00538           break;
00539         }
00540       }
00541     }
00542 
00543     // We're now at the end of the response header lines
00544     if (lineStart == NULL) {
00545       envir().setResultMsg("no content following header lines: ", readBuf);
00546       break;
00547     }
00548 
00549     // Use the remaining data as the SDP descr, but first, check
00550     // the "Content-Length:" header (if any) that we saw.  We may need to
00551     // read more data, or we may have extraneous data in the buffer.
00552     char* bodyStart = nextLineStart;
00553     if (bodyStart != NULL && contentLength >= 0) {
00554       // We saw a "Content-Length:" header
00555       unsigned numBodyBytes = &readBuf[bytesRead] - bodyStart;
00556       if (contentLength > (int)numBodyBytes) {
00557         // We need to read more data.  First, make sure we have enough
00558         // space for it:
00559         unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
00560 #ifdef USING_TCP
00561         // THIS CODE WORKS ONLY FOR TCP: #####
00562         unsigned remainingBufferSize
00563           = readBufSize - (bytesRead + (readBuf - readBuffer));
00564         if (numExtraBytesNeeded > remainingBufferSize) {
00565           char tmpBuf[200];
00566           sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-Length:\" %d (need a buffer size of >= %d bytes\n",
00567                   readBufSize, contentLength,
00568                   readBufSize + numExtraBytesNeeded - remainingBufferSize);
00569           envir().setResultMsg(tmpBuf);
00570           break;
00571         }
00572 
00573         // Keep reading more data until we have enough:
00574         if (fVerbosityLevel >= 1) {
00575           envir() << "Need to read " << numExtraBytesNeeded
00576                   << " extra bytes\n";
00577         }
00578         while (numExtraBytesNeeded > 0) {
00579           char* ptr = &readBuf[bytesRead];
00580           unsigned bytesRead2;
00581           struct sockaddr_in fromAddr;
00582           Boolean readSuccess
00583             = fOurSocket->handleRead((unsigned char*)ptr,
00584                                      numExtraBytesNeeded,
00585                                      bytesRead2, fromAddr);
00586           if (!readSuccess) break;
00587           ptr[bytesRead2] = '\0';
00588           if (fVerbosityLevel >= 1) {
00589             envir() << "Read " << bytesRead2
00590                     << " extra bytes: " << ptr << "\n";
00591           }
00592 
00593           bytesRead += bytesRead2;
00594           numExtraBytesNeeded -= bytesRead2;
00595         }
00596 #endif
00597         if (numExtraBytesNeeded > 0) break; // one of the reads failed
00598       }
00599 
00600       bodyStart[contentLength] = '\0'; // trims any extra data
00601       delete[] fInviteSDPDescriptionReturned; fInviteSDPDescriptionReturned = strDup(bodyStart);
00602     }
00603   } while (0);
00604 
00605   return responseCode;
00606 }
00607 
00608 char* SIPClient::inviteWithPassword(char const* url, char const* username,
00609                                     char const* password) {
00610   delete[] (char*)fUserName; fUserName = strDup(username);
00611   fUserNameSize = strlen(fUserName);
00612 
00613   Authenticator authenticator(username, password);
00614   char* inviteResult = invite(url, &authenticator);
00615   if (inviteResult != NULL) {
00616     // We are already authorized
00617     return inviteResult;
00618   }
00619 
00620   // The "realm" and "nonce" fields should have been filled in:
00621   if (authenticator.realm() == NULL || authenticator.nonce() == NULL) {
00622     // We haven't been given enough information to try again, so fail:
00623     return NULL;
00624   }
00625 
00626   // Try again (but with the same CallId):
00627   inviteResult = invite1(&authenticator);
00628   if (inviteResult != NULL) {
00629     // The authenticator worked, so use it in future requests:
00630     fValidAuthenticator = authenticator;
00631   }
00632 
00633   return inviteResult;
00634 }
00635 
00636 Boolean SIPClient::sendACK() {
00637   char* cmd = NULL;
00638   do {
00639     char const* const cmdFmt =
00640       "ACK %s SIP/2.0\r\n"
00641       "From: %s <sip:%s@%s>;tag=%u\r\n"
00642       "Via: SIP/2.0/UDP %s:%u\r\n"
00643       "Max-Forwards: 70\r\n"
00644       "To: %s;tag=%s\r\n"
00645       "Call-ID: %u@%s\r\n"
00646       "CSeq: %d ACK\r\n"
00647       "Content-Length: 0\r\n\r\n";
00648     unsigned cmdSize = strlen(cmdFmt)
00649       + fURLSize
00650       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00651       + fOurAddressStrSize + 5 /* max port len */
00652       + fURLSize + fToTagStrSize
00653       + 20 + fOurAddressStrSize
00654       + 20;
00655     cmd = new char[cmdSize];
00656     sprintf(cmd, cmdFmt,
00657             fURL,
00658             fUserName, fUserName, fOurAddressStr, fFromTag,
00659             fOurAddressStr, fOurPortNum,
00660             fURL, fToTagStr,
00661             fCallId, fOurAddressStr,
00662             fCSeq /* note: it's the same as before; not incremented */);
00663 
00664     if (!sendRequest(cmd, strlen(cmd))) {
00665       envir().setResultErrMsg("ACK send() failed: ");
00666       break;
00667     }
00668 
00669     delete[] cmd;
00670     return True;
00671   } while (0);
00672 
00673   delete[] cmd;
00674   return False;
00675 }
00676 
00677 Boolean SIPClient::sendBYE() {
00678   // NOTE: This should really be retransmitted, for reliability #####
00679   char* cmd = NULL;
00680   do {
00681     char const* const cmdFmt =
00682       "BYE %s SIP/2.0\r\n"
00683       "From: %s <sip:%s@%s>;tag=%u\r\n"
00684       "Via: SIP/2.0/UDP %s:%u\r\n"
00685       "Max-Forwards: 70\r\n"
00686       "To: %s;tag=%s\r\n"
00687       "Call-ID: %u@%s\r\n"
00688       "CSeq: %d BYE\r\n"
00689       "Content-Length: 0\r\n\r\n";
00690     unsigned cmdSize = strlen(cmdFmt)
00691       + fURLSize
00692       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00693       + fOurAddressStrSize + 5 /* max port len */
00694       + fURLSize + fToTagStrSize
00695       + 20 + fOurAddressStrSize
00696       + 20;
00697     cmd = new char[cmdSize];
00698     sprintf(cmd, cmdFmt,
00699             fURL,
00700             fUserName, fUserName, fOurAddressStr, fFromTag,
00701             fOurAddressStr, fOurPortNum,
00702             fURL, fToTagStr,
00703             fCallId, fOurAddressStr,
00704             ++fCSeq);
00705 
00706     if (!sendRequest(cmd, strlen(cmd))) {
00707       envir().setResultErrMsg("BYE send() failed: ");
00708       break;
00709     }
00710 
00711     delete[] cmd;
00712     return True;
00713   } while (0);
00714 
00715   delete[] cmd;
00716   return False;
00717 }
00718 
00719 Boolean SIPClient::processURL(char const* url) {
00720   do {
00721     // If we don't already have a server address/port, then
00722     // get these by parsing the URL:
00723     if (fServerAddress.s_addr == 0) {
00724       NetAddress destAddress;
00725       if (!parseSIPURL(envir(), url, destAddress, fServerPortNum)) break;
00726       fServerAddress.s_addr = *(unsigned*)(destAddress.data());
00727 
00728       if (fOurSocket != NULL) {
00729         fOurSocket->changeDestinationParameters(fServerAddress,
00730                                                 fServerPortNum, 255);
00731       }
00732     }
00733 
00734     return True;
00735   } while (0);
00736 
00737   return False;
00738 }
00739 
00740 Boolean SIPClient::parseSIPURL(UsageEnvironment& env, char const* url,
00741                                NetAddress& address,
00742                                portNumBits& portNum) {
00743   do {
00744     // Parse the URL as "sip:<username>@<address>:<port>/<etc>"
00745     // (with ":<port>" and "/<etc>" optional)
00746     // Also, skip over any "<username>[:<password>]@" preceding <address>
00747     char const* prefix = "sip:";
00748     unsigned const prefixLength = 4;
00749     if (_strncasecmp(url, prefix, prefixLength) != 0) {
00750       env.setResultMsg("URL is not of the form \"", prefix, "\"");
00751       break;
00752     }
00753 
00754     unsigned const parseBufferSize = 100;
00755     char parseBuffer[parseBufferSize];
00756     unsigned addressStartIndex = prefixLength;
00757     while (url[addressStartIndex] != '\0'
00758            && url[addressStartIndex++] != '@') {}
00759     char const* from = &url[addressStartIndex];
00760 
00761     // Skip over any "<username>[:<password>]@"
00762     char const* from1 = from;
00763     while (*from1 != '\0' && *from1 != '/') {
00764       if (*from1 == '@') {
00765         from = ++from1;
00766         break;
00767       }
00768       ++from1;
00769     }
00770 
00771     char* to = &parseBuffer[0];
00772     unsigned i;
00773     for (i = 0; i < parseBufferSize; ++i) {
00774       if (*from == '\0' || *from == ':' || *from == '/') {
00775         // We've completed parsing the address
00776         *to = '\0';
00777         break;
00778       }
00779       *to++ = *from++;
00780     }
00781     if (i == parseBufferSize) {
00782       env.setResultMsg("URL is too long");
00783       break;
00784     }
00785 
00786     NetAddressList addresses(parseBuffer);
00787     if (addresses.numAddresses() == 0) {
00788       env.setResultMsg("Failed to find network address for \"",
00789                            parseBuffer, "\"");
00790       break;
00791     }
00792     address = *(addresses.firstAddress());
00793 
00794     portNum = 5060; // default value
00795     char nextChar = *from;
00796     if (nextChar == ':') {
00797       int portNumInt;
00798       if (sscanf(++from, "%d", &portNumInt) != 1) {
00799         env.setResultMsg("No port number follows ':'");
00800         break;
00801       }
00802       if (portNumInt < 1 || portNumInt > 65535) {
00803         env.setResultMsg("Bad port number");
00804         break;
00805       }
00806       portNum = (portNumBits)portNumInt;
00807     }
00808 
00809     return True;
00810   } while (0);
00811 
00812   return False;
00813 }
00814 
00815 Boolean SIPClient::parseSIPURLUsernamePassword(char const* url,
00816                                                char*& username,
00817                                                char*& password) {
00818   username = password = NULL; // by default
00819   do {
00820     // Parse the URL as "sip:<username>[:<password>]@<whatever>"
00821     char const* prefix = "sip:";
00822     unsigned const prefixLength = 4;
00823     if (_strncasecmp(url, prefix, prefixLength) != 0) break;
00824 
00825     // Look for the ':' and '@':
00826     unsigned usernameIndex = prefixLength;
00827     unsigned colonIndex = 0, atIndex = 0;
00828     for (unsigned i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) {
00829       if (url[i] == ':' && colonIndex == 0) {
00830         colonIndex = i;
00831       } else if (url[i] == '@') {
00832         atIndex = i;
00833         break; // we're done
00834       }
00835     }
00836     if (atIndex == 0) break; // no '@' found
00837 
00838     char* urlCopy = strDup(url);
00839     urlCopy[atIndex] = '\0';
00840     if (colonIndex > 0) {
00841       urlCopy[colonIndex] = '\0';
00842       password = strDup(&urlCopy[colonIndex+1]);
00843     } else {
00844       password = strDup("");
00845     }
00846     username = strDup(&urlCopy[usernameIndex]);
00847     delete[] urlCopy;
00848 
00849     return True;
00850   } while (0);
00851 
00852   return False;
00853 }
00854 
00855 char*
00856 SIPClient::createAuthenticatorString(Authenticator const* authenticator,
00857                                       char const* cmd, char const* url) {
00858   if (authenticator != NULL && authenticator->realm() != NULL
00859       && authenticator->nonce() != NULL && authenticator->username() != NULL
00860       && authenticator->password() != NULL) {
00861     // We've been provided a filled-in authenticator, so use it:
00862     char const* const authFmt
00863       = "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", response=\"%s\", uri=\"%s\"\r\n";
00864     char const* response = authenticator->computeDigestResponse(cmd, url);
00865     unsigned authBufSize = strlen(authFmt)
00866       + strlen(authenticator->username()) + strlen(authenticator->realm())
00867       + strlen(authenticator->nonce()) + strlen(url) + strlen(response);
00868     char* authenticatorStr = new char[authBufSize];
00869     sprintf(authenticatorStr, authFmt,
00870             authenticator->username(), authenticator->realm(),
00871             authenticator->nonce(), response, url);
00872     authenticator->reclaimDigestResponse(response);
00873 
00874     return authenticatorStr;
00875   }
00876 
00877   return strDup("");
00878 }
00879 
00880 Boolean SIPClient::sendRequest(char const* requestString,
00881                                unsigned requestLength) {
00882   if (fVerbosityLevel >= 1) {
00883     envir() << "Sending request: " << requestString << "\n";
00884   }
00885   // NOTE: We should really check that "requestLength" is not #####
00886   // too large for UDP (see RFC 3261, section 18.1.1) #####
00887   return fOurSocket->output(envir(), 255, (unsigned char*)requestString,
00888                             requestLength);
00889 }
00890 
00891 unsigned SIPClient::getResponse(char*& responseBuffer,
00892                                 unsigned responseBufferSize) {
00893   if (responseBufferSize == 0) return 0; // just in case...
00894   responseBuffer[0] = '\0'; // ditto
00895 
00896   // Keep reading data from the socket until we see "\r\n\r\n" (except
00897   // at the start), or until we fill up our buffer.
00898   // Don't read any more than this.
00899   char* p = responseBuffer;
00900   Boolean haveSeenNonCRLF = False;
00901   int bytesRead = 0;
00902   while (bytesRead < (int)responseBufferSize) {
00903     unsigned bytesReadNow;
00904     struct sockaddr_in fromAddr;
00905     unsigned char* toPosn = (unsigned char*)(responseBuffer+bytesRead);
00906     Boolean readSuccess
00907       = fOurSocket->handleRead(toPosn, responseBufferSize-bytesRead,
00908                                bytesReadNow, fromAddr);
00909     if (!readSuccess || bytesReadNow == 0) {
00910       envir().setResultMsg("SIP response was truncated");
00911       break;
00912     }
00913     bytesRead += bytesReadNow;
00914 
00915     // Check whether we have "\r\n\r\n":
00916     char* lastToCheck = responseBuffer+bytesRead-4;
00917     if (lastToCheck < responseBuffer) continue;
00918     for (; p <= lastToCheck; ++p) {
00919       if (haveSeenNonCRLF) {
00920         if (*p == '\r' && *(p+1) == '\n' &&
00921             *(p+2) == '\r' && *(p+3) == '\n') {
00922           responseBuffer[bytesRead] = '\0';
00923 
00924           // Before returning, trim any \r or \n from the start:
00925           while (*responseBuffer == '\r' || *responseBuffer == '\n') {
00926             ++responseBuffer;
00927             --bytesRead;
00928           }
00929           return bytesRead;
00930         }
00931       } else {
00932         if (*p != '\r' && *p != '\n') {
00933           haveSeenNonCRLF = True;
00934         }
00935       }
00936     }
00937   }
00938 
00939   return 0;
00940 }
00941 
00942 Boolean SIPClient::parseResponseCode(char const* line,
00943                                       unsigned& responseCode) {
00944   if (sscanf(line, "%*s%u", &responseCode) != 1) {
00945     envir().setResultMsg("no response code in line: \"", line, "\"");
00946     return False;
00947   }
00948 
00949   return True;
00950 }

Generated on Mon Apr 29 13:28:03 2013 for live by  doxygen 1.5.2