// Authenticate connection and request the info of a particular sockaddr_in. // Return 0 on success. int citrusleaf_info_host_auth(as_cluster* cluster, struct sockaddr_in *sa_in, char *names, char **values, int timeout_ms, bool send_asis, bool check_bounds) { int fd = cf_socket_create_and_connect_nb(sa_in); if (fd == -1) { return CITRUSLEAF_FAIL_UNAVAILABLE; } if (cluster->user) { int status = as_authenticate(fd, cluster->user, cluster->password, timeout_ms); if (status) { cf_error("Authentication failed for %s", cluster->user); cf_close(fd); return status; } } int rv = citrusleaf_info_host_limit(fd, names, values, timeout_ms, send_asis, 0, check_bounds); shutdown(fd, SHUT_RDWR); cf_close(fd); if (rv == 0 && strncmp(*values, "security error", 14) == 0) { cf_error("%s", *values); free(*values); return CITRUSLEAF_NOT_AUTHENTICATED; } return rv; }
// Request the info of a particular sockaddr_in. // Used internally for host-crawling as well as supporting the external interface. // Return 0 on success and -1 on error. int citrusleaf_info_host(struct sockaddr_in *sa_in, char *names, char **values, int timeout_ms, bool send_asis, bool check_bounds) { // Actually doing a non-blocking connect int fd = cf_socket_create_and_connect_nb(sa_in); if (fd == -1) { return -1; } int rv = citrusleaf_info_host_limit(fd, names, values, timeout_ms, send_asis, 0, check_bounds); shutdown(fd, SHUT_RDWR); cf_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(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); }