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; }
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; }
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; }
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; }
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; }
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; }
/** 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; }
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); }
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; } }
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; }
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; }
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; } }
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; }
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; }
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; }
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; } }
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; }
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; } }