Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 368ef167 authored by Andreas Huber's avatar Andreas Huber
Browse files

Apparently our native TCP sockets do not return an error from blocking "connect"

if the network interface is shutdown while connecting.

Change-Id: I1bd7e43475edf20f35e15dd4eae547126289f5f0
related-to-bug: 3362836
parent bf6489f8
Loading
Loading
Loading
Loading
+81 −4
Original line number Diff line number Diff line
@@ -25,13 +25,14 @@
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/foundation/ADebug.h>

namespace android {

@@ -47,6 +48,82 @@ HTTPStream::~HTTPStream() {
    disconnect();
}

static bool MakeSocketBlocking(int s, bool blocking) {
    // Make socket non-blocking.
    int flags = fcntl(s, F_GETFL, 0);
    if (flags == -1) {
        return false;
    }

    if (blocking) {
        flags &= ~O_NONBLOCK;
    } else {
        flags |= O_NONBLOCK;
    }

    return fcntl(s, F_SETFL, flags) != -1;
}

static status_t MyConnect(
        int s, const struct sockaddr *addr, socklen_t addrlen) {
    status_t result = UNKNOWN_ERROR;

    MakeSocketBlocking(s, false);

    if (connect(s, addr, addrlen) == 0) {
        result = OK;
    } else if (errno != EINPROGRESS) {
        result = -errno;
    } else {
        for (;;) {
            fd_set rs, ws;
            FD_ZERO(&rs);
            FD_ZERO(&ws);
            FD_SET(s, &rs);
            FD_SET(s, &ws);

            struct timeval tv;
            tv.tv_sec = 0;
            tv.tv_usec = 100000ll;

            int nfds = ::select(s + 1, &rs, &ws, NULL, &tv);

            if (nfds < 0) {
                if (errno == EINTR) {
                    continue;
                }

                result = -errno;
                break;
            }

            if (FD_ISSET(s, &ws) && !FD_ISSET(s, &rs)) {
                result = OK;
                break;
            }

            if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
                // Get the pending error.
                int error = 0;
                socklen_t errorLen = sizeof(error);
                if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) {
                    // Couldn't get the real error, so report why not.
                    result = -errno;
                } else {
                    result = -error;
                }
                break;
            }

            // Timeout expired. Try again.
        }
    }

    MakeSocketBlocking(s, true);

    return result;
}

status_t HTTPStream::connect(const char *server, int port) {
    Mutex::Autolock autoLock(mLock);

@@ -82,7 +159,7 @@ status_t HTTPStream::connect(const char *server, int port) {
    addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
    memset(addr.sin_zero, 0, sizeof(addr.sin_zero));

    int res = ::connect(s, (const struct sockaddr *)&addr, sizeof(addr));
    status_t res = MyConnect(s, (const struct sockaddr *)&addr, sizeof(addr));

    mLock.lock();

@@ -90,12 +167,12 @@ status_t HTTPStream::connect(const char *server, int port) {
        return UNKNOWN_ERROR;
    }

    if (res < 0) {
    if (res != OK) {
        close(mSocket);
        mSocket = -1;

        mState = READY;
        return UNKNOWN_ERROR;
        return res;
    }

    mState = CONNECTED;