enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb)
{
    fd_set read_set, write_set;
    int socket;
    int rslt;
    struct timeval timev = { 0, 300000 };

    if (-1 == BIO_get_fd(pb->pal.socket, &socket)) {
        PUBNUB_LOG_ERROR("pbpal_connected(): Uninitialized BIO!\n");
        return pbpal_connect_resource_failure;
    }
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    FD_SET(socket, &read_set);
    FD_SET(socket, &write_set);
    rslt = select(socket + 1, &read_set, &write_set, NULL, &timev);
    if (SOCKET_ERROR == rslt) {
        PUBNUB_LOG_ERROR("pbpal_connected(): select() Error!\n");
        return pbpal_connect_resource_failure;
    }
    else if (rslt > 0) {
        PUBNUB_LOG_TRACE("pbpal_connected(): select() event\n");
        return pbpal_resolv_and_connect(pb);
    }
    PUBNUB_LOG_TRACE("pbpal_connected(): no select() events\n");
    return pbpal_connect_wouldblock;
}
Exemplo n.º 2
0
static void finish(struct pubnub_ *pb)
{
    enum pubnub_res pbres;

#if PUBNUB_PROXY_API
    switch (pbproxy_handle_finish(pb)) {
    case pbproxyFinError:
        outcome_detected(pb, PNR_HTTP_ERROR);
        return;
    case pbproxyFinRetryConnected:
        pb->state = PBS_CONNECTED;
        strcpy(pb->core.http_buf, pb->proxy_saved_path);
        return;
    case pbproxyFinRetryReconnect:
        PUBNUB_LOG_TRACE("Proxy: Will retry after close\n");
        strcpy(pb->core.http_buf, pb->proxy_saved_path);
        pb->retry_after_close = true;
        break;
    default:
        break;
    }
#endif

    pb->core.http_reply[pb->core.http_buf_len] = '\0';
    PUBNUB_LOG_TRACE("finish('%s')\n", pb->core.http_reply);

    pbres = parse_pubnub_result(pb);
    if ((PNR_OK == pbres) && ((pb->http_code / 100) != 2)) {
        pbres = PNR_HTTP_ERROR;
    }
    
    outcome_detected(pb, pbres);
}
static enum pbpal_resolv_n_connect_result resolv_and_connect_wout_SSL(pubnub_t *pb)
{
    PUBNUB_LOG_TRACE("resolv_and_connect_wout_SSL\n");
    if (NULL == pb->pal.socket) {
        char const*origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;
        PUBNUB_LOG_TRACE("pb=%p: Don't have BIO\n", pb);
        pb->pal.socket = BIO_new_connect((char*)origin);
    }
    if (NULL == pb->pal.socket) {
        return pbpal_resolv_resource_failure;
    }
    BIO_set_conn_port(pb->pal.socket, "http");

    BIO_set_nbio(pb->pal.socket, !pb->options.use_blocking_io);

    WATCH_ENUM(pb->options.use_blocking_io);
    if (BIO_do_connect(pb->pal.socket) <= 0) {
        if (BIO_should_retry(pb->pal.socket)) {
            return pbpal_connect_wouldblock;
        }
        ERR_print_errors_cb(print_to_pubnub_log, NULL);
        PUBNUB_LOG_ERROR("BIO_do_connect failed\n");
        return pbpal_connect_failed;
    }

    PUBNUB_LOG_TRACE("pb=%p: BIO connected\n", pb);
    {
        int fd = BIO_get_fd(pb->pal.socket, NULL);
        socket_set_rcv_timeout(fd, pb->transaction_timeout_ms);
    }

    return pbpal_connect_success;
}
Exemplo n.º 4
0
int pubnub_free(pubnub_t* pb)
{
    int result = -1;

    PUBNUB_ASSERT(check_ctx_ptr(pb));

    PUBNUB_LOG_TRACE("pubnub_free(%p)\n", pb);

    pubnub_mutex_lock(pb->monitor);
    pbnc_stop(pb, PNR_CANCELLED);
    if (PBS_IDLE == pb->state) {
        PUBNUB_LOG_TRACE("pubnub_free(%p) PBS_IDLE\n", pb);
        pb->state = PBS_NULL;
#if defined(PUBNUB_CALLBACK_API)
        pbntf_requeue_for_processing(pb);
        pubnub_mutex_unlock(pb->monitor);
#else
        pubnub_mutex_unlock(pb->monitor);
        pballoc_free_at_last(pb);
#endif

        result = 0;
    }
    else {
        PUBNUB_LOG_TRACE("pubnub_free(%p) pb->state=%d\n", pb, pb->state);
        pubnub_mutex_unlock(pb->monitor);
    }

    return result;
}
Exemplo n.º 5
0
enum pubnub_res pbpal_line_read_status(pubnub_t *pb)
{
    uint8_t c;

    if (pb->readlen == 0) {
        int recvres = socket_recv(pb->pal.socket, (char*)pb->ptr, pb->left, 0);
        if (recvres < 0) {
            if (socket_timed_out()) {
                return PNR_TIMEOUT;
            }
            if (PUBNUB_BLOCKING_IO_SETTABLE && pb->options.use_blocking_io) {
                return PNR_IO_ERROR;
            }
            return socket_would_block() ? PNR_IN_PROGRESS : PNR_IO_ERROR;
        }
        else if (0 == recvres) {
            return PNR_TIMEOUT;
        }
        PUBNUB_LOG_TRACE("have new data of length=%d: %s\n", recvres, pb->ptr);
        pb->sock_state = STATE_READ_LINE;
        pb->readlen = recvres;
    }

    while (pb->left > 0 && pb->readlen > 0) {
        c = *pb->ptr++;

        --pb->readlen;
        --pb->left;
        
        if (c == '\n') {
            int read_len = pbpal_read_len(pb);
            PUBNUB_LOG_TRACE("\n found: "); WATCH_INT(read_len); WATCH_USHORT(pb->readlen);
            pb->sock_state = STATE_NONE;
            return PNR_OK;
        }
    }

    if (pb->left == 0) {
        /* Buffer has been filled, but new-line char has not been
         * found.  We have to "reset" this "mini-fsm", as otherwise we
         * won't read anything any more. This means that we have lost
         * the current contents of the buffer, which is bad. In some
         * general code, that should be reported, as the caller could
         * save the contents of the buffer somewhere else or simply
         * decide to ignore this line (when it does end eventually).
         */
        pb->sock_state = STATE_NONE;
    }
    else {
        pb->sock_state = STATE_NEWDATA_EXHAUSTED;
    }

    return PNR_IN_PROGRESS;
}
Exemplo n.º 6
0
int read_response(int skt, struct sockaddr *dest, unsigned char const *host, struct sockaddr_in *resolved_addr)
{
    uint8_t buf[8192];
    struct DNS_HEADER *dns = (struct DNS_HEADER *)buf;
    uint8_t *qname = buf + sizeof *dns;
    uint8_t *reader;
    int i, msg_size;

    socklen_t addr_size = sizeof *dest;
    msg_size = recvfrom(skt, (char*)buf, sizeof buf, 0, dest, &addr_size);
    if (msg_size <= 0) {
        return socket_would_block() ? +1 : -1;
    }
 
    reader = buf + sizeof *dns + strlen((const char*)qname)+1 + sizeof(struct QUESTION);

    PUBNUB_LOG_TRACE("DNS response has: %d Questions, %d Answers, %d Auth. Servers, %d Additional records.\n",
                     ntohs(dns->q_count) ,ntohs(dns->ans_count) ,ntohs(dns->auth_count),ntohs(dns->add_count));
 
    for (i = 0; i < ntohs(dns->ans_count); ++i) {
        uint8_t name[256];
        size_t to_skip;
        struct R_DATA* prdata;
        size_t r_data_len;

        dns_label_decode(name, sizeof name, reader, buf, msg_size, &to_skip);
        prdata = (struct R_DATA*)(reader + to_skip);
        r_data_len = ntohs(prdata->data_len);
        reader += to_skip + sizeof *prdata;

        PUBNUB_LOG_TRACE("DNS answer: %s, to_skip:%zu, type=%d, data_len=%zu\n", name, to_skip, ntohs(prdata->type), r_data_len);
  
        if (ntohs(prdata->type) == dnsA) {
            if (r_data_len != 4) {
                PUBNUB_LOG_WARNING("unexpected answer R_DATA length %zu\n", r_data_len);
                continue;
            }
            PUBNUB_LOG_TRACE("Got IPv4: %d.%d.%d.%d\n", reader[0],reader[1],reader[2],reader[3]);
            resolved_addr->sin_family = AF_INET;
            memcpy(&resolved_addr->sin_addr, reader, 4);
            reader += r_data_len;
        }
        else {
            /* Don't care about other resource types, for now */
            reader += r_data_len;
        }
    }

    /* Don't care about Authoritative Servers or Additional records, for now */
 
    return 0;
}
Exemplo n.º 7
0
int pbpal_set_socket_blocking_io(pbpal_native_socket_t socket, int use_blocking_io)
{
    int flags = fcntl((int)socket, F_GETFL, 0);

    PUBNUB_LOG_TRACE("pbpal_set_socket_blocking_io(): before - flags = %X, flags&NONBLOCK = %X\n", flags, flags & O_NONBLOCK);
    if (-1 == flags) {
        flags = 0;
    }
    fcntl((int)socket, F_SETFL, flags | (use_blocking_io ? 0 : O_NONBLOCK));

    flags = fcntl((int)socket, F_GETFL, 0);
    PUBNUB_LOG_TRACE("pbpal_set_socket_blocking_io(): after - flags = %X, flags&NONBLOCK = %X\n", flags, flags & O_NONBLOCK);

    return 0;
}
Exemplo n.º 8
0
/** Split @p buf string containing a JSON array (with arbitrary
 * contents) to multiple NUL-terminated C strings, in-place.
 */
static bool split_array(char *buf)
{
    bool escaped = false;
    bool in_string = false;
    int bracket_level = 0;

    for (; *buf != '\0'; ++buf) {
        if (escaped) {
            escaped = false;
        }
        else if ('"' == *buf) {
            in_string = !in_string;
        }
        else if (in_string) {
            escaped = ('\\' == *buf);
        }
        else {
            switch (*buf) {
            case '[': case '{': bracket_level++; break;
            case ']': case '}': bracket_level--; break;
                /* if at root, split! */
            case ',': if (bracket_level == 0) { *buf = '\0'; } break;
            default: break;
            }
        }
    }

    PUBNUB_LOG_TRACE("escaped = %d, in_string = %d, bracket_level = %d\n", escaped, in_string, bracket_level);
    return !(escaped || in_string || (bracket_level > 0));
}
static void prepare_port_and_hostname(pubnub_t *pb, uint16_t* p_port, char const** p_origin)
{
    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
    PUBNUB_ASSERT_OPT((pb->state == PBS_READY) || (pb->state == PBS_WAIT_DNS_SEND));
    *p_origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;
#if PUBNUB_USE_SSL
    if (pb->flags.trySSL) {
        PUBNUB_ASSERT(pb->options.useSSL);
        *p_port = TLS_PORT;
    }
#endif
#if PUBNUB_PROXY_API
    switch (pb->proxy_type) {
    case pbproxyHTTP_CONNECT:
        if (!pb->proxy_tunnel_established) {
            *p_origin = pb->proxy_hostname;
        }
        *p_port = pb->proxy_port;
        break;
    case pbproxyHTTP_GET:
        *p_origin = pb->proxy_hostname;
        *p_port = pb->proxy_port;
        PUBNUB_LOG_TRACE("Using proxy: %s : %hu\n", *p_origin, *p_port);
        break;
    default:
        break;
    }
#endif
    return;
}
enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb)
{
    fd_set write_set;
    int rslt;
    struct timeval timev = { 0, 300000 };
    
    FD_ZERO(&write_set);
    FD_SET(pb->pal.socket, &write_set);
    rslt = select(pb->pal.socket + 1, NULL, &write_set, NULL, &timev);
    if (SOCKET_ERROR == rslt) {
        PUBNUB_LOG_ERROR("pbpal_connected(): select() Error!\n");
        return pbpal_connect_resource_failure;
    }
    else if (rslt > 0) {
        PUBNUB_LOG_TRACE("pbpal_connected(): select() event\n");
        return pbpal_connect_success;
    }
    PUBNUB_LOG_TRACE("pbpal_connected(): no select() events\n");
    return pbpal_connect_wouldblock;
}
Exemplo n.º 11
0
void pballoc_free_at_last(pubnub_t* pb)
{
    PUBNUB_LOG_TRACE("pballoc_free_at_last(%p)\n", pb);

    PUBNUB_ASSERT_OPT(pb != NULL);

    PUBNUB_LOG_TRACE("pubnub_free_at_last(%p)\n", pb);

    pubnub_mutex_lock(pb->monitor);
    pubnub_mutex_init_static(m_lock);
    pubnub_mutex_lock(m_lock);

    PUBNUB_ASSERT_OPT(pb->state == PBS_NULL);

    pbcc_deinit(&pb->core);
    pbpal_free(pb);
    remove_allocated(pb);
    pubnub_mutex_unlock(pb->monitor);
    pubnub_mutex_destroy(pb->monitor);
    pubnub_mutex_unlock(m_lock);
    free(pb);
}
Exemplo n.º 12
0
void pbproxy_handle_http_header(pubnub_t *p, char const* header)
{
    char scheme_basic[] = "Basic";
    char scheme_NTLM[] = "NTLM";
    char proxy_auth[] = "Proxy-Authenticate: ";
    char const* contents;

    PUBNUB_ASSERT_OPT(p != NULL);
    PUBNUB_ASSERT_OPT(header != NULL);

    if (strncmp(p->core.http_buf, proxy_auth, sizeof proxy_auth - 1) != 0) {
        return;
    }
    contents = p->core.http_buf + sizeof proxy_auth - 1;

    PUBNUB_LOG_TRACE("pbproxy_handle_http_header(header='%s', contents='%s')\n", header, contents);

    if (0 == strncmp(contents, scheme_basic, sizeof scheme_basic -1)) {
        /* We ignore the "realm" for now */
        PUBNUB_LOG_TRACE("pbproxy_handle_http_header() Basic authentication\n");
        p->proxy_auth_scheme = pbhtauBasic;
        p->proxy_authorization_sent = false;
    }
    else if (0 == strncmp(contents, scheme_NTLM, sizeof scheme_NTLM -1)) {
        if (pbhtauNTLM != p->proxy_auth_scheme) {
            pbntlm_core_init(p);
            p->proxy_auth_scheme = pbhtauNTLM;
            p->proxy_authorization_sent = false;
        }
        else {
            char const *base64_msg = contents + sizeof scheme_NTLM;
            pbntlm_core_handle(p, base64_msg, strcspn(base64_msg, " \r\n"));
        }
    }
    else {
        PUBNUB_LOG_ERROR("Proxy Authentication '%s' not supported\n", contents);
        p->proxy_auth_scheme = pbhtauNone;
    }
}
Exemplo n.º 13
0
int pbpal_close(pubnub_t *pb)
{
    pb->readlen = 0;
    if (pb->pal.socket != SOCKET_INVALID) {
        pbntf_lost_socket(pb, pb->pal.socket);
        socket_close(pb->pal.socket);
        pb->pal.socket = SOCKET_INVALID;
        pb->sock_state = STATE_NONE;
    }

    PUBNUB_LOG_TRACE("pbpal_close() returning 0\n");

    return 0;
}
Exemplo n.º 14
0
bool pbpal_read_over(pubnub_t *pb)
{
    unsigned to_read = 0;
    WATCH_ENUM(pb->sock_state);
    WATCH_USHORT(pb->readlen);
    WATCH_USHORT(pb->left);
    WATCH_UINT(pb->len);

    if (pb->readlen == 0) {
        int recvres;
        to_read =  pb->len - pbpal_read_len(pb);
        if (to_read > pb->left) {
            to_read = pb->left;
        }
        recvres = socket_recv(pb->pal.socket, (char*)pb->ptr, to_read, 0);
        if (recvres <= 0) {
            /* This is error or connection close, which may be handled
               in some way...
             */
            return false;
        }
        pb->sock_state = STATE_READ;
        pb->readlen = recvres;
    } 

    pb->ptr += pb->readlen;
    pb->left -= pb->readlen;
    pb->readlen = 0;

    if (pbpal_read_len(pb) >= (int)pb->len) {
        /* If we have read all that was requested, we're done. */
        PUBNUB_LOG_TRACE("Read all that was to be read.\n");
        pb->sock_state = STATE_NONE;
        return true;
    }

    if ((pb->left > 0)) {
        pb->sock_state = STATE_NEWDATA_EXHAUSTED;
        return false;
    }

    /* Otherwise, we just filled the buffer, but we return 'true', to
     * enable the user to copy the data from the buffer to some other
     * storage.
     */
    PUBNUB_LOG_WARNING("Filled the buffer, but read %d and should %d\n", pbpal_read_len(pb), pb->len);
    pb->sock_state = STATE_NONE;
    return true;
}
Exemplo n.º 15
0
static void outcome_detected(struct pubnub_ *pb, enum pubnub_res rslt)
{
    pb->core.last_result = rslt;
    if (pbpal_close(pb) <= 0) {
        PUBNUB_LOG_TRACE("outcome_detected(): pb->retry_after_close=%d\n", pb->retry_after_close);
        if (pb->retry_after_close) {
            pb->state = PBS_IDLE;
        }
        else {
            pbpal_forget(pb);
            pbntf_trans_outcome(pb);
        }
    }
    else {
        pb->state = PBS_WAIT_CLOSE;
    }
}
Exemplo n.º 16
0
int pubnub_free_with_timeout(pubnub_t* pbp, unsigned millisec)
{
    const clock_t t0 = clock();
    const clock_t clocks_till_timeout = (millisec * CLOCKS_PER_SEC) / 1000;

    PUBNUB_ASSERT_OPT(pbp != NULL);

    while (pubnub_free(pbp) != 0) {
        const clock_t elapsed = clock() - t0;
        if (elapsed  > clocks_till_timeout) {
            PUBNUB_LOG_ERROR("Failed to free the context in %u milli seconds\n", millisec);
            return -1;
        }
    }
    PUBNUB_LOG_TRACE("Freed the context in %lf seconds\n",  ((float)clock() - t0) / CLOCKS_PER_SEC);

    return 0;
}
Exemplo n.º 17
0
int pbnc_fsm(struct pubnub_ *pb)
{
    enum pubnub_res pbrslt;
    int i;

    PUBNUB_LOG_TRACE("pbnc_fsm()\t");

next_state:
    WATCH_ENUM_ONCHANGE(pb->state);
    switch (pb->state) {
    case PBS_NULL:
        break;
    case PBS_IDLE:
    {
        enum pbpal_resolv_n_connect_result rslv = pbpal_resolv_and_connect(pb);
        WATCH_ENUM(rslv);
        switch (rslv) {
        case pbpal_resolv_send_wouldblock:
            return 0;
        case pbpal_resolv_sent:
        case pbpal_resolv_rcv_wouldblock:
            pb->state = PBS_WAIT_DNS;
            break;
        case pbpal_connect_wouldblock:
            pb->state = PBS_WAIT_CONNECT;
            break;
        case pbpal_connect_success:
            pb->state = PBS_CONNECTED;
            break;
        default:
            pb->core.last_result = PNR_ADDR_RESOLUTION_FAILED;
            pbntf_trans_outcome(pb);
            return 0;
        }
        i = pbntf_got_socket(pb, pb->pal.socket);
        if (0 == i) {
            goto next_state;
        }
        else if (i < 0) {
            pb->core.last_result = PNR_CONNECT_FAILED;
            pbntf_trans_outcome(pb);
        }
        break;
    }
    case PBS_WAIT_DNS:
    {
        enum pbpal_resolv_n_connect_result rslv = pbpal_check_resolv_and_connect(pb);
        WATCH_ENUM(rslv);
        switch (rslv) {
        case pbpal_resolv_send_wouldblock:
        case pbpal_resolv_sent:
            pb->core.last_result = PNR_INTERNAL_ERROR;
            pbntf_trans_outcome(pb);
            break;
        case pbpal_resolv_rcv_wouldblock:
            break;
        case pbpal_connect_wouldblock:
            pbntf_update_socket(pb, pb->pal.socket);
            pb->state = PBS_WAIT_CONNECT;
            break;
        case pbpal_connect_success:
            pb->state = PBS_CONNECTED;
            goto next_state;
        default:
            pb->core.last_result = PNR_ADDR_RESOLUTION_FAILED;
            pbntf_trans_outcome(pb);
            break;
        }
        break;
    }
    case PBS_WAIT_CONNECT:
    {
        enum pbpal_resolv_n_connect_result rslv = pbpal_connected(pb);
        WATCH_ENUM(rslv);
        switch (rslv) {
        case pbpal_resolv_resource_failure:
        case pbpal_connect_resource_failure:
        case pbpal_connect_failed:
            pb->core.last_result = PNR_ADDR_RESOLUTION_FAILED;
            pbntf_trans_outcome(pb);
            return 0;
        case pbpal_connect_success:
            pb->state = PBS_CONNECTED;
            goto next_state;
        case pbpal_connect_wouldblock:
        default:
            pbntf_update_socket(pb, pb->pal.socket);
            break;
        }
        break;
    }
    case PBS_CONNECTED:
        pbpal_send_literal_str(pb, "GET ");
        pb->state = PBS_TX_GET;
        goto next_state;
    case PBS_TX_GET:
        i = pbpal_send_status(pb);
        if (i <= 0) {
            pb->state = PBS_TX_PATH;
            if ((i < 0) || (-1 == pbpal_send_str(pb, pb->core.http_buf))) {
                outcome_detected(pb, PNR_IO_ERROR);
                break;
            }
            goto next_state;
        }
        break;
    case PBS_TX_PATH:
        i = pbpal_send_status(pb);
        if (i < 0) {
            outcome_detected(pb, PNR_IO_ERROR);
        }
        else if (0 == i) {
            pbpal_send_literal_str(pb, " HTTP/1.1\r\nHost: ");
            pb->state = PBS_TX_VER;
            goto next_state;
        }
        break;
    case PBS_TX_VER:
        i = pbpal_send_status(pb);
        if (i <= 0) {
            char const* o = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;
            pb->state = PBS_TX_ORIGIN;
            if ((i < 0) || (-1 == pbpal_send_str(pb, o))) {
                outcome_detected(pb, PNR_IO_ERROR);
                break;
            }
            goto next_state;
        }
        break;
    case PBS_TX_ORIGIN:
        i = pbpal_send_status(pb);
        if (i < 0) {
            outcome_detected(pb, PNR_IO_ERROR);
        }
        else if (0 == i) {
            pbpal_send_literal_str(pb, "\r\nUser-Agent: PubNub-C-core/2.1\r\nConnection: Keep-Alive\r\n\r\n");
            pb->state = PBS_TX_FIN_HEAD;
            goto next_state;
        }
        break;
    case PBS_TX_FIN_HEAD:
        i = pbpal_send_status(pb);
        if (i < 0) {
            outcome_detected(pb, PNR_IO_ERROR);
        }
        else if (0 == i) {
            pbpal_start_read_line(pb);
            pb->state = PBS_RX_HTTP_VER;
            goto next_state;
        }
        break;
    case PBS_RX_HTTP_VER:
        pbrslt = pbpal_line_read_status(pb);
        switch (pbrslt) {
        case PNR_IN_PROGRESS:
            break;
        case PNR_OK:
            if (strncmp(pb->core.http_buf, "HTTP/1.", 7) != 0) {
                outcome_detected(pb, PNR_IO_ERROR);
                break;
            }
            pb->http_code = atoi(pb->core.http_buf + 9);
            WATCH_USHORT(pb->http_code);
            pb->core.http_content_len = 0;
            pb->http_chunked = false;
            pb->state = PBS_RX_HEADERS;
            goto next_state;
        default:
            outcome_detected(pb, pbrslt);
            break;
        }
        break;
    case PBS_RX_HEADERS:
        PUBNUB_LOG_TRACE("PBS_RX_HEADERS\n");
        pbpal_start_read_line(pb);
        pb->state = PBS_RX_HEADER_LINE;
        goto next_state;
    case PBS_RX_HEADER_LINE:
        PUBNUB_LOG_TRACE("PBS_RX_HEADER_LINE\n");
        pbrslt = pbpal_line_read_status(pb);
        switch (pbrslt) {
        case PNR_IN_PROGRESS:
            break;
        case PNR_OK:
        {
            char h_chunked[] = "Transfer-Encoding: chunked";
            char h_length[] = "Content-Length: ";
            int read_len = pbpal_read_len(pb);
            PUBNUB_LOG_TRACE("header line was read: %.*s\n", read_len, pb->core.http_buf);
            WATCH_INT(read_len);
            if (read_len <= 2) {
                pb->core.http_buf_len = 0;
                if (!pb->http_chunked) {
                    if (0 == pb->core.http_content_len) {
                        outcome_detected(pb, PNR_IO_ERROR);
                        break;
                    }
                    pb->state = PBS_RX_BODY;
                }
                else {
                    pb->state = PBS_RX_CHUNK_LEN;
                }
                goto next_state;
            }
            if (strncmp(pb->core.http_buf, h_chunked, sizeof h_chunked - 1) == 0) {
                pb->http_chunked = true;
            }
            else if (strncmp(pb->core.http_buf, h_length, sizeof h_length - 1) == 0) {
                size_t len = atoi(pb->core.http_buf + sizeof h_length - 1);
                if (0 != pbcc_realloc_reply_buffer(&pb->core, len)) {
                    outcome_detected(pb, PNR_REPLY_TOO_BIG);
                    break;
                }
                pb->core.http_content_len = len;
            }
            pb->state = PBS_RX_HEADERS;
            goto next_state;
        }
        default:
            outcome_detected(pb, pbrslt);
            break;
        }
        break;
    case PBS_RX_BODY:
        PUBNUB_LOG_TRACE("PBS_RX_BODY\n");
        if (pb->core.http_buf_len < pb->core.http_content_len) {
            pbpal_start_read(pb, pb->core.http_content_len - pb->core.http_buf_len);
            pb->state = PBS_RX_BODY_WAIT;
            goto next_state;
        }
        else {
            finish(pb);
        }
        break;
    case PBS_RX_BODY_WAIT:
        PUBNUB_LOG_TRACE("PBS_RX_BODY_WAIT\n");
        if (pbpal_read_over(pb)) {
            unsigned len = pbpal_read_len(pb);
            WATCH_UINT(len);
            WATCH_UINT(pb->core.http_buf_len);
            memcpy(
                pb->core.http_reply + pb->core.http_buf_len,
                pb->core.http_buf,
                len
                );
            pb->core.http_buf_len += len;
            pb->state = PBS_RX_BODY;
            goto next_state;
        }
        break;
    case PBS_RX_CHUNK_LEN:
        PUBNUB_LOG_TRACE("PBS_RX_CHUNK_LEN\n");
        pbpal_start_read_line(pb);
        pb->state = PBS_RX_CHUNK_LEN_LINE;
        goto next_state;
    case PBS_RX_CHUNK_LEN_LINE:
        pbrslt = pbpal_line_read_status(pb);
        PUBNUB_LOG_TRACE("PBS_RX_CHUNK_LEN_LINE: pbrslt=%d\n", pbrslt);
        switch (pbrslt) {
        case PNR_IN_PROGRESS:
            break;
        case PNR_OK:
        {
            unsigned chunk_length = strtoul(pb->core.http_buf, NULL, 16);

            PUBNUB_LOG_TRACE("About to read a chunk w/length: %d\n", chunk_length);
            if (chunk_length == 0) {
                finish(pb);
            }
            else if (chunk_length > sizeof pb->core.http_buf) {
                outcome_detected(pb, PNR_IO_ERROR);
            }
            else if (0 != pbcc_realloc_reply_buffer(&pb->core, pb->core.http_buf_len + chunk_length)) {
                outcome_detected(pb, PNR_REPLY_TOO_BIG);
            }
            else {
                pb->core.http_content_len = chunk_length + 2;
                pb->state = PBS_RX_BODY_CHUNK;
                goto next_state;
            }
            break;
        }
        default:
            outcome_detected(pb, pbrslt);
            break;
        }
        break;
    case PBS_RX_BODY_CHUNK:
        PUBNUB_LOG_TRACE("PBS_RX_BODY_CHUNK\n");
        if (pb->core.http_content_len > 0) {
            pbpal_start_read(pb, pb->core.http_content_len);
            pb->state = PBS_RX_BODY_CHUNK_WAIT;
        }
        else {
            pb->state = PBS_RX_CHUNK_LEN;
        }
        goto next_state;
    case PBS_RX_BODY_CHUNK_WAIT:
        PUBNUB_LOG_TRACE("PBS_RX_BODY_CHUNK_WAIT\n");
        if (pbpal_read_over(pb)) {
            unsigned len = pbpal_read_len(pb);

            PUBNUB_ASSERT_OPT(pb->core.http_content_len >= len);
            PUBNUB_ASSERT_OPT(len > 0);

            if (pb->core.http_content_len > 2) {
                unsigned to_copy = pb->core.http_content_len - 2;
                if (len < to_copy) {
                    to_copy = len;
                }
                memcpy(
                    pb->core.http_reply + pb->core.http_buf_len,
                    pb->core.http_buf,
                    to_copy
                    );
                pb->core.http_buf_len += to_copy;
            }
            pb->core.http_content_len -= len;
            pb->state = PBS_RX_BODY_CHUNK;
            goto next_state;
        }
        break;
    case PBS_WAIT_CLOSE:
        if (pbpal_closed(pb)) {
            pbpal_forget(pb);
            pbntf_trans_outcome(pb);
        }
        break;
    case PBS_WAIT_CANCEL:
        pb->state = PBS_WAIT_CANCEL_CLOSE;
        if (pbpal_close(pb) <= 0) {
            goto next_state;
        }
        break;
    case PBS_WAIT_CANCEL_CLOSE:
        if (pbpal_closed(pb)) {
            pbpal_forget(pb);
            pb->core.msg_ofs = pb->core.msg_end = 0;
            pbntf_trans_outcome(pb);
        }
        break;
    }
    return 0;
}
Exemplo n.º 18
0
static void finish(struct pubnub_ *pb)
{
    enum pubnub_res pbres = PNR_OK;

    pb->core.http_reply[pb->core.http_buf_len] = '\0';
    PUBNUB_LOG_TRACE("finish('%s')\n", pb->core.http_reply);

    switch (pb->trans) {
    case PBTT_SUBSCRIBE:
        if (pbcc_parse_subscribe_response(&pb->core) != 0) {
            PUBNUB_LOG_WARNING("parse_subscribe failed\n");
            pbres = PNR_FORMAT_ERROR;
        }
        break;
    case PBTT_PUBLISH:
        pbres = pbcc_parse_publish_response(&pb->core);
        if (pbres != PNR_OK) {
            PUBNUB_LOG_WARNING("parse_publish failed\n");
        }
        break;
    case PBTT_TIME:
        if (pbcc_parse_time_response(&pb->core) != 0) {
            PUBNUB_LOG_WARNING("parse_time failed\n");
            pbres = PNR_FORMAT_ERROR;
        }
        break;
    case PBTT_HISTORY:
        if (pbcc_parse_history_response(&pb->core) != 0) {
            PUBNUB_LOG_WARNING("parse_history failed\n");
            pbres = PNR_FORMAT_ERROR;
        }
        break;
    case PBTT_LEAVE:
    case PBTT_HERENOW:
    case PBTT_GLOBAL_HERENOW:
    case PBTT_WHERENOW:
    case PBTT_SET_STATE:
    case PBTT_STATE_GET:
    case PBTT_HEARTBEAT:
        if (pbcc_parse_presence_response(&pb->core) != 0) {
            PUBNUB_LOG_WARNING("parse_presence failed\n");
            pbres = PNR_FORMAT_ERROR;
        }
        break;
    case PBTT_REMOVE_CHANNEL_GROUP:
    case PBTT_REMOVE_CHANNEL_FROM_GROUP:
    case PBTT_ADD_CHANNEL_TO_GROUP:
    case PBTT_LIST_CHANNEL_GROUP:
        pbres = pbcc_parse_channel_registry_response(&pb->core);
        if (pbres != PNR_OK) {
            PUBNUB_LOG_WARNING("parse_channel_registry failed\n");
        }
        break;
    default:
        break;
    }

    if ((PNR_OK == pbres) && ((pb->http_code / 100) != 2)) {
        pbres = PNR_HTTP_ERROR;
    }

    outcome_detected(pb, pbres);
}
enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb)
{
    SSL *ssl = NULL;
    int rslt;
    char const* origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;

    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
    PUBNUB_ASSERT_OPT((pb->state == PBS_READY) || (pb->state == PBS_WAIT_CONNECT));

    if (!pb->options.useSSL) {
        return resolv_and_connect_wout_SSL(pb);
    }

    if (NULL == pb->pal.ctx) {
        PUBNUB_LOG_TRACE("pb=%p: Don't have SSL_CTX\n", pb);
        pb->pal.ctx = SSL_CTX_new(SSLv23_client_method());
        if (NULL == pb->pal.ctx) {
            ERR_print_errors_cb(print_to_pubnub_log, NULL);
            PUBNUB_LOG_ERROR("pb=%p SSL_CTX_new failed\n", pb);
            return pbpal_resolv_resource_failure;
        }
        PUBNUB_LOG_TRACE("pb=%p: Got SSL_CTX\n", pb);
        add_pubnub_cert(pb->pal.ctx);
    }

    if (NULL == pb->pal.socket) {
        PUBNUB_LOG_TRACE("pb=%p: Don't have BIO\n", pb);
        pb->pal.socket = BIO_new_ssl_connect(pb->pal.ctx);
        if (PUBNUB_TIMERS_API) {
            pb->pal.connect_timeout = time(NULL)  + pb->transaction_timeout_ms/1000;
        }
    }
    else {
        BIO_get_ssl(pb->pal.socket, &ssl);
        if (NULL == ssl) {
            return resolv_and_connect_wout_SSL(pb);
        }
        ssl = NULL;
    }
    if (NULL == pb->pal.socket) {
        ERR_print_errors_cb(print_to_pubnub_log, NULL);
        return pbpal_resolv_resource_failure;
    }

    PUBNUB_LOG_TRACE("pb=%p: Using BIO == %p\n", pb, pb->pal.socket);

    BIO_get_ssl(pb->pal.socket, &ssl);
    PUBNUB_ASSERT(NULL != ssl);
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* maybe not auto_retry? */
    if (pb->pal.session != NULL) {
        SSL_set_session(ssl, pb->pal.session);
    }

    BIO_set_conn_hostname(pb->pal.socket, origin);
    BIO_set_conn_port(pb->pal.socket, "https");
    if (pb->pal.ip_timeout != 0) {
        if (pb->pal.ip_timeout < time(NULL)) {
            pb->pal.ip_timeout = 0;
        }
        else {
            PUBNUB_LOG_TRACE("SSL re-connect to: %d.%d.%d.%d\n", pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]);
            BIO_set_conn_ip(pb->pal.socket, pb->pal.ip);
        }
    }
    
    BIO_set_nbio(pb->pal.socket, !pb->options.use_blocking_io);
    
    WATCH_ENUM(pb->options.use_blocking_io);
    if (BIO_do_connect(pb->pal.socket) <= 0) {
        if (BIO_should_retry(pb->pal.socket) && PUBNUB_TIMERS_API && (pb->pal.connect_timeout > time(NULL))) {
            PUBNUB_LOG_TRACE("pb=%p: BIO_should_retry\n", pb);
            return pbpal_connect_wouldblock;
        }
        /* Expire the IP for the next connect */
        pb->pal.ip_timeout = 0;
        ERR_print_errors_cb(print_to_pubnub_log, NULL);
        if (pb->pal.session != NULL) {
            SSL_SESSION_free(pb->pal.session);
            pb->pal.session = NULL;
        }
        PUBNUB_LOG_ERROR("pb=%p: BIO_do_connect failed\n", pb);
        return pbpal_connect_failed;
    }

    PUBNUB_LOG_TRACE("pb=%p: BIO connected\n", pb);
    {
        int fd = BIO_get_fd(pb->pal.socket, NULL);
        socket_set_rcv_timeout(fd, pb->transaction_timeout_ms);
    }

    rslt = SSL_get_verify_result(ssl);
    if (rslt != X509_V_OK) {
        PUBNUB_LOG_WARNING("pb=%p: SSL_get_verify_result() failed == %d(%s)\n", pb, rslt, X509_verify_cert_error_string(rslt));
        ERR_print_errors_cb(print_to_pubnub_log, NULL);
        if (pb->options.fallbackSSL) {
            BIO_free_all(pb->pal.socket);
            pb->pal.socket = NULL;
            return resolv_and_connect_wout_SSL(pb);
        }
        return pbpal_connect_failed;
    }

    PUBNUB_LOG_INFO("pb=%p: SSL session reused: %s\n", pb, SSL_session_reused(ssl) ? "yes" : "no");
    if (pb->pal.session != NULL) {
        SSL_SESSION_free(pb->pal.session);
    }
    pb->pal.session = SSL_get1_session(ssl);
    if (0 == pb->pal.ip_timeout) {
        pb->pal.ip_timeout = SSL_SESSION_get_time(pb->pal.session) + SSL_SESSION_get_timeout(pb->pal.session);
        memcpy(pb->pal.ip, BIO_get_conn_ip(pb->pal.socket), 4);
    }
    PUBNUB_LOG_TRACE("pb=%p: SSL connected to IP: %d.%d.%d.%d\n", pb, pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]);

    return pbpal_connect_success;
}
Exemplo n.º 20
0
void pbproxy_handle_http_header(pubnub_t* p, char const* header)
{
    char        scheme_basic[]  = "Basic";
    char        scheme_digest[] = "Digest";
    char        scheme_NTLM[]   = "NTLM";
    char        proxy_auth[]    = "Proxy-Authenticate: ";
    char const* contents;

    PUBNUB_ASSERT_OPT(p != NULL);
    PUBNUB_ASSERT_OPT(header != NULL);

    switch (header[0]) {
    case ' ':
    case '\t':
        /* Though this is not very nice, we only support multi-line
           headers for Digest proxy. In practice, Basic and NTLM never
           use multi-line headers.
        */
        if (p->proxy_auth_scheme != pbhtauDigest) {
            return;
        }
        pbhttp_digest_parse_header(&p->digest_context, header + 1);
        return;
    default:
        if (strncmp(header, proxy_auth, sizeof proxy_auth - 1) != 0) {
            return;
        }
        break;
    }
    contents = header + sizeof proxy_auth - 1;

    PUBNUB_LOG_TRACE("pbproxy_handle_http_header(header='%s', contents='%s')\n",
                     header,
                     contents);

    if (0 == strncmp(contents, scheme_basic, sizeof scheme_basic - 1)) {
        /* We ignore the "realm" for now */
        PUBNUB_LOG_TRACE("pbproxy_handle_http_header() Basic authentication\n");
        p->proxy_auth_scheme        = pbhtauBasic;
        p->proxy_authorization_sent = false;
    }
    else if (0 == strncmp(contents, scheme_digest, sizeof scheme_digest - 1)) {
        /* We ignore the "realm" for now */
        PUBNUB_LOG_TRACE(
            "pbproxy_handle_http_header() Digest authentication\n");
        p->proxy_auth_scheme = pbhtauDigest;
        pbhttp_digest_init(&p->digest_context);
        pbhttp_digest_parse_header(&p->digest_context,
                                   contents + sizeof scheme_digest);
        p->proxy_authorization_sent = false;
    }
    else if (0 == strncmp(contents, scheme_NTLM, sizeof scheme_NTLM - 1)) {
        if (pbhtauNTLM != p->proxy_auth_scheme) {
            pbntlm_core_init(p);
            p->proxy_auth_scheme        = pbhtauNTLM;
            p->proxy_authorization_sent = false;
        }
        else {
            char const* base64_msg = contents + sizeof scheme_NTLM;
            pbntlm_core_handle(p, base64_msg, strcspn(base64_msg, " \r\n"));
        }
    }
    else {
        PUBNUB_LOG_ERROR("Proxy Authentication '%s' not supported\n", contents);
        p->proxy_auth_scheme = pbhtauNone;
    }
}
Exemplo n.º 21
0
int pbnc_fsm(struct pubnub_ *pb)
{
    enum pubnub_res pbrslt;
    int i;

    PUBNUB_LOG_TRACE("pbnc_fsm()\t");

next_state:
    WATCH_ENUM(pb->state);
    switch (pb->state) {
    case PBS_NULL:
        break;
    case PBS_IDLE:
        pb->retry_after_close = false;
        pb->state = PBS_READY;
        switch (pbntf_enqueue_for_processing(pb)) {
        case -1:
            pb->core.last_result = PNR_INTERNAL_ERROR;
            pbntf_trans_outcome(pb);
            return 0;
        case 0:
            goto next_state;
        case +1:
            break;
        }
        break;
    case PBS_READY:
    {
        enum pbpal_resolv_n_connect_result rslv = pbpal_resolv_and_connect(pb);
        WATCH_ENUM(rslv);
        switch (rslv) {
        case pbpal_resolv_send_wouldblock:
            pb->state = PBS_WAIT_DNS_SEND;
            break;
        case pbpal_resolv_sent:
        case pbpal_resolv_rcv_wouldblock:
            pb->state = PBS_WAIT_DNS_RCV;
            pbntf_watch_in_events(pb);
            break;
        case pbpal_connect_wouldblock:
            pb->state = PBS_WAIT_CONNECT;
            break;
        case pbpal_connect_success:
            pb->state = PBS_CONNECTED;
            break;
        default:
            pb->core.last_result = PNR_ADDR_RESOLUTION_FAILED;
            pbntf_trans_outcome(pb);
            return 0;
        }
        i = pbntf_got_socket(pb, pb->pal.socket);
        if (0 == i) {
            goto next_state;
        }
        else if (i < 0) {
            pb->core.last_result = PNR_CONNECT_FAILED;
            pbntf_trans_outcome(pb);
        }
        break;
    }
    case PBS_WAIT_DNS_SEND:
    {
        enum pbpal_resolv_n_connect_result rslv = pbpal_check_resolv_and_connect(pb);
        WATCH_ENUM(rslv);
        switch (rslv) {
        case pbpal_resolv_send_wouldblock:
            break;
        case pbpal_resolv_sent:
        case pbpal_resolv_rcv_wouldblock:
            pbntf_update_socket(pb, pb->pal.socket);
            pb->state = PBS_WAIT_DNS_RCV;
            pbntf_watch_in_events(pb);
            break;
        case pbpal_connect_wouldblock:
            pbntf_update_socket(pb, pb->pal.socket);
            pb->state = PBS_WAIT_CONNECT;
            break;
        case pbpal_connect_success:
            pb->state = PBS_CONNECTED;
            goto next_state;
        default:
            outcome_detected(pb, PNR_ADDR_RESOLUTION_FAILED);
            break;
        }
        break;
    }
    case PBS_WAIT_DNS_RCV:
    {
        enum pbpal_resolv_n_connect_result rslv = pbpal_check_resolv_and_connect(pb);
        WATCH_ENUM(rslv);
        switch (rslv) {
        case pbpal_resolv_send_wouldblock:
        case pbpal_resolv_sent:
            outcome_detected(pb, PNR_INTERNAL_ERROR);
            break;
        case pbpal_resolv_rcv_wouldblock:
            break;
        case pbpal_connect_wouldblock:
            pbntf_update_socket(pb, pb->pal.socket);
            pb->state = PBS_WAIT_CONNECT;
            pbntf_watch_out_events(pb);
            break;
        case pbpal_connect_success:
            pb->state = PBS_CONNECTED;
            pbntf_watch_out_events(pb);
            goto next_state;
        default:
            outcome_detected(pb, PNR_ADDR_RESOLUTION_FAILED);
            break;
        }
        break;
    }
    case PBS_WAIT_CONNECT:
    {
        enum pbpal_resolv_n_connect_result rslv = pbpal_check_connect(pb);
        WATCH_ENUM(rslv);
        switch (rslv) {
        case pbpal_resolv_send_wouldblock:
        case pbpal_resolv_sent:
        case pbpal_resolv_rcv_wouldblock:
            pb->core.last_result = PNR_INTERNAL_ERROR;
            pbntf_trans_outcome(pb);
            break;
        case pbpal_connect_wouldblock:
            break;
        case pbpal_connect_success:
            pb->state = PBS_CONNECTED;
            goto next_state;
        default:
            outcome_detected(pb, PNR_CONNECT_FAILED);
            break;
        }
        break;
    }
    case PBS_CONNECTED:
#if PUBNUB_PROXY_API
        if ((pb->proxy_type == pbproxyHTTP_CONNECT) && (!pb->proxy_tunnel_established)) {
            pbpal_send_literal_str(pb, "CONNECT ");
        }
        else {
            pbpal_send_literal_str(pb, "GET ");
        }
#else
        pbpal_send_literal_str(pb, "GET ");
#endif
        pb->state = PBS_TX_GET;
        goto next_state;
    case PBS_TX_GET:
        i = pbpal_send_status(pb);
        if (i <= 0) {
#if PUBNUB_PROXY_API
            switch (pb->proxy_type) {
            case pbproxyHTTP_GET:
                pb->state = PBS_TX_SCHEME;
                if (i < 0) {
                    outcome_detected(pb, PNR_IO_ERROR);
                    break;
                }
                strcpy(pb->proxy_saved_path, pb->core.http_buf);
                pbpal_send_literal_str(pb, "http://");
                break;
            case pbproxyHTTP_CONNECT:
                pb->state = PBS_TX_SCHEME;
                if (i < 0) {
                    outcome_detected(pb, PNR_IO_ERROR);
                    break;
                }
                if (!pb->proxy_tunnel_established) {
                    strcpy(pb->proxy_saved_path, pb->core.http_buf);
                }
                else {
                    strcpy(pb->core.http_buf, pb->proxy_saved_path);
                }
                break;
            case pbproxyNONE:
                pb->state = PBS_TX_PATH;
                if ((i < 0) || (-1 == pbpal_send_str(pb, pb->core.http_buf))) {
                    outcome_detected(pb, PNR_IO_ERROR);
                }
                break;
            default:
                outcome_detected(pb, PNR_INTERNAL_ERROR);
                break;
            }
#else
            pb->state = PBS_TX_PATH;
            if ((i < 0) || (-1 == pbpal_send_str(pb, pb->core.http_buf))) {
                outcome_detected(pb, PNR_IO_ERROR);
                break;
            }
#endif /* PUBNUB_PROXY_API */
            goto next_state;
        }
        break;
#if PUBNUB_PROXY_API
    case PBS_TX_SCHEME:
        i = pbpal_send_status(pb);
        if (i <= 0) {
            if ((pb->proxy_type == pbproxyHTTP_CONNECT) && pb->proxy_tunnel_established) {
                pb->state = PBS_TX_HOST;
            }
            else {
                char const* o = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;
                pb->state = PBS_TX_HOST;
                if ((i < 0) || (-1 == pbpal_send_str(pb, o))) {
                    outcome_detected(pb, PNR_IO_ERROR);
                    break;
                }
            }
            goto next_state;
        }
        break;
    case PBS_TX_HOST:
        i = pbpal_send_status(pb);
        if (i <= 0) {
            if ((pb->proxy_type == pbproxyHTTP_CONNECT) && !pb->proxy_tunnel_established) {
                char port_num[20];
                snprintf(port_num, sizeof port_num, ":%d", 80);
                pbpal_send_str(pb, port_num);
                pb->state = PBS_TX_PORT_NUM;
                goto next_state;
            }
            else {
                pb->state = PBS_TX_PATH;
                if ((i < 0) || (-1 == pbpal_send_str(pb, pb->core.http_buf))) {
                    outcome_detected(pb, PNR_IO_ERROR);
                    break;
                }
            }
            goto next_state;
        }
        break;
    case PBS_TX_PORT_NUM:
        i = pbpal_send_status(pb);
        if (i <= 0) {
            pb->state = PBS_TX_PATH;
            if (i < 0) {
                outcome_detected(pb, PNR_IO_ERROR);
                break;
            }
            goto next_state;
        }
        break;
#endif /* PUBNUB_PROXY_API */
    case PBS_TX_PATH:
        i = pbpal_send_status(pb);
        if (i < 0) {
            outcome_detected(pb, PNR_IO_ERROR);
        }
        else if (0 == i) {
            pbpal_send_literal_str(pb, " HTTP/1.1\r\nHost: ");
            pb->state = PBS_TX_VER;
            goto next_state;
        }
        break;
    case PBS_TX_VER:
        i = pbpal_send_status(pb);
        if (i <= 0) {
            char const* o = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;
            pb->state = PBS_TX_ORIGIN;
            if ((i < 0) || (-1 == pbpal_send_str(pb, o))) {
                outcome_detected(pb, PNR_IO_ERROR);
                break;
            }
            goto next_state;
        }
        break;
    case PBS_TX_ORIGIN:
        i = pbpal_send_status(pb);
        if (i < 0) {
            outcome_detected(pb, PNR_IO_ERROR);
        }
        else if (0 == i) {
#if PUBNUB_PROXY_API
            char header_to_send[1024] = "\r\n";
            if (0 == pbproxy_http_header_to_send(pb, header_to_send+2, sizeof header_to_send-2)) {
                PUBNUB_LOG_TRACE("Sending HTTP proxy header: '%s'\n", header_to_send);
                pb->state = PBS_TX_PROXY_AUTHORIZATION;
                if ((i < 0) || (-1 == pbpal_send_str(pb, header_to_send))) {
                    outcome_detected(pb, PNR_IO_ERROR);
                    break;
                }
            }
            else {
                pbpal_send_literal_str(pb, "\r\nUser-Agent: PubNub-C-core/2.2\r\nConnection: Keep-Alive\r\n\r\n");
                pb->state = PBS_TX_FIN_HEAD;
            }
#else
            pbpal_send_literal_str(pb, "\r\nUser-Agent: PubNub-C-core/2.2\r\nConnection: Keep-Alive\r\n\r\n");
            pb->state = PBS_TX_FIN_HEAD;
#endif
            goto next_state;
        }
        break;
    case PBS_TX_PROXY_AUTHORIZATION:
        i = pbpal_send_status(pb);
        if (i < 0) {
            outcome_detected(pb, PNR_IO_ERROR);
        }
        else if (0 == i) {
            pbpal_send_literal_str(pb, "\r\nUser-Agent: PubNub-C-core/2.2\r\nConnection: Keep-Alive\r\n\r\n");
            pb->state = PBS_TX_FIN_HEAD;
            goto next_state;
        }
        break;
    case PBS_TX_FIN_HEAD:
        i = pbpal_send_status(pb);
        if (i < 0) {
            outcome_detected(pb, PNR_IO_ERROR);
        }
        else if (0 == i) {
            pbpal_start_read_line(pb);
            pb->state = PBS_RX_HTTP_VER;
            pbntf_watch_in_events(pb);
            goto next_state;
        }
        break;
    case PBS_RX_HTTP_VER:
        pbrslt = pbpal_line_read_status(pb);
        switch (pbrslt) {
        case PNR_IN_PROGRESS:
            break;
        case PNR_OK:
            if (strncmp(pb->core.http_buf, "HTTP/1.", 7) != 0) {
                outcome_detected(pb, PNR_IO_ERROR);
                break;
            }
            pb->http_code = atoi(pb->core.http_buf + 9);
            WATCH_USHORT(pb->http_code);
            pb->core.http_content_len = 0;
            pb->http_chunked = false;
            pb->state = PBS_RX_HEADERS;
            goto next_state;
        default:
            outcome_detected(pb, pbrslt);
            break;
        }
        break;
    case PBS_RX_HEADERS:
        PUBNUB_LOG_TRACE("PBS_RX_HEADERS\n");
        pbpal_start_read_line(pb);
        pb->state = PBS_RX_HEADER_LINE;
        goto next_state;
    case PBS_RX_HEADER_LINE:
        PUBNUB_LOG_TRACE("PBS_RX_HEADER_LINE\n");
        pbrslt = pbpal_line_read_status(pb);
        switch (pbrslt) {
        case PNR_IN_PROGRESS:
            break;
        case PNR_OK:
        {
            char h_chunked[] = "Transfer-Encoding: chunked";
            char h_length[] = "Content-Length: ";
            int read_len = pbpal_read_len(pb);
            PUBNUB_LOG_TRACE("header line was read: %.*s\n", read_len, pb->core.http_buf);
            WATCH_INT(read_len);
            if (read_len <= 2) {
                pb->core.http_buf_len = 0;
                if (!pb->http_chunked) {
                    if (0 == pb->core.http_content_len) {
#if PUBNUB_PROXY_API
                        if ((pb->proxy_type == pbproxyHTTP_CONNECT) && !pb->proxy_tunnel_established) {
                            finish(pb);
                            if (pb->retry_after_close) {
                                goto next_state;
                            }
                            break;
                        }
#endif
                        outcome_detected(pb, PNR_IO_ERROR);
                        break;
                    }
                    pb->state = PBS_RX_BODY;
                }
                else {
                    pb->state = PBS_RX_CHUNK_LEN;
                }
                goto next_state;
            }
            if (strncmp(pb->core.http_buf, h_chunked, sizeof h_chunked - 1) == 0) {
                pb->http_chunked = true;
            }
            else if (strncmp(pb->core.http_buf, h_length, sizeof h_length - 1) == 0) {
                size_t len = atoi(pb->core.http_buf + sizeof h_length - 1);
                if (0 != pbcc_realloc_reply_buffer(&pb->core, len)) {
                    outcome_detected(pb, PNR_REPLY_TOO_BIG);
                    break;
                }
                pb->core.http_content_len = len;
            }
            else {
                pbproxy_handle_http_header(pb, pb->core.http_buf);
            }
            pb->state = PBS_RX_HEADERS;
            goto next_state;
        }
        default:
            outcome_detected(pb, pbrslt);
            break;
        }
        break;
    case PBS_RX_BODY:
        PUBNUB_LOG_TRACE("PBS_RX_BODY\n");
        if (pb->core.http_buf_len < pb->core.http_content_len) {
            pbpal_start_read(pb, pb->core.http_content_len - pb->core.http_buf_len);
            pb->state = PBS_RX_BODY_WAIT;
            goto next_state;
        }
        else {
            finish(pb);
            if (pb->retry_after_close) {
                goto next_state;
            }
        }
        break;
    case PBS_RX_BODY_WAIT:
        PUBNUB_LOG_TRACE("PBS_RX_BODY_WAIT\n");
        if (pbpal_read_over(pb)) {
            unsigned len = pbpal_read_len(pb);
            WATCH_UINT(len);
            WATCH_UINT(pb->core.http_buf_len);
            memcpy(
                pb->core.http_reply + pb->core.http_buf_len,
                pb->core.http_buf,
                len
                );
            pb->core.http_buf_len += len;
            pb->state = PBS_RX_BODY;
            goto next_state;
        }
        break;
    case PBS_RX_CHUNK_LEN:
        PUBNUB_LOG_TRACE("PBS_RX_CHUNK_LEN\n");
        pbpal_start_read_line(pb);
        pb->state = PBS_RX_CHUNK_LEN_LINE;
        goto next_state;
    case PBS_RX_CHUNK_LEN_LINE:
        pbrslt = pbpal_line_read_status(pb);
        PUBNUB_LOG_TRACE("PBS_RX_CHUNK_LEN_LINE: pbrslt=%d\n", pbrslt);
        switch (pbrslt) {
        case PNR_IN_PROGRESS:
            break;
        case PNR_OK:
        {
            unsigned chunk_length = strtoul(pb->core.http_buf, NULL, 16);

            PUBNUB_LOG_TRACE("About to read a chunk w/length: %d\n", chunk_length);
            if (chunk_length == 0) {
                finish(pb);
                if (pb->retry_after_close) {
                    goto next_state;
                }
            }
            else if (chunk_length > sizeof pb->core.http_buf) {
                outcome_detected(pb, PNR_IO_ERROR);
            }
            else if (0 != pbcc_realloc_reply_buffer(&pb->core, pb->core.http_buf_len + chunk_length)) {
                outcome_detected(pb, PNR_REPLY_TOO_BIG);
            }
            else {
                pb->core.http_content_len = chunk_length + 2;
                pb->state = PBS_RX_BODY_CHUNK;
                goto next_state;
            }
            break;
        }
        default:
            outcome_detected(pb, pbrslt);
            break;
        }
        break;
    case PBS_RX_BODY_CHUNK:
        PUBNUB_LOG_TRACE("PBS_RX_BODY_CHUNK\n");
        if (pb->core.http_content_len > 0) {
            pbpal_start_read(pb, pb->core.http_content_len);
            pb->state = PBS_RX_BODY_CHUNK_WAIT;
        }
        else {
            pb->state = PBS_RX_CHUNK_LEN;
        }
        goto next_state;
    case PBS_RX_BODY_CHUNK_WAIT:
        PUBNUB_LOG_TRACE("PBS_RX_BODY_CHUNK_WAIT\n");
        if (pbpal_read_over(pb)) {
            unsigned len = pbpal_read_len(pb);

            PUBNUB_ASSERT_OPT(pb->core.http_content_len >= len);
            PUBNUB_ASSERT_OPT(len > 0);

            if (pb->core.http_content_len > 2) {
                unsigned to_copy = pb->core.http_content_len - 2;
                if (len < to_copy) {
                    to_copy = len;
                }
                memcpy(
                    pb->core.http_reply + pb->core.http_buf_len,
                    pb->core.http_buf,
                    to_copy
                    );
                pb->core.http_buf_len += to_copy;
            }
            pb->core.http_content_len -= len;
            pb->state = PBS_RX_BODY_CHUNK;
            goto next_state;
        }
        break;
    case PBS_WAIT_CLOSE:
        if (pbpal_closed(pb)) {
            if (pb->retry_after_close) {
                pb->state = PBS_IDLE;
            }
            else {
                pbpal_forget(pb);
                pbntf_trans_outcome(pb);
            }
        }
        break;
    case PBS_WAIT_CANCEL:
        pb->state = PBS_WAIT_CANCEL_CLOSE;
        if (pbpal_close(pb) <= 0) {
            goto next_state;
        }
        break;
    case PBS_WAIT_CANCEL_CLOSE:
        if (pbpal_closed(pb)) {
            if (pb->retry_after_close) {
                pb->state = PBS_IDLE;
            }
            else {
                pbpal_forget(pb);
                pb->core.msg_ofs = pb->core.msg_end = 0;
                pbntf_trans_outcome(pb);
            }
        }
        break;
    }
    return 0;
}
Exemplo n.º 22
0
int pbproxy_http_header_to_send(pubnub_t *p, char* header, size_t n)
{
    PUBNUB_ASSERT_OPT(p != NULL);
    PUBNUB_ASSERT_OPT(header != NULL);

    switch (p->proxy_auth_scheme) {
    case pbhtauBasic:
    {
        int i;
        char prefix[] = "Proxy-Authorization: Basic ";
        char response[128];
        pubnub_bymebl_t data = { (uint8_t*)response, 0 };

        PUBNUB_ASSERT_OPT(n > sizeof prefix);
        memcpy(header, prefix, sizeof prefix);
        n -= sizeof prefix;
        data.size = snprintf(response, sizeof response, "%s:%s", figure_out_username(p), figure_out_password(p));

        PUBNUB_LOG_TRACE("pbproxy_http_header_to_send(): Basic header (before Base64): '%s'\n", response);

        i = pbbase64_encode_std(data, header + sizeof prefix - 1, &n);
        if (0 == i) {
            PUBNUB_LOG_TRACE("pbproxy_http_header_to_send(): Basic header (after Base64): '%s'\n", header);
            p->proxy_authorization_sent = true;
        }
        else {
            PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): Basic Failed Base64 encoding of header\n");
        }
        return i;
    }
    case pbhtauNTLM:
    {
        char prefix[] = "Proxy-Authorization: NTLM ";
        uint8_t response[PUBNUB_NTLM_MAX_TOKEN];
        pubnub_bymebl_t data = { response, sizeof response };
        int i = pbntlm_core_prep_msg_to_send(p, &data);

        if (i != 0) {
            PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): NTLM failed preparing message to send'\n");
            return -1;
        }
        if (0 == data.size) {
            return -1;
        }

        PUBNUB_ASSERT_OPT(n > sizeof prefix);
        memcpy(header, prefix, sizeof prefix);
        n -= sizeof prefix;

        i = pbbase64_encode_std(data, header + sizeof prefix - 1, &n);
        if (0 == i) {
            PUBNUB_LOG_TRACE("pbproxy_http_header_to_send(): NTLM header (after Base64): '%s'\n", header);
        }
        else {
            PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): NTLM Failed Base64 encoding of header\n");
        }

        return i;
    }
    case pbhtauNone:
        return -1;
    default:
        PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): Proxy auth scheme %d not supported\n", p->proxy_auth_scheme);
        return -1;
    }
}