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-2010 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(our_inet_ntoa(ourAddress));
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         << our_inet_ntoa(ourAddress) << ": "
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           << our_inet_ntoa(ourAddress) << ", 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_random();
00185   fFromTag = our_random();
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;
00610   authenticator.setUsernameAndPassword(username, password);
00611   char* inviteResult = invite(url, &authenticator);
00612   if (inviteResult != NULL) {
00613     // We are already authorized
00614     return inviteResult;
00615   }
00616 
00617   // The "realm" and "nonce" fields should have been filled in:
00618   if (authenticator.realm() == NULL || authenticator.nonce() == NULL) {
00619     // We haven't been given enough information to try again, so fail:
00620     return NULL;
00621   }
00622 
00623   // Try again (but with the same CallId):
00624   inviteResult = invite1(&authenticator);
00625   if (inviteResult != NULL) {
00626     // The authenticator worked, so use it in future requests:
00627     fValidAuthenticator = authenticator;
00628   }
00629 
00630   return inviteResult;
00631 }
00632 
00633 Boolean SIPClient::sendACK() {
00634   char* cmd = NULL;
00635   do {
00636     char const* const cmdFmt =
00637       "ACK %s SIP/2.0\r\n"
00638       "From: %s <sip:%s@%s>;tag=%u\r\n"
00639       "Via: SIP/2.0/UDP %s:%u\r\n"
00640       "To: %s;tag=%s\r\n"
00641       "Call-ID: %u@%s\r\n"
00642       "CSeq: %d ACK\r\n"
00643       "Content-length: 0\r\n\r\n";
00644     unsigned cmdSize = strlen(cmdFmt)
00645       + fURLSize
00646       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00647       + fOurAddressStrSize + 5 /* max port len */
00648       + fURLSize + fToTagStrSize
00649       + 20 + fOurAddressStrSize
00650       + 20;
00651     cmd = new char[cmdSize];
00652     sprintf(cmd, cmdFmt,
00653             fURL,
00654             fUserName, fUserName, fOurAddressStr, fFromTag,
00655             fOurAddressStr, fOurPortNum,
00656             fURL, fToTagStr,
00657             fCallId, fOurAddressStr,
00658             fCSeq /* note: it's the same as before; not incremented */);
00659 
00660     if (!sendRequest(cmd, strlen(cmd))) {
00661       envir().setResultErrMsg("ACK send() failed: ");
00662       break;
00663     }
00664 
00665     delete[] cmd;
00666     return True;
00667   } while (0);
00668 
00669   delete[] cmd;
00670   return False;
00671 }
00672 
00673 Boolean SIPClient::sendBYE() {
00674   // NOTE: This should really be retransmitted, for reliability #####
00675   char* cmd = NULL;
00676   do {
00677     char const* const cmdFmt =
00678       "BYE %s SIP/2.0\r\n"
00679       "From: %s <sip:%s@%s>;tag=%u\r\n"
00680       "Via: SIP/2.0/UDP %s:%u\r\n"
00681       "To: %s;tag=%s\r\n"
00682       "Call-ID: %u@%s\r\n"
00683       "CSeq: %d ACK\r\n"
00684       "Content-length: 0\r\n\r\n";
00685     unsigned cmdSize = strlen(cmdFmt)
00686       + fURLSize
00687       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00688       + fOurAddressStrSize + 5 /* max port len */
00689       + fURLSize + fToTagStrSize
00690       + 20 + fOurAddressStrSize
00691       + 20;
00692     cmd = new char[cmdSize];
00693     sprintf(cmd, cmdFmt,
00694             fURL,
00695             fUserName, fUserName, fOurAddressStr, fFromTag,
00696             fOurAddressStr, fOurPortNum,
00697             fURL, fToTagStr,
00698             fCallId, fOurAddressStr,
00699             ++fCSeq);
00700 
00701     if (!sendRequest(cmd, strlen(cmd))) {
00702       envir().setResultErrMsg("BYE send() failed: ");
00703       break;
00704     }
00705 
00706     delete[] cmd;
00707     return True;
00708   } while (0);
00709 
00710   delete[] cmd;
00711   return False;
00712 }
00713 
00714 Boolean SIPClient::processURL(char const* url) {
00715   do {
00716     // If we don't already have a server address/port, then
00717     // get these by parsing the URL:
00718     if (fServerAddress.s_addr == 0) {
00719       NetAddress destAddress;
00720       if (!parseSIPURL(envir(), url, destAddress, fServerPortNum)) break;
00721       fServerAddress.s_addr = *(unsigned*)(destAddress.data());
00722 
00723       if (fOurSocket != NULL) {
00724         fOurSocket->changeDestinationParameters(fServerAddress,
00725                                                 fServerPortNum, 255);
00726       }
00727     }
00728 
00729     return True;
00730   } while (0);
00731 
00732   return False;
00733 }
00734 
00735 Boolean SIPClient::parseSIPURL(UsageEnvironment& env, char const* url,
00736                                NetAddress& address,
00737                                portNumBits& portNum) {
00738   do {
00739     // Parse the URL as "sip:<username>@<address>:<port>/<etc>"
00740     // (with ":<port>" and "/<etc>" optional)
00741     // Also, skip over any "<username>[:<password>]@" preceding <address>
00742     char const* prefix = "sip:";
00743     unsigned const prefixLength = 4;
00744     if (_strncasecmp(url, prefix, prefixLength) != 0) {
00745       env.setResultMsg("URL is not of the form \"", prefix, "\"");
00746       break;
00747     }
00748 
00749     unsigned const parseBufferSize = 100;
00750     char parseBuffer[parseBufferSize];
00751     unsigned addressStartIndex = prefixLength;
00752     while (url[addressStartIndex] != '\0'
00753            && url[addressStartIndex++] != '@') {}
00754     char const* from = &url[addressStartIndex];
00755 
00756     // Skip over any "<username>[:<password>]@"
00757     char const* from1 = from;
00758     while (*from1 != '\0' && *from1 != '/') {
00759       if (*from1 == '@') {
00760         from = ++from1;
00761         break;
00762       }
00763       ++from1;
00764     }
00765 
00766     char* to = &parseBuffer[0];
00767     unsigned i;
00768     for (i = 0; i < parseBufferSize; ++i) {
00769       if (*from == '\0' || *from == ':' || *from == '/') {
00770         // We've completed parsing the address
00771         *to = '\0';
00772         break;
00773       }
00774       *to++ = *from++;
00775     }
00776     if (i == parseBufferSize) {
00777       env.setResultMsg("URL is too long");
00778       break;
00779     }
00780 
00781     NetAddressList addresses(parseBuffer);
00782     if (addresses.numAddresses() == 0) {
00783       env.setResultMsg("Failed to find network address for \"",
00784                            parseBuffer, "\"");
00785       break;
00786     }
00787     address = *(addresses.firstAddress());
00788 
00789     portNum = 5060; // default value
00790     char nextChar = *from;
00791     if (nextChar == ':') {
00792       int portNumInt;
00793       if (sscanf(++from, "%d", &portNumInt) != 1) {
00794         env.setResultMsg("No port number follows ':'");
00795         break;
00796       }
00797       if (portNumInt < 1 || portNumInt > 65535) {
00798         env.setResultMsg("Bad port number");
00799         break;
00800       }
00801       portNum = (portNumBits)portNumInt;
00802     }
00803 
00804     return True;
00805   } while (0);
00806 
00807   return False;
00808 }
00809 
00810 Boolean SIPClient::parseSIPURLUsernamePassword(char const* url,
00811                                                char*& username,
00812                                                char*& password) {
00813   username = password = NULL; // by default
00814   do {
00815     // Parse the URL as "sip:<username>[:<password>]@<whatever>"
00816     char const* prefix = "sip:";
00817     unsigned const prefixLength = 4;
00818     if (_strncasecmp(url, prefix, prefixLength) != 0) break;
00819 
00820     // Look for the ':' and '@':
00821     unsigned usernameIndex = prefixLength;
00822     unsigned colonIndex = 0, atIndex = 0;
00823     for (unsigned i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) {
00824       if (url[i] == ':' && colonIndex == 0) {
00825         colonIndex = i;
00826       } else if (url[i] == '@') {
00827         atIndex = i;
00828         break; // we're done
00829       }
00830     }
00831     if (atIndex == 0) break; // no '@' found
00832 
00833     char* urlCopy = strDup(url);
00834     urlCopy[atIndex] = '\0';
00835     if (colonIndex > 0) {
00836       urlCopy[colonIndex] = '\0';
00837       password = strDup(&urlCopy[colonIndex+1]);
00838     } else {
00839       password = strDup("");
00840     }
00841     username = strDup(&urlCopy[usernameIndex]);
00842     delete[] urlCopy;
00843 
00844     return True;
00845   } while (0);
00846 
00847   return False;
00848 }
00849 
00850 char*
00851 SIPClient::createAuthenticatorString(Authenticator const* authenticator,
00852                                       char const* cmd, char const* url) {
00853   if (authenticator != NULL && authenticator->realm() != NULL
00854       && authenticator->nonce() != NULL && authenticator->username() != NULL
00855       && authenticator->password() != NULL) {
00856     // We've been provided a filled-in authenticator, so use it:
00857     char const* const authFmt
00858       = "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", response=\"%s\", uri=\"%s\"\r\n";
00859     char const* response = authenticator->computeDigestResponse(cmd, url);
00860     unsigned authBufSize = strlen(authFmt)
00861       + strlen(authenticator->username()) + strlen(authenticator->realm())
00862       + strlen(authenticator->nonce()) + strlen(url) + strlen(response);
00863     char* authenticatorStr = new char[authBufSize];
00864     sprintf(authenticatorStr, authFmt,
00865             authenticator->username(), authenticator->realm(),
00866             authenticator->nonce(), response, url);
00867     authenticator->reclaimDigestResponse(response);
00868 
00869     return authenticatorStr;
00870   }
00871 
00872   return strDup("");
00873 }
00874 
00875 Boolean SIPClient::sendRequest(char const* requestString,
00876                                unsigned requestLength) {
00877   if (fVerbosityLevel >= 1) {
00878     envir() << "Sending request: " << requestString << "\n";
00879   }
00880   // NOTE: We should really check that "requestLength" is not #####
00881   // too large for UDP (see RFC 3261, section 18.1.1) #####
00882   return fOurSocket->output(envir(), 255, (unsigned char*)requestString,
00883                             requestLength);
00884 }
00885 
00886 unsigned SIPClient::getResponse(char*& responseBuffer,
00887                                 unsigned responseBufferSize) {
00888   if (responseBufferSize == 0) return 0; // just in case...
00889   responseBuffer[0] = '\0'; // ditto
00890 
00891   // Keep reading data from the socket until we see "\r\n\r\n" (except
00892   // at the start), or until we fill up our buffer.
00893   // Don't read any more than this.
00894   char* p = responseBuffer;
00895   Boolean haveSeenNonCRLF = False;
00896   int bytesRead = 0;
00897   while (bytesRead < (int)responseBufferSize) {
00898     unsigned bytesReadNow;
00899     struct sockaddr_in fromAddr;
00900     unsigned char* toPosn = (unsigned char*)(responseBuffer+bytesRead);
00901     Boolean readSuccess
00902       = fOurSocket->handleRead(toPosn, responseBufferSize-bytesRead,
00903                                bytesReadNow, fromAddr);
00904     if (!readSuccess || bytesReadNow == 0) {
00905       envir().setResultMsg("SIP response was truncated");
00906       break;
00907     }
00908     bytesRead += bytesReadNow;
00909 
00910     // Check whether we have "\r\n\r\n":
00911     char* lastToCheck = responseBuffer+bytesRead-4;
00912     if (lastToCheck < responseBuffer) continue;
00913     for (; p <= lastToCheck; ++p) {
00914       if (haveSeenNonCRLF) {
00915         if (*p == '\r' && *(p+1) == '\n' &&
00916             *(p+2) == '\r' && *(p+3) == '\n') {
00917           responseBuffer[bytesRead] = '\0';
00918 
00919           // Before returning, trim any \r or \n from the start:
00920           while (*responseBuffer == '\r' || *responseBuffer == '\n') {
00921             ++responseBuffer;
00922             --bytesRead;
00923           }
00924           return bytesRead;
00925         }
00926       } else {
00927         if (*p != '\r' && *p != '\n') {
00928           haveSeenNonCRLF = True;
00929         }
00930       }
00931     }
00932   }
00933 
00934   return 0;
00935 }
00936 
00937 Boolean SIPClient::parseResponseCode(char const* line,
00938                                       unsigned& responseCode) {
00939   if (sscanf(line, "%*s%u", &responseCode) != 1) {
00940     envir().setResultMsg("no response code in line: \"", line, "\"");
00941     return False;
00942   }
00943 
00944   return True;
00945 }

Generated on Fri Sep 3 02:35:42 2010 for live by  doxygen 1.5.2