ARTSPConnection.cpp revision aa70226152d2084f85a96b52359dbc8476a86a45
1/*
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
17//#define LOG_NDEBUG 0
18#define LOG_TAG "ARTSPConnection"
19#include <utils/Log.h>
20
21#include "ARTSPConnection.h"
22
23#include <cutils/properties.h>
24
25#include <media/stagefright/foundation/ABuffer.h>
26#include <media/stagefright/foundation/ADebug.h>
27#include <media/stagefright/foundation/AMessage.h>
28#include <media/stagefright/foundation/base64.h>
29#include <media/stagefright/MediaErrors.h>
30
31#include <arpa/inet.h>
32#include <fcntl.h>
33#include <netdb.h>
34#include <openssl/md5.h>
35#include <sys/socket.h>
36
37#include "HTTPBase.h"
38
39namespace android {
40
41// static
42const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll;
43
44ARTSPConnection::ARTSPConnection(bool uidValid, uid_t uid)
45    : mUIDValid(uidValid),
46      mUID(uid),
47      mState(DISCONNECTED),
48      mAuthType(NONE),
49      mSocket(-1),
50      mConnectionID(0),
51      mNextCSeq(0),
52      mReceiveResponseEventPending(false) {
53    MakeUserAgent(&mUserAgent);
54}
55
56ARTSPConnection::~ARTSPConnection() {
57    if (mSocket >= 0) {
58        LOGE("Connection is still open, closing the socket.");
59        if (mUIDValid) {
60            HTTPBase::UnRegisterSocketUserTag(mSocket);
61        }
62        close(mSocket);
63        mSocket = -1;
64    }
65}
66
67void ARTSPConnection::connect(const char *url, const sp<AMessage> &reply) {
68    sp<AMessage> msg = new AMessage(kWhatConnect, id());
69    msg->setString("url", url);
70    msg->setMessage("reply", reply);
71    msg->post();
72}
73
74void ARTSPConnection::disconnect(const sp<AMessage> &reply) {
75    sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
76    msg->setMessage("reply", reply);
77    msg->post();
78}
79
80void ARTSPConnection::sendRequest(
81        const char *request, const sp<AMessage> &reply) {
82    sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
83    msg->setString("request", request);
84    msg->setMessage("reply", reply);
85    msg->post();
86}
87
88void ARTSPConnection::observeBinaryData(const sp<AMessage> &reply) {
89    sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, id());
90    msg->setMessage("reply", reply);
91    msg->post();
92}
93
94void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
95    switch (msg->what()) {
96        case kWhatConnect:
97            onConnect(msg);
98            break;
99
100        case kWhatDisconnect:
101            onDisconnect(msg);
102            break;
103
104        case kWhatCompleteConnection:
105            onCompleteConnection(msg);
106            break;
107
108        case kWhatSendRequest:
109            onSendRequest(msg);
110            break;
111
112        case kWhatReceiveResponse:
113            onReceiveResponse();
114            break;
115
116        case kWhatObserveBinaryData:
117        {
118            CHECK(msg->findMessage("reply", &mObserveBinaryMessage));
119            break;
120        }
121
122        default:
123            TRESPASS();
124            break;
125    }
126}
127
128// static
129bool ARTSPConnection::ParseURL(
130        const char *url, AString *host, unsigned *port, AString *path,
131        AString *user, AString *pass) {
132    host->clear();
133    *port = 0;
134    path->clear();
135    user->clear();
136    pass->clear();
137
138    if (strncasecmp("rtsp://", url, 7)) {
139        return false;
140    }
141
142    const char *slashPos = strchr(&url[7], '/');
143
144    if (slashPos == NULL) {
145        host->setTo(&url[7]);
146        path->setTo("/");
147    } else {
148        host->setTo(&url[7], slashPos - &url[7]);
149        path->setTo(slashPos);
150    }
151
152    ssize_t atPos = host->find("@");
153
154    if (atPos >= 0) {
155        // Split of user:pass@ from hostname.
156
157        AString userPass(*host, 0, atPos);
158        host->erase(0, atPos + 1);
159
160        ssize_t colonPos = userPass.find(":");
161
162        if (colonPos < 0) {
163            *user = userPass;
164        } else {
165            user->setTo(userPass, 0, colonPos);
166            pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
167        }
168    }
169
170    const char *colonPos = strchr(host->c_str(), ':');
171
172    if (colonPos != NULL) {
173        unsigned long x;
174        if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) {
175            return false;
176        }
177
178        *port = x;
179
180        size_t colonOffset = colonPos - host->c_str();
181        size_t trailing = host->size() - colonOffset;
182        host->erase(colonOffset, trailing);
183    } else {
184        *port = 554;
185    }
186
187    return true;
188}
189
190static status_t MakeSocketBlocking(int s, bool blocking) {
191    // Make socket non-blocking.
192    int flags = fcntl(s, F_GETFL, 0);
193
194    if (flags == -1) {
195        return UNKNOWN_ERROR;
196    }
197
198    if (blocking) {
199        flags &= ~O_NONBLOCK;
200    } else {
201        flags |= O_NONBLOCK;
202    }
203
204    flags = fcntl(s, F_SETFL, flags);
205
206    return flags == -1 ? UNKNOWN_ERROR : OK;
207}
208
209void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
210    ++mConnectionID;
211
212    if (mState != DISCONNECTED) {
213        if (mUIDValid) {
214            HTTPBase::UnRegisterSocketUserTag(mSocket);
215        }
216        close(mSocket);
217        mSocket = -1;
218
219        flushPendingRequests();
220    }
221
222    mState = CONNECTING;
223
224    AString url;
225    CHECK(msg->findString("url", &url));
226
227    sp<AMessage> reply;
228    CHECK(msg->findMessage("reply", &reply));
229
230    AString host, path;
231    unsigned port;
232    if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass)
233            || (mUser.size() > 0 && mPass.size() == 0)) {
234        // If we have a user name but no password we have to give up
235        // right here, since we currently have no way of asking the user
236        // for this information.
237
238        LOGE("Malformed rtsp url %s", url.c_str());
239
240        reply->setInt32("result", ERROR_MALFORMED);
241        reply->post();
242
243        mState = DISCONNECTED;
244        return;
245    }
246
247    if (mUser.size() > 0) {
248        ALOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str());
249    }
250
251    struct hostent *ent = gethostbyname(host.c_str());
252    if (ent == NULL) {
253        LOGE("Unknown host %s", host.c_str());
254
255        reply->setInt32("result", -ENOENT);
256        reply->post();
257
258        mState = DISCONNECTED;
259        return;
260    }
261
262    mSocket = socket(AF_INET, SOCK_STREAM, 0);
263
264    if (mUIDValid) {
265        HTTPBase::RegisterSocketUserTag(mSocket, mUID,
266                                        (uint32_t)*(uint32_t*) "RTSP");
267    }
268
269    MakeSocketBlocking(mSocket, false);
270
271    struct sockaddr_in remote;
272    memset(remote.sin_zero, 0, sizeof(remote.sin_zero));
273    remote.sin_family = AF_INET;
274    remote.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
275    remote.sin_port = htons(port);
276
277    int err = ::connect(
278            mSocket, (const struct sockaddr *)&remote, sizeof(remote));
279
280    reply->setInt32("server-ip", ntohl(remote.sin_addr.s_addr));
281
282    if (err < 0) {
283        if (errno == EINPROGRESS) {
284            sp<AMessage> msg = new AMessage(kWhatCompleteConnection, id());
285            msg->setMessage("reply", reply);
286            msg->setInt32("connection-id", mConnectionID);
287            msg->post();
288            return;
289        }
290
291        reply->setInt32("result", -errno);
292        mState = DISCONNECTED;
293
294        if (mUIDValid) {
295            HTTPBase::UnRegisterSocketUserTag(mSocket);
296        }
297        close(mSocket);
298        mSocket = -1;
299    } else {
300        reply->setInt32("result", OK);
301        mState = CONNECTED;
302        mNextCSeq = 1;
303
304        postReceiveReponseEvent();
305    }
306
307    reply->post();
308}
309
310void ARTSPConnection::performDisconnect() {
311    if (mUIDValid) {
312        HTTPBase::UnRegisterSocketUserTag(mSocket);
313    }
314    close(mSocket);
315    mSocket = -1;
316
317    flushPendingRequests();
318
319    mUser.clear();
320    mPass.clear();
321    mAuthType = NONE;
322    mNonce.clear();
323
324    mState = DISCONNECTED;
325}
326
327void ARTSPConnection::onDisconnect(const sp<AMessage> &msg) {
328    if (mState == CONNECTED || mState == CONNECTING) {
329        performDisconnect();
330    }
331
332    sp<AMessage> reply;
333    CHECK(msg->findMessage("reply", &reply));
334
335    reply->setInt32("result", OK);
336
337    reply->post();
338}
339
340void ARTSPConnection::onCompleteConnection(const sp<AMessage> &msg) {
341    sp<AMessage> reply;
342    CHECK(msg->findMessage("reply", &reply));
343
344    int32_t connectionID;
345    CHECK(msg->findInt32("connection-id", &connectionID));
346
347    if ((connectionID != mConnectionID) || mState != CONNECTING) {
348        // While we were attempting to connect, the attempt was
349        // cancelled.
350        reply->setInt32("result", -ECONNABORTED);
351        reply->post();
352        return;
353    }
354
355    struct timeval tv;
356    tv.tv_sec = 0;
357    tv.tv_usec = kSelectTimeoutUs;
358
359    fd_set ws;
360    FD_ZERO(&ws);
361    FD_SET(mSocket, &ws);
362
363    int res = select(mSocket + 1, NULL, &ws, NULL, &tv);
364    CHECK_GE(res, 0);
365
366    if (res == 0) {
367        // Timed out. Not yet connected.
368
369        msg->post();
370        return;
371    }
372
373    int err;
374    socklen_t optionLen = sizeof(err);
375    CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
376    CHECK_EQ(optionLen, (socklen_t)sizeof(err));
377
378    if (err != 0) {
379        LOGE("err = %d (%s)", err, strerror(err));
380
381        reply->setInt32("result", -err);
382
383        mState = DISCONNECTED;
384        if (mUIDValid) {
385            HTTPBase::UnRegisterSocketUserTag(mSocket);
386        }
387        close(mSocket);
388        mSocket = -1;
389    } else {
390        reply->setInt32("result", OK);
391        mState = CONNECTED;
392        mNextCSeq = 1;
393
394        postReceiveReponseEvent();
395    }
396
397    reply->post();
398}
399
400void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) {
401    sp<AMessage> reply;
402    CHECK(msg->findMessage("reply", &reply));
403
404    if (mState != CONNECTED) {
405        reply->setInt32("result", -ENOTCONN);
406        reply->post();
407        return;
408    }
409
410    AString request;
411    CHECK(msg->findString("request", &request));
412
413    // Just in case we need to re-issue the request with proper authentication
414    // later, stash it away.
415    reply->setString("original-request", request.c_str(), request.size());
416
417    addAuthentication(&request);
418    addUserAgent(&request);
419
420    // Find the boundary between headers and the body.
421    ssize_t i = request.find("\r\n\r\n");
422    CHECK_GE(i, 0);
423
424    int32_t cseq = mNextCSeq++;
425
426    AString cseqHeader = "CSeq: ";
427    cseqHeader.append(cseq);
428    cseqHeader.append("\r\n");
429
430    request.insert(cseqHeader, i + 2);
431
432    ALOGV("request: '%s'", request.c_str());
433
434    size_t numBytesSent = 0;
435    while (numBytesSent < request.size()) {
436        ssize_t n =
437            send(mSocket, request.c_str() + numBytesSent,
438                 request.size() - numBytesSent, 0);
439
440        if (n < 0 && errno == EINTR) {
441            continue;
442        }
443
444        if (n <= 0) {
445            performDisconnect();
446
447            if (n == 0) {
448                // Server closed the connection.
449                LOGE("Server unexpectedly closed the connection.");
450
451                reply->setInt32("result", ERROR_IO);
452                reply->post();
453            } else {
454                LOGE("Error sending rtsp request. (%s)", strerror(errno));
455                reply->setInt32("result", -errno);
456                reply->post();
457            }
458
459            return;
460        }
461
462        numBytesSent += (size_t)n;
463    }
464
465    mPendingRequests.add(cseq, reply);
466}
467
468void ARTSPConnection::onReceiveResponse() {
469    mReceiveResponseEventPending = false;
470
471    if (mState != CONNECTED) {
472        return;
473    }
474
475    struct timeval tv;
476    tv.tv_sec = 0;
477    tv.tv_usec = kSelectTimeoutUs;
478
479    fd_set rs;
480    FD_ZERO(&rs);
481    FD_SET(mSocket, &rs);
482
483    int res = select(mSocket + 1, &rs, NULL, NULL, &tv);
484    CHECK_GE(res, 0);
485
486    if (res == 1) {
487        MakeSocketBlocking(mSocket, true);
488
489        bool success = receiveRTSPReponse();
490
491        MakeSocketBlocking(mSocket, false);
492
493        if (!success) {
494            // Something horrible, irreparable has happened.
495            flushPendingRequests();
496            return;
497        }
498    }
499
500    postReceiveReponseEvent();
501}
502
503void ARTSPConnection::flushPendingRequests() {
504    for (size_t i = 0; i < mPendingRequests.size(); ++i) {
505        sp<AMessage> reply = mPendingRequests.valueAt(i);
506
507        reply->setInt32("result", -ECONNABORTED);
508        reply->post();
509    }
510
511    mPendingRequests.clear();
512}
513
514void ARTSPConnection::postReceiveReponseEvent() {
515    if (mReceiveResponseEventPending) {
516        return;
517    }
518
519    sp<AMessage> msg = new AMessage(kWhatReceiveResponse, id());
520    msg->post();
521
522    mReceiveResponseEventPending = true;
523}
524
525status_t ARTSPConnection::receive(void *data, size_t size) {
526    size_t offset = 0;
527    while (offset < size) {
528        ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
529
530        if (n < 0 && errno == EINTR) {
531            continue;
532        }
533
534        if (n <= 0) {
535            performDisconnect();
536
537            if (n == 0) {
538                // Server closed the connection.
539                LOGE("Server unexpectedly closed the connection.");
540                return ERROR_IO;
541            } else {
542                LOGE("Error reading rtsp response. (%s)", strerror(errno));
543                return -errno;
544            }
545        }
546
547        offset += (size_t)n;
548    }
549
550    return OK;
551}
552
553bool ARTSPConnection::receiveLine(AString *line) {
554    line->clear();
555
556    bool sawCR = false;
557    for (;;) {
558        char c;
559        if (receive(&c, 1) != OK) {
560            return false;
561        }
562
563        if (sawCR && c == '\n') {
564            line->erase(line->size() - 1, 1);
565            return true;
566        }
567
568        line->append(&c, 1);
569
570        if (c == '$' && line->size() == 1) {
571            // Special-case for interleaved binary data.
572            return true;
573        }
574
575        sawCR = (c == '\r');
576    }
577}
578
579sp<ABuffer> ARTSPConnection::receiveBinaryData() {
580    uint8_t x[3];
581    if (receive(x, 3) != OK) {
582        return NULL;
583    }
584
585    sp<ABuffer> buffer = new ABuffer((x[1] << 8) | x[2]);
586    if (receive(buffer->data(), buffer->size()) != OK) {
587        return NULL;
588    }
589
590    buffer->meta()->setInt32("index", (int32_t)x[0]);
591
592    return buffer;
593}
594
595static bool IsRTSPVersion(const AString &s) {
596    return s == "RTSP/1.0";
597}
598
599bool ARTSPConnection::receiveRTSPReponse() {
600    AString statusLine;
601
602    if (!receiveLine(&statusLine)) {
603        return false;
604    }
605
606    if (statusLine == "$") {
607        sp<ABuffer> buffer = receiveBinaryData();
608
609        if (buffer == NULL) {
610            return false;
611        }
612
613        if (mObserveBinaryMessage != NULL) {
614            sp<AMessage> notify = mObserveBinaryMessage->dup();
615            notify->setObject("buffer", buffer);
616            notify->post();
617        } else {
618            ALOGW("received binary data, but no one cares.");
619        }
620
621        return true;
622    }
623
624    sp<ARTSPResponse> response = new ARTSPResponse;
625    response->mStatusLine = statusLine;
626
627    ALOGI("status: %s", response->mStatusLine.c_str());
628
629    ssize_t space1 = response->mStatusLine.find(" ");
630    if (space1 < 0) {
631        return false;
632    }
633    ssize_t space2 = response->mStatusLine.find(" ", space1 + 1);
634    if (space2 < 0) {
635        return false;
636    }
637
638    bool isRequest = false;
639
640    if (!IsRTSPVersion(AString(response->mStatusLine, 0, space1))) {
641        CHECK(IsRTSPVersion(
642                    AString(
643                        response->mStatusLine,
644                        space2 + 1,
645                        response->mStatusLine.size() - space2 - 1)));
646
647        isRequest = true;
648
649        response->mStatusCode = 0;
650    } else {
651        AString statusCodeStr(
652                response->mStatusLine, space1 + 1, space2 - space1 - 1);
653
654        if (!ParseSingleUnsignedLong(
655                    statusCodeStr.c_str(), &response->mStatusCode)
656                || response->mStatusCode < 100 || response->mStatusCode > 999) {
657            return false;
658        }
659    }
660
661    AString line;
662    ssize_t lastDictIndex = -1;
663    for (;;) {
664        if (!receiveLine(&line)) {
665            break;
666        }
667
668        if (line.empty()) {
669            break;
670        }
671
672        ALOGV("line: '%s'", line.c_str());
673
674        if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
675            // Support for folded header values.
676
677            if (lastDictIndex < 0) {
678                // First line cannot be a continuation of the previous one.
679                return false;
680            }
681
682            AString &value = response->mHeaders.editValueAt(lastDictIndex);
683            value.append(line);
684
685            continue;
686        }
687
688        ssize_t colonPos = line.find(":");
689        if (colonPos < 0) {
690            // Malformed header line.
691            return false;
692        }
693
694        AString key(line, 0, colonPos);
695        key.trim();
696        key.tolower();
697
698        line.erase(0, colonPos + 1);
699
700        lastDictIndex = response->mHeaders.add(key, line);
701    }
702
703    for (size_t i = 0; i < response->mHeaders.size(); ++i) {
704        response->mHeaders.editValueAt(i).trim();
705    }
706
707    unsigned long contentLength = 0;
708
709    ssize_t i = response->mHeaders.indexOfKey("content-length");
710
711    if (i >= 0) {
712        AString value = response->mHeaders.valueAt(i);
713        if (!ParseSingleUnsignedLong(value.c_str(), &contentLength)) {
714            return false;
715        }
716    }
717
718    if (contentLength > 0) {
719        response->mContent = new ABuffer(contentLength);
720
721        if (receive(response->mContent->data(), contentLength) != OK) {
722            return false;
723        }
724    }
725
726    if (response->mStatusCode == 401) {
727        if (mAuthType == NONE && mUser.size() > 0
728                && parseAuthMethod(response)) {
729            ssize_t i;
730            CHECK_EQ((status_t)OK, findPendingRequest(response, &i));
731            CHECK_GE(i, 0);
732
733            sp<AMessage> reply = mPendingRequests.valueAt(i);
734            mPendingRequests.removeItemsAt(i);
735
736            AString request;
737            CHECK(reply->findString("original-request", &request));
738
739            sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
740            msg->setMessage("reply", reply);
741            msg->setString("request", request.c_str(), request.size());
742
743            ALOGI("re-sending request with authentication headers...");
744            onSendRequest(msg);
745
746            return true;
747        }
748    }
749
750    return isRequest
751        ? handleServerRequest(response)
752        : notifyResponseListener(response);
753}
754
755bool ARTSPConnection::handleServerRequest(const sp<ARTSPResponse> &request) {
756    // Implementation of server->client requests is optional for all methods
757    // but we do need to respond, even if it's just to say that we don't
758    // support the method.
759
760    ssize_t space1 = request->mStatusLine.find(" ");
761    CHECK_GE(space1, 0);
762
763    AString response;
764    response.append("RTSP/1.0 501 Not Implemented\r\n");
765
766    ssize_t i = request->mHeaders.indexOfKey("cseq");
767
768    if (i >= 0) {
769        AString value = request->mHeaders.valueAt(i);
770
771        unsigned long cseq;
772        if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
773            return false;
774        }
775
776        response.append("CSeq: ");
777        response.append(cseq);
778        response.append("\r\n");
779    }
780
781    response.append("\r\n");
782
783    size_t numBytesSent = 0;
784    while (numBytesSent < response.size()) {
785        ssize_t n =
786            send(mSocket, response.c_str() + numBytesSent,
787                 response.size() - numBytesSent, 0);
788
789        if (n < 0 && errno == EINTR) {
790            continue;
791        }
792
793        if (n <= 0) {
794            if (n == 0) {
795                // Server closed the connection.
796                LOGE("Server unexpectedly closed the connection.");
797            } else {
798                LOGE("Error sending rtsp response (%s).", strerror(errno));
799            }
800
801            performDisconnect();
802
803            return false;
804        }
805
806        numBytesSent += (size_t)n;
807    }
808
809    return true;
810}
811
812// static
813bool ARTSPConnection::ParseSingleUnsignedLong(
814        const char *from, unsigned long *x) {
815    char *end;
816    *x = strtoul(from, &end, 10);
817
818    if (end == from || *end != '\0') {
819        return false;
820    }
821
822    return true;
823}
824
825status_t ARTSPConnection::findPendingRequest(
826        const sp<ARTSPResponse> &response, ssize_t *index) const {
827    *index = 0;
828
829    ssize_t i = response->mHeaders.indexOfKey("cseq");
830
831    if (i < 0) {
832        // This is an unsolicited server->client message.
833        return OK;
834    }
835
836    AString value = response->mHeaders.valueAt(i);
837
838    unsigned long cseq;
839    if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
840        return ERROR_MALFORMED;
841    }
842
843    i = mPendingRequests.indexOfKey(cseq);
844
845    if (i < 0) {
846        return -ENOENT;
847    }
848
849    *index = i;
850
851    return OK;
852}
853
854bool ARTSPConnection::notifyResponseListener(
855        const sp<ARTSPResponse> &response) {
856    ssize_t i;
857    status_t err = findPendingRequest(response, &i);
858
859    if (err == OK && i < 0) {
860        // An unsolicited server response is not a problem.
861        return true;
862    }
863
864    if (err != OK) {
865        return false;
866    }
867
868    sp<AMessage> reply = mPendingRequests.valueAt(i);
869    mPendingRequests.removeItemsAt(i);
870
871    reply->setInt32("result", OK);
872    reply->setObject("response", response);
873    reply->post();
874
875    return true;
876}
877
878bool ARTSPConnection::parseAuthMethod(const sp<ARTSPResponse> &response) {
879    ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
880
881    if (i < 0) {
882        return false;
883    }
884
885    AString value = response->mHeaders.valueAt(i);
886
887    if (!strncmp(value.c_str(), "Basic", 5)) {
888        mAuthType = BASIC;
889    } else {
890#if !defined(HAVE_ANDROID_OS)
891        // We don't have access to the MD5 implementation on the simulator,
892        // so we won't support digest authentication.
893        return false;
894#endif
895
896        CHECK(!strncmp(value.c_str(), "Digest", 6));
897        mAuthType = DIGEST;
898
899        i = value.find("nonce=");
900        CHECK_GE(i, 0);
901        CHECK_EQ(value.c_str()[i + 6], '\"');
902        ssize_t j = value.find("\"", i + 7);
903        CHECK_GE(j, 0);
904
905        mNonce.setTo(value, i + 7, j - i - 7);
906    }
907
908    return true;
909}
910
911#if defined(HAVE_ANDROID_OS)
912static void H(const AString &s, AString *out) {
913    out->clear();
914
915    MD5_CTX m;
916    MD5_Init(&m);
917    MD5_Update(&m, s.c_str(), s.size());
918
919    uint8_t key[16];
920    MD5_Final(key, &m);
921
922    for (size_t i = 0; i < 16; ++i) {
923        char nibble = key[i] >> 4;
924        if (nibble <= 9) {
925            nibble += '0';
926        } else {
927            nibble += 'a' - 10;
928        }
929        out->append(&nibble, 1);
930
931        nibble = key[i] & 0x0f;
932        if (nibble <= 9) {
933            nibble += '0';
934        } else {
935            nibble += 'a' - 10;
936        }
937        out->append(&nibble, 1);
938    }
939}
940#endif
941
942static void GetMethodAndURL(
943        const AString &request, AString *method, AString *url) {
944    ssize_t space1 = request.find(" ");
945    CHECK_GE(space1, 0);
946
947    ssize_t space2 = request.find(" ", space1 + 1);
948    CHECK_GE(space2, 0);
949
950    method->setTo(request, 0, space1);
951    url->setTo(request, space1 + 1, space2 - space1);
952}
953
954void ARTSPConnection::addAuthentication(AString *request) {
955    if (mAuthType == NONE) {
956        return;
957    }
958
959    // Find the boundary between headers and the body.
960    ssize_t i = request->find("\r\n\r\n");
961    CHECK_GE(i, 0);
962
963    if (mAuthType == BASIC) {
964        AString tmp;
965        tmp.append(mUser);
966        tmp.append(":");
967        tmp.append(mPass);
968
969        AString out;
970        encodeBase64(tmp.c_str(), tmp.size(), &out);
971
972        AString fragment;
973        fragment.append("Authorization: Basic ");
974        fragment.append(out);
975        fragment.append("\r\n");
976
977        request->insert(fragment, i + 2);
978
979        return;
980    }
981
982#if defined(HAVE_ANDROID_OS)
983    CHECK_EQ((int)mAuthType, (int)DIGEST);
984
985    AString method, url;
986    GetMethodAndURL(*request, &method, &url);
987
988    AString A1;
989    A1.append(mUser);
990    A1.append(":");
991    A1.append("Streaming Server");
992    A1.append(":");
993    A1.append(mPass);
994
995    AString A2;
996    A2.append(method);
997    A2.append(":");
998    A2.append(url);
999
1000    AString HA1, HA2;
1001    H(A1, &HA1);
1002    H(A2, &HA2);
1003
1004    AString tmp;
1005    tmp.append(HA1);
1006    tmp.append(":");
1007    tmp.append(mNonce);
1008    tmp.append(":");
1009    tmp.append(HA2);
1010
1011    AString digest;
1012    H(tmp, &digest);
1013
1014    AString fragment;
1015    fragment.append("Authorization: Digest ");
1016    fragment.append("nonce=\"");
1017    fragment.append(mNonce);
1018    fragment.append("\", ");
1019    fragment.append("username=\"");
1020    fragment.append(mUser);
1021    fragment.append("\", ");
1022    fragment.append("uri=\"");
1023    fragment.append(url);
1024    fragment.append("\", ");
1025    fragment.append("response=\"");
1026    fragment.append(digest);
1027    fragment.append("\"");
1028    fragment.append("\r\n");
1029
1030    request->insert(fragment, i + 2);
1031#endif
1032}
1033
1034// static
1035void ARTSPConnection::MakeUserAgent(AString *userAgent) {
1036    userAgent->clear();
1037    userAgent->setTo("User-Agent: stagefright/1.1 (Linux;Android ");
1038
1039#if (PROPERTY_VALUE_MAX < 8)
1040#error "PROPERTY_VALUE_MAX must be at least 8"
1041#endif
1042
1043    char value[PROPERTY_VALUE_MAX];
1044    property_get("ro.build.version.release", value, "Unknown");
1045    userAgent->append(value);
1046    userAgent->append(")\r\n");
1047}
1048
1049void ARTSPConnection::addUserAgent(AString *request) const {
1050    // Find the boundary between headers and the body.
1051    ssize_t i = request->find("\r\n\r\n");
1052    CHECK_GE(i, 0);
1053
1054    request->insert(mUserAgent, i + 2);
1055}
1056
1057}  // namespace android
1058