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

Generated on Thu May 17 07:11:47 2012 for live by  doxygen 1.5.2