// 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); }
static uint8_t* as_node_get_info(as_node* node, const char* names, size_t names_len, int timeout_ms, uint8_t* stack_buf) { int fd = node->info_fd; // Prepare the write request buffer. size_t write_size = sizeof(cl_proto) + names_len; cl_proto* proto = (cl_proto*)stack_buf; proto->sz = names_len; proto->version = CL_PROTO_VERSION; proto->type = CL_PROTO_TYPE_INFO; cl_proto_swap_to_be(proto); memcpy((void*)(stack_buf + sizeof(cl_proto)), (const void*)names, names_len); // Write the request. Note that timeout_ms is never 0. if (cf_socket_write_timeout(fd, stack_buf, write_size, 0, timeout_ms) != 0) { cf_debug("Node %s failed info socket write", node->name); return 0; } // Reuse the buffer, read the response - first 8 bytes contains body size. if (cf_socket_read_timeout(fd, stack_buf, sizeof(cl_proto), 0, timeout_ms) != 0) { cf_debug("Node %s failed info socket read header", node->name); return 0; } proto = (cl_proto*)stack_buf; cl_proto_swap_from_be(proto); // Sanity check body size. if (proto->sz == 0 || proto->sz > 512 * 1024) { cf_info("Node %s bad info response size %lu", node->name, proto->sz); return 0; } // Allocate a buffer if the response is bigger than the stack buffer - // caller must free it if this call succeeds. Note that proto is overwritten // if stack_buf is used, so we save the sz field here. size_t proto_sz = proto->sz; uint8_t* rbuf = proto_sz >= INFO_STACK_BUF_SIZE ? (uint8_t*)cf_malloc(proto_sz + 1) : stack_buf; if (! rbuf) { cf_error("Node %s failed allocation for info response", node->name); return 0; } // Read the response body. if (cf_socket_read_timeout(fd, rbuf, proto_sz, 0, timeout_ms) != 0) { cf_debug("Node %s failed info socket read body", node->name); if (rbuf != stack_buf) { cf_free(rbuf); } return 0; } // Null-terminate the response body and return it. rbuf[proto_sz] = 0; return rbuf; }