blob: c4e0cdcab5d34561ebe4928b2feb52df961035d3 [file] [log] [blame]
Andreas Huber7a747b82010-06-07 15:19:40 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andreas Huber6e3fa442010-09-21 13:13:15 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "ARTSPConnection"
19#include <utils/Log.h>
20
Andreas Huber7a747b82010-06-07 15:19:40 -070021#include "ARTSPConnection.h"
22
Andreas Huberb0e73812011-03-29 13:34:28 -070023#include <cutils/properties.h>
24
Andreas Huber7a747b82010-06-07 15:19:40 -070025#include <media/stagefright/foundation/ABuffer.h>
26#include <media/stagefright/foundation/ADebug.h>
27#include <media/stagefright/foundation/AMessage.h>
Andreas Hubera0b442e2010-10-20 15:00:34 -070028#include <media/stagefright/foundation/base64.h>
Andreas Huber0416da72010-08-26 11:17:32 -070029#include <media/stagefright/MediaErrors.h>
Andreas Huber7a747b82010-06-07 15:19:40 -070030
31#include <arpa/inet.h>
32#include <fcntl.h>
33#include <netdb.h>
Andreas Hubera0b442e2010-10-20 15:00:34 -070034#include <openssl/md5.h>
Andreas Huber7a747b82010-06-07 15:19:40 -070035#include <sys/socket.h>
36
37namespace android {
38
39// static
40const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll;
41
42ARTSPConnection::ARTSPConnection()
43 : mState(DISCONNECTED),
Andreas Hubera0b442e2010-10-20 15:00:34 -070044 mAuthType(NONE),
Andreas Huber7a747b82010-06-07 15:19:40 -070045 mSocket(-1),
46 mConnectionID(0),
47 mNextCSeq(0),
48 mReceiveResponseEventPending(false) {
Andreas Huberb0e73812011-03-29 13:34:28 -070049 MakeUserAgent(&mUserAgent);
Andreas Huber7a747b82010-06-07 15:19:40 -070050}
51
52ARTSPConnection::~ARTSPConnection() {
53 if (mSocket >= 0) {
Andreas Huber6e3fa442010-09-21 13:13:15 -070054 LOGE("Connection is still open, closing the socket.");
Andreas Huber7a747b82010-06-07 15:19:40 -070055 close(mSocket);
56 mSocket = -1;
57 }
58}
59
60void ARTSPConnection::connect(const char *url, const sp<AMessage> &reply) {
61 sp<AMessage> msg = new AMessage(kWhatConnect, id());
62 msg->setString("url", url);
63 msg->setMessage("reply", reply);
64 msg->post();
65}
66
67void ARTSPConnection::disconnect(const sp<AMessage> &reply) {
68 sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
69 msg->setMessage("reply", reply);
70 msg->post();
71}
72
73void ARTSPConnection::sendRequest(
74 const char *request, const sp<AMessage> &reply) {
75 sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
76 msg->setString("request", request);
77 msg->setMessage("reply", reply);
78 msg->post();
79}
80
Andreas Huber0416da72010-08-26 11:17:32 -070081void ARTSPConnection::observeBinaryData(const sp<AMessage> &reply) {
82 sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, id());
83 msg->setMessage("reply", reply);
84 msg->post();
85}
86
Andreas Huber7a747b82010-06-07 15:19:40 -070087void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
88 switch (msg->what()) {
89 case kWhatConnect:
90 onConnect(msg);
91 break;
92
93 case kWhatDisconnect:
94 onDisconnect(msg);
95 break;
96
97 case kWhatCompleteConnection:
98 onCompleteConnection(msg);
99 break;
100
101 case kWhatSendRequest:
102 onSendRequest(msg);
103 break;
104
105 case kWhatReceiveResponse:
106 onReceiveResponse();
107 break;
108
Andreas Huber0416da72010-08-26 11:17:32 -0700109 case kWhatObserveBinaryData:
110 {
111 CHECK(msg->findMessage("reply", &mObserveBinaryMessage));
112 break;
113 }
114
Andreas Huber7a747b82010-06-07 15:19:40 -0700115 default:
116 TRESPASS();
117 break;
118 }
119}
120
121// static
122bool ARTSPConnection::ParseURL(
Andreas Hubera0b442e2010-10-20 15:00:34 -0700123 const char *url, AString *host, unsigned *port, AString *path,
124 AString *user, AString *pass) {
Andreas Huber7a747b82010-06-07 15:19:40 -0700125 host->clear();
126 *port = 0;
127 path->clear();
Andreas Hubera0b442e2010-10-20 15:00:34 -0700128 user->clear();
129 pass->clear();
Andreas Huber7a747b82010-06-07 15:19:40 -0700130
131 if (strncasecmp("rtsp://", url, 7)) {
132 return false;
133 }
134
135 const char *slashPos = strchr(&url[7], '/');
136
137 if (slashPos == NULL) {
138 host->setTo(&url[7]);
139 path->setTo("/");
140 } else {
141 host->setTo(&url[7], slashPos - &url[7]);
142 path->setTo(slashPos);
143 }
144
Andreas Hubera0b442e2010-10-20 15:00:34 -0700145 ssize_t atPos = host->find("@");
146
147 if (atPos >= 0) {
148 // Split of user:pass@ from hostname.
149
150 AString userPass(*host, 0, atPos);
151 host->erase(0, atPos + 1);
152
153 ssize_t colonPos = userPass.find(":");
154
155 if (colonPos < 0) {
156 *user = userPass;
157 } else {
158 user->setTo(userPass, 0, colonPos);
159 pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
160 }
161 }
162
Mike Lockwood5a23f8c2010-07-15 14:58:25 -0400163 const char *colonPos = strchr(host->c_str(), ':');
Andreas Huber7a747b82010-06-07 15:19:40 -0700164
165 if (colonPos != NULL) {
166 unsigned long x;
167 if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) {
168 return false;
169 }
170
171 *port = x;
172
173 size_t colonOffset = colonPos - host->c_str();
174 size_t trailing = host->size() - colonOffset;
175 host->erase(colonOffset, trailing);
176 } else {
177 *port = 554;
178 }
179
180 return true;
181}
182
Andreas Huberaf063a62010-08-18 10:17:18 -0700183static void MakeSocketBlocking(int s, bool blocking) {
184 // Make socket non-blocking.
185 int flags = fcntl(s, F_GETFL, 0);
186 CHECK_NE(flags, -1);
187
188 if (blocking) {
189 flags &= ~O_NONBLOCK;
190 } else {
191 flags |= O_NONBLOCK;
192 }
193
194 CHECK_NE(fcntl(s, F_SETFL, flags), -1);
195}
196
Andreas Huber7a747b82010-06-07 15:19:40 -0700197void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
198 ++mConnectionID;
199
200 if (mState != DISCONNECTED) {
201 close(mSocket);
202 mSocket = -1;
203
204 flushPendingRequests();
205 }
206
207 mState = CONNECTING;
208
Andreas Huber7a747b82010-06-07 15:19:40 -0700209 AString url;
210 CHECK(msg->findString("url", &url));
211
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700212 sp<AMessage> reply;
213 CHECK(msg->findMessage("reply", &reply));
214
Andreas Huber7a747b82010-06-07 15:19:40 -0700215 AString host, path;
216 unsigned port;
Andreas Hubera0b442e2010-10-20 15:00:34 -0700217 if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass)
218 || (mUser.size() > 0 && mPass.size() == 0)) {
219 // If we have a user name but no password we have to give up
220 // right here, since we currently have no way of asking the user
221 // for this information.
222
Andreas Huber6e3fa442010-09-21 13:13:15 -0700223 LOGE("Malformed rtsp url %s", url.c_str());
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700224
225 reply->setInt32("result", ERROR_MALFORMED);
226 reply->post();
227
228 mState = DISCONNECTED;
229 return;
230 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700231
Andreas Hubera0b442e2010-10-20 15:00:34 -0700232 if (mUser.size() > 0) {
233 LOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str());
234 }
235
Andreas Huber7a747b82010-06-07 15:19:40 -0700236 struct hostent *ent = gethostbyname(host.c_str());
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700237 if (ent == NULL) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700238 LOGE("Unknown host %s", host.c_str());
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700239
240 reply->setInt32("result", -ENOENT);
241 reply->post();
242
243 mState = DISCONNECTED;
244 return;
245 }
246
247 mSocket = socket(AF_INET, SOCK_STREAM, 0);
248
249 MakeSocketBlocking(mSocket, false);
Andreas Huber7a747b82010-06-07 15:19:40 -0700250
251 struct sockaddr_in remote;
252 memset(remote.sin_zero, 0, sizeof(remote.sin_zero));
253 remote.sin_family = AF_INET;
254 remote.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
255 remote.sin_port = htons(port);
256
257 int err = ::connect(
258 mSocket, (const struct sockaddr *)&remote, sizeof(remote));
259
Andreas Huber7a747b82010-06-07 15:19:40 -0700260 reply->setInt32("server-ip", ntohl(remote.sin_addr.s_addr));
261
262 if (err < 0) {
263 if (errno == EINPROGRESS) {
264 sp<AMessage> msg = new AMessage(kWhatCompleteConnection, id());
265 msg->setMessage("reply", reply);
266 msg->setInt32("connection-id", mConnectionID);
267 msg->post();
268 return;
269 }
270
271 reply->setInt32("result", -errno);
272 mState = DISCONNECTED;
273
274 close(mSocket);
275 mSocket = -1;
276 } else {
277 reply->setInt32("result", OK);
278 mState = CONNECTED;
279 mNextCSeq = 1;
280
281 postReceiveReponseEvent();
282 }
283
284 reply->post();
285}
286
287void ARTSPConnection::onDisconnect(const sp<AMessage> &msg) {
288 if (mState == CONNECTED || mState == CONNECTING) {
289 close(mSocket);
290 mSocket = -1;
291
292 flushPendingRequests();
Andreas Huberaf063a62010-08-18 10:17:18 -0700293 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700294
295 sp<AMessage> reply;
296 CHECK(msg->findMessage("reply", &reply));
297
298 reply->setInt32("result", OK);
299 mState = DISCONNECTED;
300
Andreas Hubera0b442e2010-10-20 15:00:34 -0700301 mUser.clear();
302 mPass.clear();
303 mAuthType = NONE;
304 mNonce.clear();
305
Andreas Huber7a747b82010-06-07 15:19:40 -0700306 reply->post();
307}
308
309void ARTSPConnection::onCompleteConnection(const sp<AMessage> &msg) {
310 sp<AMessage> reply;
311 CHECK(msg->findMessage("reply", &reply));
312
313 int32_t connectionID;
314 CHECK(msg->findInt32("connection-id", &connectionID));
315
316 if ((connectionID != mConnectionID) || mState != CONNECTING) {
317 // While we were attempting to connect, the attempt was
318 // cancelled.
319 reply->setInt32("result", -ECONNABORTED);
320 reply->post();
321 return;
322 }
323
324 struct timeval tv;
325 tv.tv_sec = 0;
326 tv.tv_usec = kSelectTimeoutUs;
327
328 fd_set ws;
329 FD_ZERO(&ws);
330 FD_SET(mSocket, &ws);
331
332 int res = select(mSocket + 1, NULL, &ws, NULL, &tv);
333 CHECK_GE(res, 0);
334
335 if (res == 0) {
336 // Timed out. Not yet connected.
337
338 msg->post();
339 return;
340 }
341
342 int err;
343 socklen_t optionLen = sizeof(err);
344 CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
345 CHECK_EQ(optionLen, (socklen_t)sizeof(err));
346
347 if (err != 0) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700348 LOGE("err = %d (%s)", err, strerror(err));
Andreas Huber7a747b82010-06-07 15:19:40 -0700349
350 reply->setInt32("result", -err);
351
352 mState = DISCONNECTED;
353 close(mSocket);
354 mSocket = -1;
355 } else {
356 reply->setInt32("result", OK);
357 mState = CONNECTED;
358 mNextCSeq = 1;
359
360 postReceiveReponseEvent();
361 }
362
363 reply->post();
364}
365
366void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) {
367 sp<AMessage> reply;
368 CHECK(msg->findMessage("reply", &reply));
369
370 if (mState != CONNECTED) {
371 reply->setInt32("result", -ENOTCONN);
372 reply->post();
373 return;
374 }
375
376 AString request;
377 CHECK(msg->findString("request", &request));
378
Andreas Hubera0b442e2010-10-20 15:00:34 -0700379 // Just in case we need to re-issue the request with proper authentication
380 // later, stash it away.
381 reply->setString("original-request", request.c_str(), request.size());
382
383 addAuthentication(&request);
Andreas Huberb0e73812011-03-29 13:34:28 -0700384 addUserAgent(&request);
Andreas Hubera0b442e2010-10-20 15:00:34 -0700385
Andreas Huber7a747b82010-06-07 15:19:40 -0700386 // Find the boundary between headers and the body.
387 ssize_t i = request.find("\r\n\r\n");
388 CHECK_GE(i, 0);
389
390 int32_t cseq = mNextCSeq++;
391
392 AString cseqHeader = "CSeq: ";
393 cseqHeader.append(cseq);
394 cseqHeader.append("\r\n");
395
396 request.insert(cseqHeader, i + 2);
397
Andreas Hubera0b442e2010-10-20 15:00:34 -0700398 LOGV("request: '%s'", request.c_str());
Andreas Huber7a747b82010-06-07 15:19:40 -0700399
400 size_t numBytesSent = 0;
401 while (numBytesSent < request.size()) {
402 ssize_t n =
403 send(mSocket, request.c_str() + numBytesSent,
404 request.size() - numBytesSent, 0);
405
406 if (n == 0) {
407 // Server closed the connection.
Andreas Huber6e3fa442010-09-21 13:13:15 -0700408 LOGE("Server unexpectedly closed the connection.");
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700409
410 reply->setInt32("result", ERROR_IO);
411 reply->post();
412 return;
Andreas Huber7a747b82010-06-07 15:19:40 -0700413 } else if (n < 0) {
414 if (errno == EINTR) {
415 continue;
416 }
417
Andreas Huber6e3fa442010-09-21 13:13:15 -0700418 LOGE("Error sending rtsp request.");
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700419 reply->setInt32("result", -errno);
420 reply->post();
421 return;
Andreas Huber7a747b82010-06-07 15:19:40 -0700422 }
423
424 numBytesSent += (size_t)n;
425 }
426
427 mPendingRequests.add(cseq, reply);
428}
429
430void ARTSPConnection::onReceiveResponse() {
431 mReceiveResponseEventPending = false;
432
433 if (mState != CONNECTED) {
434 return;
435 }
436
437 struct timeval tv;
438 tv.tv_sec = 0;
439 tv.tv_usec = kSelectTimeoutUs;
440
441 fd_set rs;
442 FD_ZERO(&rs);
443 FD_SET(mSocket, &rs);
444
445 int res = select(mSocket + 1, &rs, NULL, NULL, &tv);
446 CHECK_GE(res, 0);
447
448 if (res == 1) {
Andreas Huberaf063a62010-08-18 10:17:18 -0700449 MakeSocketBlocking(mSocket, true);
450
451 bool success = receiveRTSPReponse();
452
453 MakeSocketBlocking(mSocket, false);
454
455 if (!success) {
Andreas Huber7a747b82010-06-07 15:19:40 -0700456 // Something horrible, irreparable has happened.
457 flushPendingRequests();
458 return;
459 }
460 }
461
462 postReceiveReponseEvent();
463}
464
465void ARTSPConnection::flushPendingRequests() {
466 for (size_t i = 0; i < mPendingRequests.size(); ++i) {
467 sp<AMessage> reply = mPendingRequests.valueAt(i);
468
469 reply->setInt32("result", -ECONNABORTED);
470 reply->post();
471 }
472
473 mPendingRequests.clear();
474}
475
476void ARTSPConnection::postReceiveReponseEvent() {
477 if (mReceiveResponseEventPending) {
478 return;
479 }
480
481 sp<AMessage> msg = new AMessage(kWhatReceiveResponse, id());
482 msg->post();
483
484 mReceiveResponseEventPending = true;
485}
486
Andreas Huber0416da72010-08-26 11:17:32 -0700487status_t ARTSPConnection::receive(void *data, size_t size) {
488 size_t offset = 0;
489 while (offset < size) {
490 ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
Andreas Huber7a747b82010-06-07 15:19:40 -0700491 if (n == 0) {
492 // Server closed the connection.
Andreas Huber6e3fa442010-09-21 13:13:15 -0700493 LOGE("Server unexpectedly closed the connection.");
Andreas Huber0416da72010-08-26 11:17:32 -0700494 return ERROR_IO;
Andreas Huber7a747b82010-06-07 15:19:40 -0700495 } else if (n < 0) {
496 if (errno == EINTR) {
497 continue;
498 }
499
Andreas Huber6e3fa442010-09-21 13:13:15 -0700500 LOGE("Error reading rtsp response.");
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700501 return -errno;
Andreas Huber7a747b82010-06-07 15:19:40 -0700502 }
503
Andreas Huber0416da72010-08-26 11:17:32 -0700504 offset += (size_t)n;
505 }
506
507 return OK;
508}
509
510bool ARTSPConnection::receiveLine(AString *line) {
511 line->clear();
512
513 bool sawCR = false;
514 for (;;) {
515 char c;
516 if (receive(&c, 1) != OK) {
517 return false;
518 }
519
Andreas Huber7a747b82010-06-07 15:19:40 -0700520 if (sawCR && c == '\n') {
521 line->erase(line->size() - 1, 1);
522 return true;
523 }
524
525 line->append(&c, 1);
526
Andreas Huber0416da72010-08-26 11:17:32 -0700527 if (c == '$' && line->size() == 1) {
528 // Special-case for interleaved binary data.
529 return true;
530 }
531
Andreas Huber7a747b82010-06-07 15:19:40 -0700532 sawCR = (c == '\r');
533 }
534}
535
Andreas Huber0416da72010-08-26 11:17:32 -0700536sp<ABuffer> ARTSPConnection::receiveBinaryData() {
537 uint8_t x[3];
538 if (receive(x, 3) != OK) {
539 return NULL;
540 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700541
Andreas Huber0416da72010-08-26 11:17:32 -0700542 sp<ABuffer> buffer = new ABuffer((x[1] << 8) | x[2]);
543 if (receive(buffer->data(), buffer->size()) != OK) {
544 return NULL;
545 }
546
547 buffer->meta()->setInt32("index", (int32_t)x[0]);
548
549 return buffer;
550}
551
Andreas Hubere0666162011-02-16 13:20:02 -0800552static bool IsRTSPVersion(const AString &s) {
553 return s == "RTSP/1.0";
554}
555
Andreas Huber0416da72010-08-26 11:17:32 -0700556bool ARTSPConnection::receiveRTSPReponse() {
557 AString statusLine;
558
559 if (!receiveLine(&statusLine)) {
Andreas Huber7a747b82010-06-07 15:19:40 -0700560 return false;
561 }
562
Andreas Huber0416da72010-08-26 11:17:32 -0700563 if (statusLine == "$") {
564 sp<ABuffer> buffer = receiveBinaryData();
565
566 if (buffer == NULL) {
567 return false;
568 }
569
570 if (mObserveBinaryMessage != NULL) {
571 sp<AMessage> notify = mObserveBinaryMessage->dup();
572 notify->setObject("buffer", buffer);
573 notify->post();
574 } else {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700575 LOGW("received binary data, but no one cares.");
Andreas Huber0416da72010-08-26 11:17:32 -0700576 }
577
578 return true;
579 }
580
581 sp<ARTSPResponse> response = new ARTSPResponse;
582 response->mStatusLine = statusLine;
583
Andreas Huber6e3fa442010-09-21 13:13:15 -0700584 LOGI("status: %s", response->mStatusLine.c_str());
Andreas Huber7a747b82010-06-07 15:19:40 -0700585
586 ssize_t space1 = response->mStatusLine.find(" ");
587 if (space1 < 0) {
588 return false;
589 }
590 ssize_t space2 = response->mStatusLine.find(" ", space1 + 1);
591 if (space2 < 0) {
592 return false;
593 }
594
Andreas Hubere0666162011-02-16 13:20:02 -0800595 bool isRequest = false;
Andreas Huber7a747b82010-06-07 15:19:40 -0700596
Andreas Hubere0666162011-02-16 13:20:02 -0800597 if (!IsRTSPVersion(AString(response->mStatusLine, 0, space1))) {
598 CHECK(IsRTSPVersion(
599 AString(
600 response->mStatusLine,
601 space2 + 1,
602 response->mStatusLine.size() - space2 - 1)));
603
604 isRequest = true;
605
606 response->mStatusCode = 0;
607 } else {
608 AString statusCodeStr(
609 response->mStatusLine, space1 + 1, space2 - space1 - 1);
610
611 if (!ParseSingleUnsignedLong(
612 statusCodeStr.c_str(), &response->mStatusCode)
613 || response->mStatusCode < 100 || response->mStatusCode > 999) {
614 return false;
615 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700616 }
617
618 AString line;
619 for (;;) {
620 if (!receiveLine(&line)) {
621 break;
622 }
623
624 if (line.empty()) {
625 break;
626 }
627
Andreas Huber6e3fa442010-09-21 13:13:15 -0700628 LOGV("line: %s", line.c_str());
Andreas Huber7a747b82010-06-07 15:19:40 -0700629
630 ssize_t colonPos = line.find(":");
631 if (colonPos < 0) {
632 // Malformed header line.
633 return false;
634 }
635
636 AString key(line, 0, colonPos);
637 key.trim();
638 key.tolower();
639
640 line.erase(0, colonPos + 1);
641 line.trim();
642
643 response->mHeaders.add(key, line);
644 }
645
646 unsigned long contentLength = 0;
647
648 ssize_t i = response->mHeaders.indexOfKey("content-length");
649
650 if (i >= 0) {
651 AString value = response->mHeaders.valueAt(i);
652 if (!ParseSingleUnsignedLong(value.c_str(), &contentLength)) {
653 return false;
654 }
655 }
656
657 if (contentLength > 0) {
658 response->mContent = new ABuffer(contentLength);
659
660 size_t numBytesRead = 0;
661 while (numBytesRead < contentLength) {
662 ssize_t n = recv(
663 mSocket, response->mContent->data() + numBytesRead,
664 contentLength - numBytesRead, 0);
665
666 if (n == 0) {
667 // Server closed the connection.
668 TRESPASS();
669 } else if (n < 0) {
670 if (errno == EINTR) {
671 continue;
672 }
673
674 TRESPASS();
675 }
676
677 numBytesRead += (size_t)n;
678 }
679 }
680
Andreas Hubera0b442e2010-10-20 15:00:34 -0700681 if (response->mStatusCode == 401) {
682 if (mAuthType == NONE && mUser.size() > 0
683 && parseAuthMethod(response)) {
684 ssize_t i;
685 CHECK_EQ((status_t)OK, findPendingRequest(response, &i));
686 CHECK_GE(i, 0);
687
688 sp<AMessage> reply = mPendingRequests.valueAt(i);
689 mPendingRequests.removeItemsAt(i);
690
691 AString request;
692 CHECK(reply->findString("original-request", &request));
693
694 sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
695 msg->setMessage("reply", reply);
696 msg->setString("request", request.c_str(), request.size());
697
698 LOGI("re-sending request with authentication headers...");
699 onSendRequest(msg);
700
701 return true;
702 }
703 }
704
Andreas Hubere0666162011-02-16 13:20:02 -0800705 return isRequest
706 ? handleServerRequest(response)
707 : notifyResponseListener(response);
708}
709
710bool ARTSPConnection::handleServerRequest(const sp<ARTSPResponse> &request) {
711 // Implementation of server->client requests is optional for all methods
712 // but we do need to respond, even if it's just to say that we don't
713 // support the method.
714
715 ssize_t space1 = request->mStatusLine.find(" ");
716 CHECK_GE(space1, 0);
717
718 AString response;
719 response.append("RTSP/1.0 501 Not Implemented\r\n");
720
721 ssize_t i = request->mHeaders.indexOfKey("cseq");
722
723 if (i >= 0) {
724 AString value = request->mHeaders.valueAt(i);
725
726 unsigned long cseq;
727 if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
728 return false;
729 }
730
731 response.append("CSeq: ");
732 response.append(cseq);
733 response.append("\r\n");
734 }
735
736 response.append("\r\n");
737
738 size_t numBytesSent = 0;
739 while (numBytesSent < response.size()) {
740 ssize_t n =
741 send(mSocket, response.c_str() + numBytesSent,
742 response.size() - numBytesSent, 0);
743
744 if (n == 0) {
745 // Server closed the connection.
746 LOGE("Server unexpectedly closed the connection.");
747
748 return false;
749 } else if (n < 0) {
750 if (errno == EINTR) {
751 continue;
752 }
753
754 LOGE("Error sending rtsp response.");
755 return false;
756 }
757
758 numBytesSent += (size_t)n;
759 }
760
761 return true;
Andreas Huber7a747b82010-06-07 15:19:40 -0700762}
763
764// static
765bool ARTSPConnection::ParseSingleUnsignedLong(
766 const char *from, unsigned long *x) {
767 char *end;
768 *x = strtoul(from, &end, 10);
769
770 if (end == from || *end != '\0') {
771 return false;
772 }
773
774 return true;
775}
776
Andreas Hubera0b442e2010-10-20 15:00:34 -0700777status_t ARTSPConnection::findPendingRequest(
778 const sp<ARTSPResponse> &response, ssize_t *index) const {
779 *index = 0;
780
Andreas Huber7a747b82010-06-07 15:19:40 -0700781 ssize_t i = response->mHeaders.indexOfKey("cseq");
782
783 if (i < 0) {
Andreas Hubera0b442e2010-10-20 15:00:34 -0700784 // This is an unsolicited server->client message.
785 return OK;
Andreas Huber7a747b82010-06-07 15:19:40 -0700786 }
787
788 AString value = response->mHeaders.valueAt(i);
789
790 unsigned long cseq;
791 if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
Andreas Hubera0b442e2010-10-20 15:00:34 -0700792 return ERROR_MALFORMED;
Andreas Huber7a747b82010-06-07 15:19:40 -0700793 }
794
795 i = mPendingRequests.indexOfKey(cseq);
796
797 if (i < 0) {
Andreas Hubera0b442e2010-10-20 15:00:34 -0700798 return -ENOENT;
799 }
800
801 *index = i;
802
803 return OK;
804}
805
806bool ARTSPConnection::notifyResponseListener(
807 const sp<ARTSPResponse> &response) {
808 ssize_t i;
809 status_t err = findPendingRequest(response, &i);
810
811 if (err == OK && i < 0) {
812 // An unsolicited server response is not a problem.
813 return true;
814 }
815
816 if (err != OK) {
817 return false;
Andreas Huber7a747b82010-06-07 15:19:40 -0700818 }
819
820 sp<AMessage> reply = mPendingRequests.valueAt(i);
821 mPendingRequests.removeItemsAt(i);
822
823 reply->setInt32("result", OK);
824 reply->setObject("response", response);
825 reply->post();
826
827 return true;
828}
829
Andreas Hubera0b442e2010-10-20 15:00:34 -0700830bool ARTSPConnection::parseAuthMethod(const sp<ARTSPResponse> &response) {
831 ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
832
833 if (i < 0) {
834 return false;
835 }
836
837 AString value = response->mHeaders.valueAt(i);
838
839 if (!strncmp(value.c_str(), "Basic", 5)) {
840 mAuthType = BASIC;
841 } else {
Andreas Hubercf747542010-10-25 09:40:52 -0700842#if !defined(HAVE_ANDROID_OS)
843 // We don't have access to the MD5 implementation on the simulator,
844 // so we won't support digest authentication.
845 return false;
846#endif
847
Andreas Hubera0b442e2010-10-20 15:00:34 -0700848 CHECK(!strncmp(value.c_str(), "Digest", 6));
849 mAuthType = DIGEST;
850
851 i = value.find("nonce=");
852 CHECK_GE(i, 0);
853 CHECK_EQ(value.c_str()[i + 6], '\"');
854 ssize_t j = value.find("\"", i + 7);
855 CHECK_GE(j, 0);
856
857 mNonce.setTo(value, i + 7, j - i - 7);
858 }
859
860 return true;
861}
862
Andreas Hubercf747542010-10-25 09:40:52 -0700863#if defined(HAVE_ANDROID_OS)
Andreas Hubera0b442e2010-10-20 15:00:34 -0700864static void H(const AString &s, AString *out) {
865 out->clear();
866
867 MD5_CTX m;
868 MD5_Init(&m);
869 MD5_Update(&m, s.c_str(), s.size());
870
871 uint8_t key[16];
872 MD5_Final(key, &m);
873
874 for (size_t i = 0; i < 16; ++i) {
875 char nibble = key[i] >> 4;
876 if (nibble <= 9) {
877 nibble += '0';
878 } else {
879 nibble += 'a' - 10;
880 }
881 out->append(&nibble, 1);
882
883 nibble = key[i] & 0x0f;
884 if (nibble <= 9) {
885 nibble += '0';
886 } else {
887 nibble += 'a' - 10;
888 }
889 out->append(&nibble, 1);
890 }
891}
Andreas Hubercf747542010-10-25 09:40:52 -0700892#endif
Andreas Hubera0b442e2010-10-20 15:00:34 -0700893
894static void GetMethodAndURL(
895 const AString &request, AString *method, AString *url) {
896 ssize_t space1 = request.find(" ");
897 CHECK_GE(space1, 0);
898
899 ssize_t space2 = request.find(" ", space1 + 1);
900 CHECK_GE(space2, 0);
901
902 method->setTo(request, 0, space1);
903 url->setTo(request, space1 + 1, space2 - space1);
904}
905
906void ARTSPConnection::addAuthentication(AString *request) {
907 if (mAuthType == NONE) {
908 return;
909 }
910
911 // Find the boundary between headers and the body.
912 ssize_t i = request->find("\r\n\r\n");
913 CHECK_GE(i, 0);
914
915 if (mAuthType == BASIC) {
916 AString tmp;
917 tmp.append(mUser);
918 tmp.append(":");
919 tmp.append(mPass);
920
921 AString out;
922 encodeBase64(tmp.c_str(), tmp.size(), &out);
923
924 AString fragment;
925 fragment.append("Authorization: Basic ");
926 fragment.append(out);
927 fragment.append("\r\n");
928
929 request->insert(fragment, i + 2);
930
931 return;
932 }
933
Andreas Hubercf747542010-10-25 09:40:52 -0700934#if defined(HAVE_ANDROID_OS)
Andreas Hubera0b442e2010-10-20 15:00:34 -0700935 CHECK_EQ((int)mAuthType, (int)DIGEST);
936
937 AString method, url;
938 GetMethodAndURL(*request, &method, &url);
939
940 AString A1;
941 A1.append(mUser);
942 A1.append(":");
943 A1.append("Streaming Server");
944 A1.append(":");
945 A1.append(mPass);
946
947 AString A2;
948 A2.append(method);
949 A2.append(":");
950 A2.append(url);
951
952 AString HA1, HA2;
953 H(A1, &HA1);
954 H(A2, &HA2);
955
956 AString tmp;
957 tmp.append(HA1);
958 tmp.append(":");
959 tmp.append(mNonce);
960 tmp.append(":");
961 tmp.append(HA2);
962
963 AString digest;
964 H(tmp, &digest);
965
966 AString fragment;
967 fragment.append("Authorization: Digest ");
968 fragment.append("nonce=\"");
969 fragment.append(mNonce);
970 fragment.append("\", ");
971 fragment.append("username=\"");
972 fragment.append(mUser);
973 fragment.append("\", ");
974 fragment.append("uri=\"");
975 fragment.append(url);
976 fragment.append("\", ");
977 fragment.append("response=\"");
978 fragment.append(digest);
979 fragment.append("\"");
980 fragment.append("\r\n");
981
982 request->insert(fragment, i + 2);
Andreas Hubercf747542010-10-25 09:40:52 -0700983#endif
Andreas Hubera0b442e2010-10-20 15:00:34 -0700984}
985
Andreas Huberb0e73812011-03-29 13:34:28 -0700986// static
987void ARTSPConnection::MakeUserAgent(AString *userAgent) {
988 userAgent->clear();
989 userAgent->setTo("User-Agent: stagefright/1.1 (Linux;Android ");
990
991#if (PROPERTY_VALUE_MAX < 8)
992#error "PROPERTY_VALUE_MAX must be at least 8"
993#endif
994
995 char value[PROPERTY_VALUE_MAX];
996 property_get("ro.build.version.release", value, "Unknown");
997 userAgent->append(value);
998 userAgent->append(")\r\n");
999}
1000
1001void ARTSPConnection::addUserAgent(AString *request) const {
1002 // Find the boundary between headers and the body.
1003 ssize_t i = request->find("\r\n\r\n");
1004 CHECK_GE(i, 0);
1005
1006 request->insert(mUserAgent, i + 2);
1007}
1008
Andreas Huber7a747b82010-06-07 15:19:40 -07001009} // namespace android