00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 ),
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);
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
00074
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
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
00094 char const* formatStr;
00095 unsigned headerSize;
00096
00097
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
00157 for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
00158 if (*ptr == '\r' || *ptr == '\n') {
00159
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
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;
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
00193
00194
00195 fValidAuthenticator.reset();
00196 fWorkingAuthenticator = authenticator;
00197 char* authenticatorStr
00198 = createAuthenticatorString(fWorkingAuthenticator, "INVITE", fURL);
00199
00200
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 + fMIMESubtypeSize;
00208 rtpmapLine = new char[rtpmapFmtSize];
00209 sprintf(rtpmapLine, rtpmapFmt,
00210 fDesiredAudioRTPPayloadFormat, fMIMESubtype);
00211 rtpmapLineSize = strlen(rtpmapLine);
00212 } else {
00213
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 + 20 + fOurAddressStrSize
00227 + fApplicationNameSize
00228 + fOurAddressStrSize
00229 + 5 + 3
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"
00252 "%s"
00253 "Content-Length: %d\r\n\r\n"
00254 "%s";
00255 unsigned inviteCmdSize = strlen(cmdFmt)
00256 + fURLSize
00257 + 2*fUserNameSize + fOurAddressStrSize + 20
00258 + fOurAddressStrSize + 5
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
00284
00285 fInviteClientState = Calling;
00286 fEventLoopStopFlag = 0;
00287 TaskScheduler& sched = envir().taskScheduler();
00288 sched.turnOnBackgroundReadHandling(fOurSocket->socketNum(),
00289 &inviteResponseHandler, this);
00290 fTimerALen = 1*fT1;
00291 fTimerACount = 0;
00292 fTimerA = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
00293 fTimerB = sched.scheduleDelayedTask(64*fT1, timerBHandler, this);
00294 fTimerD = NULL;
00295
00296 if (!sendINVITE()) break;
00297
00298
00299 envir().taskScheduler().doEventLoop(&fEventLoopStopFlag);
00300
00301
00302
00303 sched.turnOffBackgroundReadHandling(fOurSocket->socketNum());
00304 sched.unscheduleDelayedTask(fTimerA);
00305 sched.unscheduleDelayedTask(fTimerB);
00306 sched.unscheduleDelayedTask(fTimerD);
00307
00308
00309
00310
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 ) {
00320 SIPClient* client = (SIPClient*)clientData;
00321 unsigned responseCode = client->getResponseCode();
00322 client->doInviteStateMachine(responseCode);
00323 }
00324
00325
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
00360 TaskScheduler& sched = envir().taskScheduler();
00361 switch (fInviteClientState) {
00362 case Calling: {
00363 if (responseCode == timerAFires) {
00364
00365 fTimerALen *= 2;
00366 fTimerA
00367 = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
00368
00369 fInviteClientState = Calling;
00370 if (!sendINVITE()) doInviteStateTerminated(0);
00371 } else {
00372
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
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
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;
00432 if (responseCode < 200 || responseCode > 299) {
00433
00434 delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;
00435 }
00436
00437
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
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
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
00473
00474
00475
00476
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;
00484
00485 char* realm = strDupSize(lineStart);
00486 char* nonce = strDupSize(lineStart);
00487
00488
00489
00490 Boolean foundAuthenticateHeader = False;
00491 if (
00492
00493 sscanf(lineStart, "Proxy-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"",
00494 realm, nonce) == 2 ||
00495
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
00510
00511
00512
00513
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;
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
00541 if (lineStart == NULL) {
00542 envir().setResultMsg("no content following header lines: ", readBuf);
00543 break;
00544 }
00545
00546
00547
00548
00549 char* bodyStart = nextLineStart;
00550 if (bodyStart != NULL && contentLength >= 0) {
00551
00552 unsigned numBodyBytes = &readBuf[bytesRead] - bodyStart;
00553 if (contentLength > (int)numBodyBytes) {
00554
00555
00556 unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
00557 #ifdef USING_TCP
00558
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
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;
00595 }
00596
00597 bodyStart[contentLength] = '\0';
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
00613 return inviteResult;
00614 }
00615
00616
00617 if (authenticator.realm() == NULL || authenticator.nonce() == NULL) {
00618
00619 return NULL;
00620 }
00621
00622
00623 inviteResult = invite1(&authenticator);
00624 if (inviteResult != NULL) {
00625
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
00646 + fOurAddressStrSize + 5
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 );
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
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
00687 + fOurAddressStrSize + 5
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
00716
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
00739
00740
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
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
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;
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;
00813 do {
00814
00815 char const* prefix = "sip:";
00816 unsigned const prefixLength = 4;
00817 if (_strncasecmp(url, prefix, prefixLength) != 0) break;
00818
00819
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;
00828 }
00829 }
00830 if (atIndex == 0) break;
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
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
00880
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;
00888 responseBuffer[0] = '\0';
00889
00890
00891
00892
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
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
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 }