static int is_connected(int fd) { uint8_t buf[8]; ssize_t rv = recv(fd, (void*)buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT | MSG_NOSIGNAL); if (rv == 0) { cf_debug("Connected check: Found disconnected fd %d", fd); return CONNECTED_NOT; } if (rv < 0) { if (errno == EBADF) { cf_warn("Connected check: Bad fd %d", fd); return CONNECTED_BADFD; } else if (errno == EWOULDBLOCK || errno == EAGAIN) { // The normal case. return CONNECTED; } else { cf_info("Connected check: fd %d error %d", fd, errno); return CONNECTED_ERROR; } } cf_info("Connected check: Peek got unexpected data for fd %d", fd); return CONNECTED; }
static bool as_node_verify_name(as_node* node, const char* name) { if (name == 0 || *name == 0) { cf_warn("Node name not returned from info request."); return false; } if (strcmp(node->name, name) != 0) { // Set node to inactive immediately. cf_warn("Node name has changed. Old=%s New=%s", node->name, name); // Make volatile write so changes are reflected in other threads. ck_pr_store_8(&node->active, false); return false; } return true; }
bool as_lookup(as_cluster* cluster, char* hostname, uint16_t port, bool enable_warning, as_vector* /*<struct sockaddr_in>*/ addresses) { // Check if there is an alternate address that should be used for this hostname. if (cluster && cluster->ip_map) { as_addr_map* entry = cluster->ip_map; for (uint32_t i = 0; i < cluster->ip_map_size; i++) { if (strcmp(entry->orig, hostname) == 0) { // Found mapping for this address. Use alternate. cf_debug("Using %s instead of %s", entry->alt, hostname); hostname = entry->alt; break; } entry++; } } // Lookup TCP addresses. struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; struct addrinfo* results = 0; int ret = getaddrinfo(hostname, 0, &hints, &results); if (ret) { if (enable_warning) { cf_warn("Invalid hostname %s: %s", hostname, gai_strerror(ret)); } return false; } // Add addresses to vector if it exists. if (addresses) { uint16_t port_be = cf_swap_to_be16(port); for (struct addrinfo* r = results; r; r = r->ai_next) { struct sockaddr_in* addr = (struct sockaddr_in*)r->ai_addr; addr->sin_port = port_be; as_vector_append_unique(addresses, addr); } } freeaddrinfo(results); return true; }
int as_node_get_connection(as_node* node, int* fd) { //cf_queue* q = asyncfd ? node->conn_q_asyncfd : node->conn_q; cf_queue* q = node->conn_q; while (1) { int rv = cf_queue_pop(q, fd, CF_QUEUE_NOWAIT); if (rv == CF_QUEUE_OK) { int rv2 = is_connected(*fd); switch (rv2) { case CONNECTED: // It's still good. return 0; case CONNECTED_BADFD: // Local problem, don't try closing. cf_warn("Found bad file descriptor in queue: fd %d", *fd); break; case CONNECTED_NOT: // Can't use it - the remote end closed it. case CONNECTED_ERROR: // Some other problem, could have to do with remote end. default: cf_close(*fd); break; } } else if (rv == CF_QUEUE_EMPTY) { // We exhausted the queue. Try creating a fresh socket. return as_node_create_connection(node, fd); } else { cf_error("Bad return value from cf_queue_pop"); *fd = -1; return CITRUSLEAF_FAIL_CLIENT; } } }
// Request the info of a particular sockaddr_in. // Reject info request if response length is greater than max_response_length. // Return 0 on success and -1 on error. int citrusleaf_info_host_limit(struct sockaddr_in *sa_in, char *names, char **values, int timeout_ms, bool send_asis, uint64_t max_response_length) { int rv = -1; int io_rv; *values = 0; // Deal with the incoming 'names' parameter // Translate interior ';' in the passed-in names to \n uint32_t slen = 0; if (names) { if (send_asis) { slen = strlen(names); } else { char *_t = names; while (*_t) { slen++; if ((*_t == ';') || (*_t == ':') || (*_t == ',')) *_t = '\n'; _t++; } } } // Sometimes people forget/cant add the trailing '\n'. Be nice and add it for them. // using a stack allocated variable so we dn't have to clean up, Pain in the ass syntactically // but a nice little thing if (names) { if (names[slen-1] == '\n') { slen = 0; } else { slen++; if (slen > 1024) { return(-1); } } } char names_with_term[slen+1]; if (slen) { strcpy(names_with_term, names); names_with_term[slen-1] = '\n'; names_with_term[slen] = 0; names = names_with_term; } // Actually doing a non-blocking connect int fd = cf_socket_create_and_connect_nb(sa_in); if (fd == -1) { return -1; } cl_proto *req; uint8_t buf[1024]; uint buf_sz; // Un-initialized buf can lead to junk lastshiptimes values. // Initialize buf to 0. bzero(buf, 1024); if (names) { uint sz = strlen(names); buf_sz = sz + sizeof(cl_proto); if (buf_sz < 1024) req = (cl_proto *) buf; else req = (cl_proto *) malloc(buf_sz); if (req == NULL) goto Done; req->sz = sz; memcpy(req->data,names,sz); } else { req = (cl_proto *) buf; req->sz = 0; buf_sz = sizeof(cl_proto); names = ""; } req->version = CL_PROTO_VERSION; req->type = CL_PROTO_TYPE_INFO; cl_proto_swap(req); if (timeout_ms) io_rv = cf_socket_write_timeout(fd, (uint8_t *) req, buf_sz, 0, timeout_ms); else io_rv = cf_socket_write_forever(fd, (uint8_t *) req, buf_sz); if ((uint8_t *)req != buf) free(req); if (io_rv != 0) { #ifdef DEBUG cf_debug("info returned error, rv %d errno %d bufsz %d", io_rv, errno, buf_sz); #endif goto Done; } cl_proto *rsp = (cl_proto *)buf; if (timeout_ms) io_rv = cf_socket_read_timeout(fd, buf, 8, 0, timeout_ms); else io_rv = cf_socket_read_forever(fd, buf, 8); if (0 != io_rv) { #ifdef DEBUG cf_debug("info socket read failed: rv %d errno %d", io_rv, errno); #endif goto Done; } cl_proto_swap(rsp); if (rsp->sz) { size_t read_length = rsp->sz; bool limit_reached = false; if (max_response_length > 0 && rsp->sz > max_response_length) { // Response buffer is too big. Read a few bytes just to see what the buffer contains. read_length = 100; limit_reached = true; } uint8_t *v_buf = malloc(read_length + 1); if (!v_buf) { cf_warn("Info request '%s' failed. Failed to malloc %d bytes", names, read_length); goto Done; } if (timeout_ms) io_rv = cf_socket_read_timeout(fd, v_buf, read_length, 0, timeout_ms); else io_rv = cf_socket_read_forever(fd, v_buf, read_length); if (io_rv != 0) { free(v_buf); if (io_rv != ETIMEDOUT) { cf_warn("Info request '%s' failed. Failed to read %d bytes. Return code %d", names, read_length, io_rv); } goto Done; } v_buf[read_length] = 0; if (limit_reached) { // Response buffer is too big. Log warning and reject. cf_warn("Info request '%s' failed. Response buffer length %lu is excessive. Buffer: %s", names, rsp->sz, v_buf); goto Done; } *values = (char *) v_buf; } else { *values = 0; } rv = 0; Done: shutdown(fd, SHUT_RDWR); close(fd); return(rv); }
// Request the info of a particular sockaddr_in. // Reject info request if response length is greater than max_response_length. // Return 0 on success and -1 on error. int citrusleaf_info_host_limit(int fd, char *names, char **values, int timeout_ms, bool send_asis, uint64_t max_response_length, bool check_bounds) { uint bb_size = 2048; int rv = -1; int io_rv; *values = 0; // Deal with the incoming 'names' parameter // Translate interior ';' in the passed-in names to \n uint32_t slen = 0; if (names) { if (send_asis) { slen = (uint32_t)strlen(names); } else { char *_t = names; while (*_t) { slen++; if ((*_t == ';') || (*_t == ':') || (*_t == ',')) *_t = '\n'; _t++; } } } // Sometimes people forget/cant add the trailing '\n'. Be nice and add it for them. // using a stack allocated variable so we dn't have to clean up, Pain in the ass syntactically // but a nice little thing if (names) { if (names[slen-1] == '\n') { slen = 0; } else { slen++; // If check bounds is true, do not allow beyond a certain limit if (check_bounds && (slen > bb_size)) { return(-1); } } } char names_with_term[slen+1]; if (slen) { strcpy(names_with_term, names); names_with_term[slen-1] = '\n'; names_with_term[slen] = 0; names = names_with_term; } cl_proto *req; uint8_t buf[bb_size]; uint buf_sz; bool rmalloced = false; if (names) { uint sz = (uint)strlen(names); buf_sz = sz + sizeof(cl_proto); if (buf_sz < bb_size) req = (cl_proto *) buf; else { req = (cl_proto *) malloc(buf_sz); rmalloced = true; } if (req == NULL) goto Done; req->sz = sz; memcpy((void*)req + sizeof(cl_proto), names, sz); } else { req = (cl_proto *) buf; req->sz = 0; buf_sz = sizeof(cl_proto); names = ""; } req->version = CL_PROTO_VERSION; req->type = CL_PROTO_TYPE_INFO; cl_proto_swap_to_be(req); if (timeout_ms) io_rv = cf_socket_write_timeout(fd, (uint8_t *) req, buf_sz, 0, timeout_ms); else io_rv = cf_socket_write_forever(fd, (uint8_t *) req, buf_sz); if (rmalloced) { free (req); } if (io_rv != 0) { #ifdef DEBUG cf_debug("info returned error, rv %d errno %d bufsz %d", io_rv, errno, buf_sz); #endif goto Done; } cl_proto *rsp = (cl_proto *)buf; if (timeout_ms) io_rv = cf_socket_read_timeout(fd, buf, 8, 0, timeout_ms); else io_rv = cf_socket_read_forever(fd, buf, 8); if (0 != io_rv) { #ifdef DEBUG cf_debug("info socket read failed: rv %d errno %d", io_rv, errno); #endif goto Done; } cl_proto_swap_from_be(rsp); if (rsp->sz) { size_t read_length = rsp->sz; bool limit_reached = false; if (max_response_length > 0 && rsp->sz > max_response_length) { // Response buffer is too big. Read a few bytes just to see what the buffer contains. read_length = 100; limit_reached = true; } uint8_t *v_buf = malloc(read_length + 1); if (!v_buf) { cf_warn("Info request '%s' failed. Failed to malloc %d bytes", names, read_length); goto Done; } if (timeout_ms) io_rv = cf_socket_read_timeout(fd, v_buf, read_length, 0, timeout_ms); else io_rv = cf_socket_read_forever(fd, v_buf, read_length); if (io_rv != 0) { free(v_buf); if (io_rv != ETIMEDOUT) { cf_warn("Info request '%s' failed. Failed to read %d bytes. Return code %d", names, read_length, io_rv); } goto Done; } v_buf[read_length] = 0; if (limit_reached) { // Response buffer is too big. Log warning and reject. cf_warn("Info request '%s' failed. Response buffer length %lu is excessive. Buffer: %s", names, rsp->sz, v_buf); goto Done; } *values = (char *) v_buf; } else { cf_warn("rsp size is 0"); *values = 0; } rv = 0; Done: return(rv); }