int pbpal_ntf_poll_away(struct pbpal_poll_data* data, int ms) { int i; int rslt; fd_set readfds; fd_set writefds; fd_set exceptfds; struct timeval timeout; if (0 == data->size) { return 0; } timeout.tv_sec = ms / 1000; timeout.tv_usec = (ms % 1000) * 1000; memcpy(&readfds, &data->readfds, sizeof readfds); memcpy(&writefds, &data->writefds, sizeof writefds); memcpy(&exceptfds, &data->exceptfds, sizeof exceptfds); rslt = select(data->nfds + 1, &readfds, &writefds, &exceptfds, &timeout); if (SOCKET_ERROR == rslt) { int last_err = #if defined(_WIN32) WSAGetLastError() #else errno #endif ; /* error? what to do about it? */ PUBNUB_LOG_WARNING( "poll size = %u, error = %d\n", (unsigned)data->size, last_err); return -1; } for (i = 0; (i < (int)data->size) && (rslt > 0); ++i) { bool should_process = false; if (FD_ISSET(data->asocket[i], &readfds)) { should_process = true; --rslt; } if (FD_ISSET(data->asocket[i], &writefds)) { should_process = true; --rslt; PUBNUB_ASSERT_OPT(rslt >= 0); } if (FD_ISSET(data->asocket[i], &exceptfds)) { should_process = true; --rslt; PUBNUB_ASSERT_OPT(rslt >= 0); } if (should_process) { pbntf_requeue_for_processing(data->apb[i]); } } PUBNUB_ASSERT_OPT(0 == rslt); return rslt; }
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; }
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 enum pubnub_res parse_pubnub_result(struct pubnub_ *pb) { enum pubnub_res pbres = PNR_OK; 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; } return pbres; }
int pbpal_send_status(pubnub_t *pb) { int r; if (0 == pb->sendlen) { return 0; } r = socket_send(pb->pal.socket, (char*)pb->sendptr, pb->sendlen, 0); if (r < 0) { return socket_would_block() ? +1 : -1; } if (r > pb->sendlen) { PUBNUB_LOG_WARNING("That's some over-achieving socket!\n"); r = pb->sendlen; } pb->sendptr += r; pb->sendlen -= r; return (0 == pb->sendlen) ? 0 : +1; }
static void save_allocated(pubnub_t* pb) { #if defined PUBNUB_ASSERT_LEVEL_EX pubnub_mutex_init_static(m_lock); pubnub_mutex_lock(m_lock); if (m_n == m_cap) { pubnub_t** npalloc = (pubnub_t**)realloc(m_allocated, sizeof m_allocated[0] * (m_n + 1)); if (NULL == npalloc) { PUBNUB_LOG_WARNING("Couldn't allocate memory for pubnub_alloc_std bookkeeping"); pubnub_mutex_unlock(m_lock); return; } m_allocated = npalloc; m_allocated[m_n++] = pb; m_cap = m_n; } else { m_allocated[m_n++] = pb; } pubnub_mutex_unlock(m_lock); #endif }
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) { int error; uint16_t port = HTTP_PORT; char const* origin; #ifdef PUBNUB_CALLBACK_API struct sockaddr_in dest; prepare_port_and_hostname(pb, &port, &origin); #if PUBNUB_PROXY_API if (0 != pb->proxy_ip_address.ipv4[0]) { struct pubnub_ipv4_address* p_dest_addr = (struct pubnub_ipv4_address*)&(dest.sin_addr.s_addr); memset(&dest, '\0', sizeof dest); memcpy(p_dest_addr->ipv4, pb->proxy_ip_address.ipv4, sizeof p_dest_addr->ipv4); dest.sin_family = AF_INET; return connect_TCP_socket(pb, dest, port); } #endif if (SOCKET_INVALID == pb->pal.socket) { pb->pal.socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); } if (SOCKET_INVALID == pb->pal.socket) { return pbpal_resolv_resource_failure; } pb->options.use_blocking_io = false; pbpal_set_blocking_io(pb); dest.sin_family = AF_INET; dest.sin_port = htons(DNS_PORT); get_dns_ip(&dest); error = send_dns_query(pb->pal.socket, (struct sockaddr*)&dest, origin); if (error < 0) { return pbpal_resolv_failed_send; } else if (error > 0) { return pbpal_resolv_send_wouldblock; } return pbpal_resolv_sent; #else char port_string[20]; struct addrinfo *result; struct addrinfo *it; struct addrinfo hint; hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; hint.ai_protocol = hint.ai_flags = hint.ai_addrlen = 0; hint.ai_addr = NULL; hint.ai_canonname = NULL; hint.ai_next = NULL; prepare_port_and_hostname(pb, &port, &origin); snprintf(port_string, sizeof port_string, "%hu", port); error = getaddrinfo(origin, port_string, &hint, &result); if (error != 0) { return pbpal_resolv_failed_processing; } for (it = result; it != NULL; it = it->ai_next) { pb->pal.socket = socket(it->ai_family, it->ai_socktype, it->ai_protocol); if (pb->pal.socket == SOCKET_INVALID) { continue; } pbpal_set_blocking_io(pb); if (connect(pb->pal.socket, it->ai_addr, it->ai_addrlen) == SOCKET_ERROR) { if (socket_would_block()) { error = 1; break; } else { PUBNUB_LOG_WARNING("socket connect() failed, will try another IP address, if available\n"); socket_close(pb->pal.socket); pb->pal.socket = SOCKET_INVALID; continue; } } break; } freeaddrinfo(result); if (NULL == it) { return pbpal_connect_failed; } socket_set_rcv_timeout(pb->pal.socket, pb->transaction_timeout_ms); socket_disable_SIGPIPE(pb->pal.socket); return error ? pbpal_connect_wouldblock : pbpal_connect_success; #endif /* PUBNUB_CALLBACK_API */ }
void socket_watcher_thread(void *arg) { FILETIME prev_time; GetSystemTimeAsFileTime(&prev_time); for (;;) { const DWORD ms = 100; EnterCriticalSection(&m_watcher.queue_lock); if (m_watcher.queue_head != m_watcher.queue_tail) { pubnub_t *pbp = m_watcher.queue_apb[m_watcher.queue_tail++]; LeaveCriticalSection(&m_watcher.queue_lock); if (pbp != NULL) { pubnub_mutex_lock(pbp->monitor); pbnc_fsm(pbp); pubnub_mutex_unlock(pbp->monitor); } EnterCriticalSection(&m_watcher.queue_lock); if (m_watcher.queue_tail == m_watcher.queue_size) { m_watcher.queue_tail = 0; } } LeaveCriticalSection(&m_watcher.queue_lock); EnterCriticalSection(&m_watcher.mutw); if (0 == m_watcher.apoll_size) { LeaveCriticalSection(&m_watcher.mutw); continue; } { int rslt = WSAPoll(m_watcher.apoll, m_watcher.apoll_size, ms); if (SOCKET_ERROR == rslt) { /* error? what to do about it? */ PUBNUB_LOG_WARNING("poll size = %d, error = %d\n", m_watcher.apoll_size, WSAGetLastError()); } else if (rslt > 0) { size_t i; size_t apoll_size = m_watcher.apoll_size; for (i = 0; i < apoll_size; ++i) { if (m_watcher.apoll[i].revents & (POLLIN | POLLOUT)) { pubnub_t *pbp = m_watcher.apb[i]; pubnub_mutex_lock(pbp->monitor); pbnc_fsm(pbp); if (apoll_size == m_watcher.apoll_size) { if (m_watcher.apoll[i].events == POLLOUT) { if ((pbp->state == PBS_WAIT_DNS_RCV) || (pbp->state >= PBS_RX_HTTP_VER)) { m_watcher.apoll[i].events = POLLIN; } } else { if ((pbp->state > PBS_WAIT_DNS_RCV) && (pbp->state < PBS_RX_HTTP_VER)) { m_watcher.apoll[i].events = POLLOUT; } } } else { PUBNUB_ASSERT_OPT(apoll_size == m_watcher.apoll_size + 1); apoll_size = m_watcher.apoll_size; --i; } pubnub_mutex_unlock(pbp->monitor); } } } } if (PUBNUB_TIMERS_API) { FILETIME current_time; int elapsed; GetSystemTimeAsFileTime(¤t_time); elapsed = elapsed_ms(prev_time, current_time); if (elapsed > 0) { pubnub_t *expired = pubnub_timer_list_as_time_goes_by(&m_watcher.timer_head, elapsed); while (expired != NULL) { pubnub_t *next = expired->next; pubnub_mutex_lock(expired->monitor); pbnc_stop(expired, PNR_TIMEOUT); pubnub_mutex_unlock(expired->monitor); expired->next = NULL; expired->previous = NULL; expired = next; } prev_time = current_time; } } LeaveCriticalSection(&m_watcher.mutw); } }
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; }
enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb) { int error; 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_DNS_SEND) || (pb->state == PBS_WAIT_DNS_RCV)); if (PUBNUB_USE_ADNS) { struct sockaddr_in dest; int skt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (SOCKET_INVALID == skt) { return pbpal_resolv_resource_failure; } pb->options.use_blocking_io = false; pbpal_set_blocking_io(pb); dest.sin_family = AF_INET; dest.sin_port = htons(DNS_PORT); inet_pton(AF_INET, "8.8.8.8", &dest.sin_addr.s_addr); error = send_dns_query(skt, (struct sockaddr*)&dest, (unsigned char*)origin); if (error < 0) { socket_close(skt); return pbpal_resolv_failed_send; } else if (error > 0) { return pbpal_resolv_send_wouldblock; } pb->pal.socket = skt; return pbpal_resolv_sent; } else { struct addrinfo *result; struct addrinfo *it; struct addrinfo hint; hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; hint.ai_protocol = hint.ai_flags = hint.ai_addrlen = 0; hint.ai_addr = NULL; hint.ai_canonname = NULL; hint.ai_next = NULL; error = getaddrinfo(origin, HTTP_PORT_STRING, &hint, &result); if (error != 0) { return pbpal_resolv_failed_processing; } for (it = result; it != NULL; it = it->ai_next) { pb->pal.socket = socket(it->ai_family, it->ai_socktype, it->ai_protocol); if (pb->pal.socket == SOCKET_INVALID) { continue; } pbpal_set_blocking_io(pb); if (connect(pb->pal.socket, it->ai_addr, it->ai_addrlen) == SOCKET_ERROR) { if (socket_would_block()) { error = 1; break; } else { PUBNUB_LOG_WARNING("socket connect() failed, will try another IP address, if available\n"); socket_close(pb->pal.socket); pb->pal.socket = SOCKET_INVALID; continue; } } break; } freeaddrinfo(result); if (NULL == it) { return pbpal_connect_failed; } socket_set_rcv_timeout(pb->pal.socket, pb->transaction_timeout_ms); return error ? pbpal_connect_wouldblock : pbpal_connect_success; } }