enum pbpal_resolv_n_connect_result pbpal_check_resolv_and_connect(pubnub_t *pb) { if (PUBNUB_USE_ADNS) { uint8_t const* const origin = (uint8_t*)(PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN); struct sockaddr_in dns_server; struct sockaddr_in dest; int skt = pb->pal.socket; dns_server.sin_family = AF_INET; dns_server.sin_port = htons(DNS_PORT); inet_pton(AF_INET, "8.8.8.8", &dns_server.sin_addr.s_addr); switch (read_response(skt, (struct sockaddr*)&dns_server, origin, &dest)) { case -1: return pbpal_resolv_failed_rcv; case +1: return pbpal_resolv_rcv_wouldblock; case 0: break; } socket_close(skt); skt = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (SOCKET_INVALID == skt) { return pbpal_connect_resource_failure; } pb->pal.socket = skt; dest.sin_port = htons(HTTP_PORT); if (SOCKET_ERROR == connect(skt, (struct sockaddr*)&dest, sizeof dest)) { return socket_would_block() ? pbpal_connect_wouldblock : pbpal_connect_failed; } return pbpal_connect_success; } /* Under regular BSD-ish sockets, this function should not be called unless using async DNS, so this is an error */ return pbpal_connect_failed; }
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 read_dns_response(int skt, struct sockaddr* dest, struct sockaddr_in* resolved_addr) { uint8_t buf[8192]; int msg_size; unsigned addr_size = sizeof *dest; msg_size = recvfrom(skt, (char*)buf, sizeof buf, 0, dest, CAST & addr_size); if (msg_size <= 0) { return socket_would_block() ? +1 : -1; } resolved_addr->sin_family = AF_INET; return pubnub_pick_resolved_address(buf, (size_t)msg_size, (struct pubnub_ipv4_address*)&(resolved_addr->sin_addr.s_addr)); }
static enum pbpal_resolv_n_connect_result connect_TCP_socket(pubnub_t *pb, struct sockaddr_in dest, const uint16_t port) { pb->pal.socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (SOCKET_INVALID == pb->pal.socket) { return pbpal_connect_resource_failure; } pb->options.use_blocking_io = false; pbpal_set_blocking_io(pb); socket_disable_SIGPIPE(pb->pal.socket); dest.sin_port = htons(port); if (SOCKET_ERROR == connect(pb->pal.socket, (struct sockaddr*)&dest, sizeof dest)) { return socket_would_block() ? pbpal_connect_wouldblock : pbpal_connect_failed; } return pbpal_connect_success; }
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; }
int send_dns_query(int skt, struct sockaddr const* dest, char const* host) { uint8_t buf[4096]; int to_send; int sent_to; if (-1 == pubnub_prepare_dns_request(buf, sizeof buf, host, &to_send)) { PUBNUB_LOG_ERROR("Couldn't prepare dns request! : #prepared bytes=%d\n", to_send); return -1; } TRACE_SOCKADDR("Sending DNS query to: ", dest); sent_to = sendto(skt, (char*)buf, to_send, 0, dest, sizeof *dest); if (sent_to <= 0) { return socket_would_block() ? +1 : -1; } else if (to_send != sent_to) { PUBNUB_LOG_ERROR("sendto() sent %d out of %d bytes!\n", sent_to, to_send); return -1; } return 0; }
int send_dns_query(int skt, struct sockaddr const *dest, unsigned char *host) { uint8_t buf[8192]; struct DNS_HEADER *dns = (struct DNS_HEADER *)buf; uint8_t *qname = buf + sizeof *dns; struct QUESTION *qinfo; dns->id = htons(33); /* in lack of a better ID */ dns->options = htons(dnsoptRDmask); dns->q_count = htons(1); dns->ans_count = dns->auth_count = dns->add_count = 0; dns_qname_encode(qname, sizeof buf - sizeof *dns, host); qinfo =(struct QUESTION*)(buf + sizeof *dns + strlen((const char*)qname) + 1); qinfo->qtype = htons(dnsA); qinfo->qclass = htons(dnsqclassInternet); if (sendto(skt, buf, sizeof *dns + strlen((char*)qname)+1 + sizeof *qinfo, 0, dest, sizeof *dest) < 0) { return socket_would_block() ? +1 : -1; } return 0; }
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 */ }
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; } }