1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// "liveMedia"
17// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18// A generic SIP client
19// Implementation
20
21#include "SIPClient.hh"
22#include "GroupsockHelper.hh"
23
24#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
25#define _strncasecmp _strnicmp
26#else
27#define _strncasecmp strncasecmp
28#endif
29
30////////// SIPClient //////////
31
32SIPClient* SIPClient
33::createNew(UsageEnvironment& env,
34 unsigned char desiredAudioRTPPayloadFormat,
35 char const* mimeSubtype,
36 int verbosityLevel, char const* applicationName) {
37 return new SIPClient(env, desiredAudioRTPPayloadFormat, mimeSubtype,
38 verbosityLevel, applicationName);
39}
40
41void SIPClient::setUserAgentString(char const* userAgentName) {
42 if (userAgentName == NULL) return;
43
44 // Change the existing user agent header string:
45 char const* const formatStr = "User-Agent: %s\r\n";
46 unsigned const headerSize = strlen(formatStr) + strlen(userAgentName);
47 delete[] fUserAgentHeaderStr;
48 fUserAgentHeaderStr = new char[headerSize];
49 sprintf(fUserAgentHeaderStr, formatStr, userAgentName);
50 fUserAgentHeaderStrLen = strlen(fUserAgentHeaderStr);
51}
52
53SIPClient::SIPClient(UsageEnvironment& env,
54 unsigned char desiredAudioRTPPayloadFormat,
55 char const* mimeSubtype,
56 int verbosityLevel, char const* applicationName)
57 : Medium(env),
58 fT1(500000 /* 500 ms */),
59 fDesiredAudioRTPPayloadFormat(desiredAudioRTPPayloadFormat),
60 fVerbosityLevel(verbosityLevel), fCSeq(0),
61 fUserAgentHeaderStr(NULL), fUserAgentHeaderStrLen(0),
62 fURL(NULL), fURLSize(0),
63 fToTagStr(NULL), fToTagStrSize(0),
64 fUserName(NULL), fUserNameSize(0),
65 fInviteSDPDescription(NULL), fInviteSDPDescriptionReturned(NULL),
66 fInviteCmd(NULL), fInviteCmdSize(0) {
67 if (mimeSubtype == NULL) mimeSubtype = "";
68 fMIMESubtype = strDup(mimeSubtype);
69 fMIMESubtypeSize = strlen(fMIMESubtype);
70
71 if (applicationName == NULL) applicationName = "";
72 fApplicationName = strDup(applicationName);
73 fApplicationNameSize = strlen(fApplicationName);
74
75 struct in_addr ourAddress;
76 ourAddress.s_addr = ourIPAddress(env); // hack
77 fOurAddressStr = strDup(AddressString(ourAddress).val());
78 fOurAddressStrSize = strlen(fOurAddressStr);
79
80 fOurSocket = new Groupsock(env, ourAddress, 0, 255);
81 if (fOurSocket == NULL) {
82 env << "ERROR: Failed to create socket for addr "
83 << fOurAddressStr << ": "
84 << env.getResultMsg() << "\n";
85 }
86
87 // Now, find out our source port number. Hack: Do this by first trying to
88 // send a 0-length packet, so that the "getSourcePort()" call will work.
89 fOurSocket->output(envir(), (unsigned char*)"", 0);
90 Port srcPort(0);
91 getSourcePort(env, fOurSocket->socketNum(), srcPort);
92 if (srcPort.num() != 0) {
93 fOurPortNum = ntohs(srcPort.num());
94 } else {
95 // No luck. Try again using a default port number:
96 fOurPortNum = 5060;
97 delete fOurSocket;
98 fOurSocket = new Groupsock(env, ourAddress, fOurPortNum, 255);
99 if (fOurSocket == NULL) {
100 env << "ERROR: Failed to create socket for addr "
101 << fOurAddressStr << ", port "
102 << fOurPortNum << ": "
103 << env.getResultMsg() << "\n";
104 }
105 }
106
107 // Set the "User-Agent:" header to use in each request:
108 char const* const libName = "LIVE555 Streaming Media v";
109 char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
110 char const* libPrefix; char const* libSuffix;
111 if (applicationName == NULL || applicationName[0] == '\0') {
112 applicationName = libPrefix = libSuffix = "";
113 } else {
114 libPrefix = " (";
115 libSuffix = ")";
116 }
117 unsigned userAgentNameSize
118 = fApplicationNameSize + strlen(libPrefix) + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix) + 1;
119 char* userAgentName = new char[userAgentNameSize];
120 sprintf(userAgentName, "%s%s%s%s%s",
121 applicationName, libPrefix, libName, libVersionStr, libSuffix);
122 setUserAgentString(userAgentName);
123 delete[] userAgentName;
124
125 reset();
126}
127
128SIPClient::~SIPClient() {
129 reset();
130
131 delete[] fUserAgentHeaderStr;
132 delete fOurSocket;
133 delete[] (char*)fOurAddressStr;
134 delete[] (char*)fApplicationName;
135 delete[] (char*)fMIMESubtype;
136}
137
138void SIPClient::reset() {
139 fWorkingAuthenticator = NULL;
140 delete[] fInviteCmd; fInviteCmd = NULL; fInviteCmdSize = 0;
141 delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;
142
143 delete[] (char*)fUserName; fUserName = strDup(fApplicationName);
144 fUserNameSize = strlen(fUserName);
145
146 fValidAuthenticator.reset();
147
148 delete[] (char*)fToTagStr; fToTagStr = NULL; fToTagStrSize = 0;
149 fServerPortNum = 0;
150 fServerAddress.s_addr = 0;
151 delete[] (char*)fURL; fURL = NULL; fURLSize = 0;
152}
153
154void SIPClient::setProxyServer(unsigned proxyServerAddress,
155 portNumBits proxyServerPortNum) {
156 fServerAddress.s_addr = proxyServerAddress;
157 fServerPortNum = proxyServerPortNum;
158 if (fOurSocket != NULL) {
159 fOurSocket->changeDestinationParameters(fServerAddress,
160 fServerPortNum, 255);
161 }
162}
163
164static char* getLine(char* startOfLine) {
165 // returns the start of the next line, or NULL if none
166 for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
167 if (*ptr == '\r' || *ptr == '\n') {
168 // We found the end of the line
169 *ptr++ = '\0';
170 if (*ptr == '\n') ++ptr;
171 return ptr;
172 }
173 }
174
175 return NULL;
176}
177
178char* SIPClient::invite(char const* url, Authenticator* authenticator) {
179 // First, check whether "url" contains a username:password to be used:
180 char* username; char* password;
181 if (authenticator == NULL
182 && parseSIPURLUsernamePassword(url, username, password)) {
183 char* result = inviteWithPassword(url, username, password);
184 delete[] username; delete[] password; // they were dynamically allocated
185 return result;
186 }
187
188 if (!processURL(url)) return NULL;
189
190 delete[] (char*)fURL; fURL = strDup(url);
191 fURLSize = strlen(fURL);
192
193 fCallId = our_random32();
194 fFromTag = our_random32();
195
196 return invite1(authenticator);
197}
198
199char* SIPClient::invite1(Authenticator* authenticator) {
200 do {
201 // Send the INVITE command:
202
203 // First, construct an authenticator string:
204 fValidAuthenticator.reset();
205 fWorkingAuthenticator = authenticator;
206 char* authenticatorStr
207 = createAuthenticatorString(fWorkingAuthenticator, "INVITE", fURL);
208
209 // Then, construct the SDP description to be sent in the INVITE:
210 char* rtpmapLine;
211 unsigned rtpmapLineSize;
212 if (fMIMESubtypeSize > 0) {
213 char const* const rtpmapFmt =
214 "a=rtpmap:%u %s/8000\r\n";
215 unsigned rtpmapFmtSize = strlen(rtpmapFmt)
216 + 3 /* max char len */ + fMIMESubtypeSize;
217 rtpmapLine = new char[rtpmapFmtSize];
218 sprintf(rtpmapLine, rtpmapFmt,
219 fDesiredAudioRTPPayloadFormat, fMIMESubtype);
220 rtpmapLineSize = strlen(rtpmapLine);
221 } else {
222 // Static payload type => no "a=rtpmap:" line
223 rtpmapLine = strDup("");
224 rtpmapLineSize = 0;
225 }
226 char const* const inviteSDPFmt =
227 "v=0\r\n"
228 "o=- %u %u IN IP4 %s\r\n"
229 "s=%s session\r\n"
230 "c=IN IP4 %s\r\n"
231 "t=0 0\r\n"
232 "m=audio %u RTP/AVP %u\r\n"
233 "%s";
234 unsigned inviteSDPFmtSize = strlen(inviteSDPFmt)
235 + 20 /* max int len */ + 20 + fOurAddressStrSize
236 + fApplicationNameSize
237 + fOurAddressStrSize
238 + 5 /* max short len */ + 3 /* max char len */
239 + rtpmapLineSize;
240 delete[] fInviteSDPDescription;
241 fInviteSDPDescription = new char[inviteSDPFmtSize];
242 sprintf(fInviteSDPDescription, inviteSDPFmt,
243 fCallId, fCSeq, fOurAddressStr,
244 fApplicationName,
245 fOurAddressStr,
246 fClientStartPortNum, fDesiredAudioRTPPayloadFormat,
247 rtpmapLine);
248 unsigned inviteSDPSize = strlen(fInviteSDPDescription);
249 delete[] rtpmapLine;
250
251 char const* const cmdFmt =
252 "INVITE %s SIP/2.0\r\n"
253 "From: %s <sip:%s@%s>;tag=%u\r\n"
254 "Via: SIP/2.0/UDP %s:%u\r\n"
255 "Max-Forwards: 70\r\n"
256 "To: %s\r\n"
257 "Contact: sip:%s@%s:%u\r\n"
258 "Call-ID: %u@%s\r\n"
259 "CSeq: %d INVITE\r\n"
260 "Content-Type: application/sdp\r\n"
261 "%s" /* Proxy-Authorization: line (if any) */
262 "%s" /* User-Agent: line */
263 "Content-Length: %d\r\n\r\n"
264 "%s";
265 unsigned inviteCmdSize = strlen(cmdFmt)
266 + fURLSize
267 + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
268 + fOurAddressStrSize + 5 /* max port len */
269 + fURLSize
270 + fUserNameSize + fOurAddressStrSize + 5
271 + 20 + fOurAddressStrSize
272 + 20
273 + strlen(authenticatorStr)
274 + fUserAgentHeaderStrLen
275 + 20
276 + inviteSDPSize;
277 delete[] fInviteCmd; fInviteCmd = new char[inviteCmdSize];
278 sprintf(fInviteCmd, cmdFmt,
279 fURL,
280 fUserName, fUserName, fOurAddressStr, fFromTag,
281 fOurAddressStr, fOurPortNum,
282 fURL,
283 fUserName, fOurAddressStr, fOurPortNum,
284 fCallId, fOurAddressStr,
285 ++fCSeq,
286 authenticatorStr,
287 fUserAgentHeaderStr,
288 inviteSDPSize,
289 fInviteSDPDescription);
290 fInviteCmdSize = strlen(fInviteCmd);
291 delete[] authenticatorStr;
292
293 // Before sending the "INVITE", arrange to handle any response packets,
294 // and set up timers:
295 fInviteClientState = Calling;
296 fEventLoopStopFlag = 0;
297 TaskScheduler& sched = envir().taskScheduler(); // abbrev.
298 sched.turnOnBackgroundReadHandling(fOurSocket->socketNum(),
299 &inviteResponseHandler, this);
300 fTimerALen = 1*fT1; // initially
301 fTimerACount = 0; // initially
302 fTimerA = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
303 fTimerB = sched.scheduleDelayedTask(64*fT1, timerBHandler, this);
304 fTimerD = NULL; // for now
305
306 if (!sendINVITE()) break;
307
308 // Enter the event loop, to handle response packets, and timeouts:
309 envir().taskScheduler().doEventLoop(&fEventLoopStopFlag);
310
311 // We're finished with this "INVITE".
312 // Turn off response handling and timers:
313 sched.turnOffBackgroundReadHandling(fOurSocket->socketNum());
314 sched.unscheduleDelayedTask(fTimerA);
315 sched.unscheduleDelayedTask(fTimerB);
316 sched.unscheduleDelayedTask(fTimerD);
317
318 // NOTE: We return the SDP description that we used in the "INVITE",
319 // not the one that we got from the server.
320 // ##### Later: match the codecs in the response (offer, answer) #####
321 if (fInviteSDPDescription != NULL) {
322 return strDup(fInviteSDPDescription);
323 }
324 } while (0);
325
326 return NULL;
327}
328
329void SIPClient::inviteResponseHandler(void* clientData, int /*mask*/) {
330 SIPClient* client = (SIPClient*)clientData;
331 unsigned responseCode = client->getResponseCode();
332 client->doInviteStateMachine(responseCode);
333}
334
335// Special 'response codes' that represent timers expiring:
336unsigned const timerAFires = 0xAAAAAAAA;
337unsigned const timerBFires = 0xBBBBBBBB;
338unsigned const timerDFires = 0xDDDDDDDD;
339
340void SIPClient::timerAHandler(void* clientData) {
341 SIPClient* client = (SIPClient*)clientData;
342 if (client->fVerbosityLevel >= 1) {
343 client->envir() << "RETRANSMISSION " << ++client->fTimerACount
344 << ", after " << client->fTimerALen/1000000.0
345 << " additional seconds\n";
346 }
347 client->doInviteStateMachine(timerAFires);
348}
349
350void SIPClient::timerBHandler(void* clientData) {
351 SIPClient* client = (SIPClient*)clientData;
352 if (client->fVerbosityLevel >= 1) {
353 client->envir() << "RETRANSMISSION TIMEOUT, after "
354 << 64*client->fT1/1000000.0 << " seconds\n";
355 fflush(stderr);
356 }
357 client->doInviteStateMachine(timerBFires);
358}
359
360void SIPClient::timerDHandler(void* clientData) {
361 SIPClient* client = (SIPClient*)clientData;
362 if (client->fVerbosityLevel >= 1) {
363 client->envir() << "TIMER D EXPIRED\n";
364 }
365 client->doInviteStateMachine(timerDFires);
366}
367
368void SIPClient::doInviteStateMachine(unsigned responseCode) {
369 // Implement the state transition diagram (RFC 3261, Figure 5)
370 TaskScheduler& sched = envir().taskScheduler(); // abbrev.
371 switch (fInviteClientState) {
372 case Calling: {
373 if (responseCode == timerAFires) {
374 // Restart timer A (with double the timeout interval):
375 fTimerALen *= 2;
376 fTimerA
377 = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
378
379 fInviteClientState = Calling;
380 if (!sendINVITE()) doInviteStateTerminated(0);
381 } else {
382 // Turn off timers A & B before moving to a new state:
383 sched.unscheduleDelayedTask(fTimerA);
384 sched.unscheduleDelayedTask(fTimerB);
385
386 if (responseCode == timerBFires) {
387 envir().setResultMsg("No response from server");
388 doInviteStateTerminated(0);
389 } else if (responseCode >= 100 && responseCode <= 199) {
390 fInviteClientState = Proceeding;
391 } else if (responseCode >= 200 && responseCode <= 299) {
392 doInviteStateTerminated(responseCode);
393 } else if (responseCode >= 400 && responseCode <= 499) {
394 doInviteStateTerminated(responseCode);
395 // this isn't what the spec says, but it seems right...
396 } else if (responseCode >= 300 && responseCode <= 699) {
397 fInviteClientState = Completed;
398 fTimerD
399 = sched.scheduleDelayedTask(32000000, timerDHandler, this);
400 if (!sendACK()) doInviteStateTerminated(0);
401 }
402 }
403 break;
404 }
405
406 case Proceeding: {
407 if (responseCode >= 100 && responseCode <= 199) {
408 fInviteClientState = Proceeding;
409 } else if (responseCode >= 200 && responseCode <= 299) {
410 doInviteStateTerminated(responseCode);
411 } else if (responseCode >= 400 && responseCode <= 499) {
412 doInviteStateTerminated(responseCode);
413 // this isn't what the spec says, but it seems right...
414 } else if (responseCode >= 300 && responseCode <= 699) {
415 fInviteClientState = Completed;
416 fTimerD = sched.scheduleDelayedTask(32000000, timerDHandler, this);
417 if (!sendACK()) doInviteStateTerminated(0);
418 }
419 break;
420 }
421
422 case Completed: {
423 if (responseCode == timerDFires) {
424 envir().setResultMsg("Transaction terminated");
425 doInviteStateTerminated(0);
426 } else if (responseCode >= 300 && responseCode <= 699) {
427 fInviteClientState = Completed;
428 if (!sendACK()) doInviteStateTerminated(0);
429 }
430 break;
431 }
432
433 case Terminated: {
434 doInviteStateTerminated(responseCode);
435 break;
436 }
437 }
438}
439
440void SIPClient::doInviteStateTerminated(unsigned responseCode) {
441 fInviteClientState = Terminated; // FWIW...
442 if (responseCode < 200 || responseCode > 299) {
443 // We failed, so return NULL;
444 delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;
445 delete[] fInviteSDPDescriptionReturned; fInviteSDPDescriptionReturned = NULL;
446 }
447
448 // Unblock the event loop:
449 fEventLoopStopFlag = ~0;
450}
451
452Boolean SIPClient::sendINVITE() {
453 if (!sendRequest(fInviteCmd, fInviteCmdSize)) {
454 envir().setResultErrMsg("INVITE send() failed: ");
455 return False;
456 }
457 return True;
458}
459
460unsigned SIPClient::getResponseCode() {
461 unsigned responseCode = 0;
462 do {
463 // Get the response from the server:
464 unsigned const readBufSize = 10000;
465 char readBuffer[readBufSize+1]; char* readBuf = readBuffer;
466
467 char* firstLine = NULL;
468 char* nextLineStart = NULL;
469 unsigned bytesRead = getResponse(readBuf, readBufSize);
470 if (bytesRead == 0) break;
471 if (fVerbosityLevel >= 1) {
472 envir() << "Received INVITE response: " << readBuf << "\n";
473 }
474
475 // Inspect the first line to get the response code:
476 firstLine = readBuf;
477 nextLineStart = getLine(firstLine);
478 if (!parseResponseCode(firstLine, responseCode)) break;
479
480 if (responseCode != 200) {
481 if (responseCode >= 400 && responseCode <= 499
482 && fWorkingAuthenticator != NULL) {
483 // We have an authentication failure, so fill in
484 // "*fWorkingAuthenticator" using the contents of a following
485 // "Proxy-Authenticate:" or "WWW-Authenticate:" line. (Once we compute a 'response' for
486 // "fWorkingAuthenticator", it can be used in a subsequent request
487 // - that will hopefully succeed.)
488 char* lineStart;
489 while (1) {
490 lineStart = nextLineStart;
491 if (lineStart == NULL) break;
492
493 nextLineStart = getLine(lineStart);
494 if (lineStart[0] == '\0') break; // this is a blank line
495
496 char* realm = strDupSize(lineStart);
497 char* nonce = strDupSize(lineStart);
498 // ##### Check for the format of "Proxy-Authenticate:" lines from
499 // ##### known server types.
500 // ##### This is a crock! We should make the parsing more general
501 Boolean foundAuthenticateHeader = False;
502 if (
503 // Asterisk #####
504 sscanf(lineStart, "Proxy-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"",
505 realm, nonce) == 2 ||
506 sscanf(lineStart, "WWW-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"",
507 realm, nonce) == 2 ||
508 // Cisco ATA #####
509 sscanf(lineStart, "Proxy-Authenticate: Digest algorithm=MD5,domain=\"%*[^\"]\",nonce=\"%[^\"]\", realm=\"%[^\"]\"",
510 nonce, realm) == 2) {
511 fWorkingAuthenticator->setRealmAndNonce(realm, nonce);
512 foundAuthenticateHeader = True;
513 }
514 delete[] realm; delete[] nonce;
515 if (foundAuthenticateHeader) break;
516 }
517 }
518 envir().setResultMsg("cannot handle INVITE response: ", firstLine);
519 break;
520 }
521
522 // Skip every subsequent header line, until we see a blank line.
523 // While doing so, check for "To:" and "Content-Length:" lines.
524 // The remaining data is assumed to be the SDP descriptor that we want.
525 // We should really do some more checking on the headers here - e.g., to
526 // check for "Content-type: application/sdp", "CSeq", etc. #####
527 int contentLength = -1;
528 char* lineStart;
529 while (1) {
530 lineStart = nextLineStart;
531 if (lineStart == NULL) break;
532
533 nextLineStart = getLine(lineStart);
534 if (lineStart[0] == '\0') break; // this is a blank line
535
536 char* toTagStr = strDupSize(lineStart);
537 if (sscanf(lineStart, "To:%*[^;]; tag=%s", toTagStr) == 1) {
538 delete[] (char*)fToTagStr; fToTagStr = strDup(toTagStr);
539 fToTagStrSize = strlen(fToTagStr);
540 }
541 delete[] toTagStr;
542
543 if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1
544 || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) {
545 if (contentLength < 0) {
546 envir().setResultMsg("Bad \"Content-Length:\" header: \"",
547 lineStart, "\"");
548 break;
549 }
550 }
551 }
552
553 // We're now at the end of the response header lines
554 if (lineStart == NULL) {
555 envir().setResultMsg("no content following header lines: ", readBuf);
556 break;
557 }
558
559 // Use the remaining data as the SDP descr, but first, check
560 // the "Content-Length:" header (if any) that we saw. We may need to
561 // read more data, or we may have extraneous data in the buffer.
562 char* bodyStart = nextLineStart;
563 if (bodyStart != NULL && contentLength >= 0) {
564 // We saw a "Content-Length:" header
565 unsigned numBodyBytes = &readBuf[bytesRead] - bodyStart;
566 if (contentLength > (int)numBodyBytes) {
567 // We need to read more data. First, make sure we have enough
568 // space for it:
569 unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
570#ifdef USING_TCP
571 // THIS CODE WORKS ONLY FOR TCP: #####
572 unsigned remainingBufferSize
573 = readBufSize - (bytesRead + (readBuf - readBuffer));
574 if (numExtraBytesNeeded > remainingBufferSize) {
575 char tmpBuf[200];
576 sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-Length:\" %d (need a buffer size of >= %d bytes\n",
577 readBufSize, contentLength,
578 readBufSize + numExtraBytesNeeded - remainingBufferSize);
579 envir().setResultMsg(tmpBuf);
580 break;
581 }
582
583 // Keep reading more data until we have enough:
584 if (fVerbosityLevel >= 1) {
585 envir() << "Need to read " << numExtraBytesNeeded
586 << " extra bytes\n";
587 }
588 while (numExtraBytesNeeded > 0) {
589 char* ptr = &readBuf[bytesRead];
590 unsigned bytesRead2;
591 struct sockaddr_in fromAddr;
592 Boolean readSuccess
593 = fOurSocket->handleRead((unsigned char*)ptr,
594 numExtraBytesNeeded,
595 bytesRead2, fromAddr);
596 if (!readSuccess) break;
597 ptr[bytesRead2] = '\0';
598 if (fVerbosityLevel >= 1) {
599 envir() << "Read " << bytesRead2
600 << " extra bytes: " << ptr << "\n";
601 }
602
603 bytesRead += bytesRead2;
604 numExtraBytesNeeded -= bytesRead2;
605 }
606#endif
607 if (numExtraBytesNeeded > 0) break; // one of the reads failed
608 }
609
610 bodyStart[contentLength] = '\0'; // trims any extra data
611 delete[] fInviteSDPDescriptionReturned; fInviteSDPDescriptionReturned = strDup(bodyStart);
612 }
613 } while (0);
614
615 return responseCode;
616}
617
618char* SIPClient::inviteWithPassword(char const* url, char const* username,
619 char const* password) {
620 delete[] (char*)fUserName; fUserName = strDup(username);
621 fUserNameSize = strlen(fUserName);
622
623 Authenticator authenticator(username, password);
624 char* inviteResult = invite(url, &authenticator);
625 if (inviteResult != NULL) {
626 // We are already authorized
627 return inviteResult;
628 }
629
630 // The "realm" and "nonce" fields should have been filled in:
631 if (authenticator.realm() == NULL || authenticator.nonce() == NULL) {
632 // We haven't been given enough information to try again, so fail:
633 return NULL;
634 }
635
636 // Try again (but with the same CallId):
637 inviteResult = invite1(&authenticator);
638 if (inviteResult != NULL) {
639 // The authenticator worked, so use it in future requests:
640 fValidAuthenticator = authenticator;
641 }
642
643 return inviteResult;
644}
645
646Boolean SIPClient::sendACK() {
647 char* cmd = NULL;
648 do {
649 char const* const cmdFmt =
650 "ACK %s SIP/2.0\r\n"
651 "From: %s <sip:%s@%s>;tag=%u\r\n"
652 "Via: SIP/2.0/UDP %s:%u\r\n"
653 "Max-Forwards: 70\r\n"
654 "To: %s;tag=%s\r\n"
655 "Call-ID: %u@%s\r\n"
656 "CSeq: %d ACK\r\n"
657 "Content-Length: 0\r\n\r\n";
658 unsigned cmdSize = strlen(cmdFmt)
659 + fURLSize
660 + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
661 + fOurAddressStrSize + 5 /* max port len */
662 + fURLSize + fToTagStrSize
663 + 20 + fOurAddressStrSize
664 + 20;
665 cmd = new char[cmdSize];
666 sprintf(cmd, cmdFmt,
667 fURL,
668 fUserName, fUserName, fOurAddressStr, fFromTag,
669 fOurAddressStr, fOurPortNum,
670 fURL, fToTagStr,
671 fCallId, fOurAddressStr,
672 fCSeq /* note: it's the same as before; not incremented */);
673
674 if (!sendRequest(cmd, strlen(cmd))) {
675 envir().setResultErrMsg("ACK send() failed: ");
676 break;
677 }
678
679 delete[] cmd;
680 return True;
681 } while (0);
682
683 delete[] cmd;
684 return False;
685}
686
687Boolean SIPClient::sendBYE() {
688 // NOTE: This should really be retransmitted, for reliability #####
689 char* cmd = NULL;
690 do {
691 char const* const cmdFmt =
692 "BYE %s SIP/2.0\r\n"
693 "From: %s <sip:%s@%s>;tag=%u\r\n"
694 "Via: SIP/2.0/UDP %s:%u\r\n"
695 "Max-Forwards: 70\r\n"
696 "To: %s;tag=%s\r\n"
697 "Call-ID: %u@%s\r\n"
698 "CSeq: %d BYE\r\n"
699 "Content-Length: 0\r\n\r\n";
700 unsigned cmdSize = strlen(cmdFmt)
701 + fURLSize
702 + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
703 + fOurAddressStrSize + 5 /* max port len */
704 + fURLSize + fToTagStrSize
705 + 20 + fOurAddressStrSize
706 + 20;
707 cmd = new char[cmdSize];
708 sprintf(cmd, cmdFmt,
709 fURL,
710 fUserName, fUserName, fOurAddressStr, fFromTag,
711 fOurAddressStr, fOurPortNum,
712 fURL, fToTagStr,
713 fCallId, fOurAddressStr,
714 ++fCSeq);
715
716 if (!sendRequest(cmd, strlen(cmd))) {
717 envir().setResultErrMsg("BYE send() failed: ");
718 break;
719 }
720
721 delete[] cmd;
722 return True;
723 } while (0);
724
725 delete[] cmd;
726 return False;
727}
728
729Boolean SIPClient::processURL(char const* url) {
730 do {
731 // If we don't already have a server address/port, then
732 // get these by parsing the URL:
733 if (fServerAddress.s_addr == 0) {
734 NetAddress destAddress;
735 if (!parseSIPURL(envir(), url, destAddress, fServerPortNum)) break;
736 fServerAddress.s_addr = *(unsigned*)(destAddress.data());
737
738 if (fOurSocket != NULL) {
739 fOurSocket->changeDestinationParameters(fServerAddress,
740 fServerPortNum, 255);
741 }
742 }
743
744 return True;
745 } while (0);
746
747 return False;
748}
749
750Boolean SIPClient::parseSIPURL(UsageEnvironment& env, char const* url,
751 NetAddress& address,
752 portNumBits& portNum) {
753 do {
754 // Parse the URL as "sip:<username>@<address>:<port>/<etc>"
755 // (with ":<port>" and "/<etc>" optional)
756 // Also, skip over any "<username>[:<password>]@" preceding <address>
757 char const* prefix = "sip:";
758 unsigned const prefixLength = 4;
759 if (_strncasecmp(url, prefix, prefixLength) != 0) {
760 env.setResultMsg("URL is not of the form \"", prefix, "\"");
761 break;
762 }
763
764 unsigned const parseBufferSize = 100;
765 char parseBuffer[parseBufferSize];
766 unsigned addressStartIndex = prefixLength;
767 while (url[addressStartIndex] != '\0'
768 && url[addressStartIndex++] != '@') {}
769 char const* from = &url[addressStartIndex];
770
771 // Skip over any "<username>[:<password>]@"
772 char const* from1 = from;
773 while (*from1 != '\0' && *from1 != '/') {
774 if (*from1 == '@') {
775 from = ++from1;
776 break;
777 }
778 ++from1;
779 }
780
781 char* to = &parseBuffer[0];
782 unsigned i;
783 for (i = 0; i < parseBufferSize; ++i) {
784 if (*from == '\0' || *from == ':' || *from == '/') {
785 // We've completed parsing the address
786 *to = '\0';
787 break;
788 }
789 *to++ = *from++;
790 }
791 if (i == parseBufferSize) {
792 env.setResultMsg("URL is too long");
793 break;
794 }
795
796 NetAddressList addresses(parseBuffer);
797 if (addresses.numAddresses() == 0) {
798 env.setResultMsg("Failed to find network address for \"",
799 parseBuffer, "\"");
800 break;
801 }
802 address = *(addresses.firstAddress());
803
804 portNum = 5060; // default value
805 char nextChar = *from;
806 if (nextChar == ':') {
807 int portNumInt;
808 if (sscanf(++from, "%d", &portNumInt) != 1) {
809 env.setResultMsg("No port number follows ':'");
810 break;
811 }
812 if (portNumInt < 1 || portNumInt > 65535) {
813 env.setResultMsg("Bad port number");
814 break;
815 }
816 portNum = (portNumBits)portNumInt;
817 }
818
819 return True;
820 } while (0);
821
822 return False;
823}
824
825Boolean SIPClient::parseSIPURLUsernamePassword(char const* url,
826 char*& username,
827 char*& password) {
828 username = password = NULL; // by default
829 do {
830 // Parse the URL as "sip:<username>[:<password>]@<whatever>"
831 char const* prefix = "sip:";
832 unsigned const prefixLength = 4;
833 if (_strncasecmp(url, prefix, prefixLength) != 0) break;
834
835 // Look for the ':' and '@':
836 unsigned usernameIndex = prefixLength;
837 unsigned colonIndex = 0, atIndex = 0;
838 for (unsigned i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) {
839 if (url[i] == ':' && colonIndex == 0) {
840 colonIndex = i;
841 } else if (url[i] == '@') {
842 atIndex = i;
843 break; // we're done
844 }
845 }
846 if (atIndex == 0) break; // no '@' found
847
848 char* urlCopy = strDup(url);
849 urlCopy[atIndex] = '\0';
850 if (colonIndex > 0) {
851 urlCopy[colonIndex] = '\0';
852 password = strDup(&urlCopy[colonIndex+1]);
853 } else {
854 password = strDup("");
855 }
856 username = strDup(&urlCopy[usernameIndex]);
857 delete[] urlCopy;
858
859 return True;
860 } while (0);
861
862 return False;
863}
864
865char*
866SIPClient::createAuthenticatorString(Authenticator const* authenticator,
867 char const* cmd, char const* url) {
868 if (authenticator != NULL && authenticator->realm() != NULL
869 && authenticator->nonce() != NULL && authenticator->username() != NULL
870 && authenticator->password() != NULL) {
871 // We've been provided a filled-in authenticator, so use it:
872 char const* const authFmt
873 = "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", response=\"%s\", uri=\"%s\"\r\n";
874 char const* response = authenticator->computeDigestResponse(cmd, url);
875 unsigned authBufSize = strlen(authFmt)
876 + strlen(authenticator->username()) + strlen(authenticator->realm())
877 + strlen(authenticator->nonce()) + strlen(url) + strlen(response);
878 char* authenticatorStr = new char[authBufSize];
879 sprintf(authenticatorStr, authFmt,
880 authenticator->username(), authenticator->realm(),
881 authenticator->nonce(), response, url);
882 authenticator->reclaimDigestResponse(response);
883
884 return authenticatorStr;
885 }
886
887 return strDup("");
888}
889
890Boolean SIPClient::sendRequest(char const* requestString,
891 unsigned requestLength) {
892 if (fVerbosityLevel >= 1) {
893 envir() << "Sending request: " << requestString << "\n";
894 }
895 // NOTE: We should really check that "requestLength" is not #####
896 // too large for UDP (see RFC 3261, section 18.1.1) #####
897 return fOurSocket->output(envir(), (unsigned char*)requestString, requestLength);
898}
899
900unsigned SIPClient::getResponse(char*& responseBuffer,
901 unsigned responseBufferSize) {
902 if (responseBufferSize == 0) return 0; // just in case...
903 responseBuffer[0] = '\0'; // ditto
904
905 // Keep reading data from the socket until we see "\r\n\r\n" (except
906 // at the start), or until we fill up our buffer.
907 // Don't read any more than this.
908 char* p = responseBuffer;
909 Boolean haveSeenNonCRLF = False;
910 int bytesRead = 0;
911 while (bytesRead < (int)responseBufferSize) {
912 unsigned bytesReadNow;
913 struct sockaddr_in fromAddr;
914 unsigned char* toPosn = (unsigned char*)(responseBuffer+bytesRead);
915 Boolean readSuccess
916 = fOurSocket->handleRead(toPosn, responseBufferSize-bytesRead,
917 bytesReadNow, fromAddr);
918 if (!readSuccess || bytesReadNow == 0) {
919 envir().setResultMsg("SIP response was truncated");
920 break;
921 }
922 bytesRead += bytesReadNow;
923
924 // Check whether we have "\r\n\r\n":
925 char* lastToCheck = responseBuffer+bytesRead-4;
926 if (lastToCheck < responseBuffer) continue;
927 for (; p <= lastToCheck; ++p) {
928 if (haveSeenNonCRLF) {
929 if (*p == '\r' && *(p+1) == '\n' &&
930 *(p+2) == '\r' && *(p+3) == '\n') {
931 responseBuffer[bytesRead] = '\0';
932
933 // Before returning, trim any \r or \n from the start:
934 while (*responseBuffer == '\r' || *responseBuffer == '\n') {
935 ++responseBuffer;
936 --bytesRead;
937 }
938 return bytesRead;
939 }
940 } else {
941 if (*p != '\r' && *p != '\n') {
942 haveSeenNonCRLF = True;
943 }
944 }
945 }
946 }
947
948 return 0;
949}
950
951Boolean SIPClient::parseResponseCode(char const* line,
952 unsigned& responseCode) {
953 if (sscanf(line, "%*s%u", &responseCode) != 1) {
954 envir().setResultMsg("no response code in line: \"", line, "\"");
955 return False;
956 }
957
958 return True;
959}
960