as_status as_event_command_execute(as_event_command* cmd, as_error* err) { ck_pr_inc_32(&cmd->cluster->async_pending); // Only do this after the above increment to avoid a race with as_cluster_destroy(). if (!cmd->cluster->valid) { as_event_command_free(cmd); return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Client shutting down"); } // Use pointer comparison for performance. // If portability becomes an issue, use "pthread_equal(event_loop->thread, pthread_self())" // instead. if (cmd->event_loop->thread == pthread_self()) { // We are already in event loop thread, so start processing. as_event_command_begin(cmd); } else { if (cmd->timeout_ms) { // Store current time in first 8 bytes which is not used yet. *(uint64_t*)cmd = cf_getms(); } // Send command through queue so it can be executed in event loop thread. if (! as_event_send(cmd)) { as_event_command_free(cmd); return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to queue command"); } } return AEROSPIKE_OK; }
int as_event_create_socket(as_event_command* cmd) { // Create a non-blocking socket. int fd = as_socket_create_nb(); if (fd < 0) { as_error err; as_error_set_message(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to create non-blocking socket"); as_event_connect_error(cmd, &err, fd); return -1; } if (cmd->pipe_listener != NULL) { if (as_event_send_buffer_size) { if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &as_event_send_buffer_size, sizeof(as_event_send_buffer_size)) < 0) { as_error err; as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to configure pipeline send buffer. size %d error %d (%s)", as_event_send_buffer_size, errno, strerror(errno)); as_event_connect_error(cmd, &err, fd); return -1; } } if (as_event_recv_buffer_size) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &as_event_recv_buffer_size, sizeof(as_event_recv_buffer_size)) < 0) { as_error err; as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to configure pipeline receive buffer. size %d error %d (%s)", as_event_recv_buffer_size, errno, strerror(errno)); as_event_connect_error(cmd, &err, fd); return -1; } } #if defined(__linux__) if (as_event_recv_buffer_size) { if (setsockopt(fd, SOL_TCP, TCP_WINDOW_CLAMP, &as_event_recv_buffer_size, sizeof(as_event_recv_buffer_size)) < 0) { as_error err; as_error_set_message(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to configure pipeline TCP window."); as_event_connect_error(cmd, &err, fd); return -1; } } #endif int arg = 0; if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &arg, sizeof(arg)) < 0) { as_error err; as_error_set_message(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to configure pipeline Nagle algorithm."); as_event_connect_error(cmd, &err, fd); return -1; } } return fd; }
as_status as_command_parse_success_failure_bins(uint8_t** pp, as_error* err, as_msg* msg, as_val** value) { uint8_t* p = *pp; p = as_command_ignore_fields(p, msg->n_fields); as_bin_name name; for (uint32_t i = 0; i < msg->n_ops; i++) { uint32_t op_size = cf_swap_from_be32(*(uint32_t*)p); p += 5; uint8_t type = *p; p += 2; uint8_t name_size = *p++; uint8_t name_len = (name_size <= AS_BIN_NAME_MAX_LEN)? name_size : AS_BIN_NAME_MAX_LEN; memcpy(name, p, name_len); name[name_len] = 0; p += name_size; uint32_t value_size = (op_size - (name_size + 4)); if (strcmp(name, "SUCCESS") == 0) { if (value) { as_command_parse_value(p, type, value_size, value); } *pp = p + value_size; return AEROSPIKE_OK; } if (strcmp(name, "FAILURE") == 0) { as_val* val = 0; as_command_parse_value(p, type, value_size, &val); if (val == 0) { as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Received null FAILURE bin."); } else if (val->type == AS_STRING) { as_error_set_message(err, AEROSPIKE_ERR_CLIENT, ((as_string*)val)->value); } else { as_error_update(err, AEROSPIKE_ERR_CLIENT, "Expected string for FAILURE bin. Received %d", val->type); } as_val_destroy(val); return err->code; } p += value_size; } return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to find SUCCESS or FAILURE bin."); }
static as_status as_admin_execute(aerospike* as, as_error* err, const as_policy_admin* policy, uint8_t* buffer, uint8_t* end) { uint32_t timeout_ms = (policy)? policy->timeout : as->config.policies.admin.timeout; if (timeout_ms <= 0) { timeout_ms = DEFAULT_TIMEOUT; } uint64_t deadline_ms = as_socket_deadline(timeout_ms); as_cluster* cluster = as->cluster; as_node* node = as_node_get_random(cluster); if (! node) { return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to find server node."); } int fd; as_status status = as_node_get_connection(err, node, deadline_ms, &fd); if (status) { as_node_release(node); return status; } status = as_admin_send(err, fd, buffer, end, deadline_ms); if (status) { as_node_close_connection(node, fd); as_node_release(node); return status; } status = as_socket_read_deadline(err, fd, buffer, HEADER_SIZE, deadline_ms); if (status) { as_node_close_connection(node, fd); as_node_release(node); return status; } as_node_put_connection(node, fd); as_node_release(node); status = buffer[RESULT_CODE]; if (status) { return as_error_set_message(err, status, as_error_string(status)); } return status; }
static as_status as_command_parse_udf_failure(uint8_t* p, as_error* err, as_msg* msg, as_status status) { p = as_command_ignore_fields(p, msg->n_fields); as_bin_name name; for (uint32_t i = 0; i < msg->n_ops; i++) { uint32_t op_size = cf_swap_from_be32(*(uint32_t*)p); p += 5; uint8_t type = *p; p += 2; uint8_t name_size = *p++; uint8_t name_len = (name_size <= AS_BIN_NAME_MAX_LEN)? name_size : AS_BIN_NAME_MAX_LEN; memcpy(name, p, name_len); name[name_len] = 0; p += name_size; uint32_t value_size = (op_size - (name_size + 4)); if (strcmp(name, "FAILURE") == 0) { as_val* val = 0; as_command_parse_value(p, type, value_size, &val); status = as_command_parse_udf_error(err, status, val); as_val_destroy(val); return status; } p += value_size; } return as_error_set_message(err, status, as_error_string(status)); }
as_status aerospike_scan_async( aerospike* as, as_error* err, const as_policy_scan* policy, const as_scan* scan, uint64_t* scan_id, as_async_scan_listener listener, void* udata, as_event_loop* event_loop ) { as_error_reset(err); as_nodes* nodes = as_nodes_reserve(as->cluster); uint32_t n_nodes = nodes->size; if (n_nodes == 0) { as_nodes_release(nodes); return as_error_set_message(err, AEROSPIKE_ERR_SERVER, "Scan command failed because cluster is empty."); } // Reserve each node in cluster. for (uint32_t i = 0; i < n_nodes; i++) { as_node_reserve(nodes->array[i]); } as_status status = as_scan_async(as, err, policy, scan, scan_id, listener, udata, event_loop, nodes->array, n_nodes); as_nodes_release(nodes); return status; }
static as_status as_scan_parse_record_async(as_event_command* cmd, uint8_t** pp, as_msg* msg, as_error* err) { as_record rec; as_record_inita(&rec, msg->n_ops); rec.gen = msg->generation; rec.ttl = cf_server_void_time_to_ttl(msg->record_ttl); *pp = as_command_parse_key(*pp, msg->n_fields, &rec.key); as_status status = as_command_parse_bins(pp, err, &rec, msg->n_ops, cmd->flags2 & AS_ASYNC_FLAGS2_DESERIALIZE); if (status != AEROSPIKE_OK) { as_record_destroy(&rec); return status; } as_event_executor* executor = cmd->udata; // udata is overloaded to contain executor. bool rv = ((as_async_scan_executor*)executor)->listener(0, &rec, executor->udata, executor->event_loop); as_record_destroy(&rec); if (! rv) { executor->notify = false; return as_error_set_message(err, AEROSPIKE_ERR_CLIENT_ABORT, ""); } return AEROSPIKE_OK; }
as_status as_authenticate(as_error* err, int fd, const char* user, const char* credential, uint64_t deadline_ms) { uint8_t buffer[AS_STACK_BUF_SIZE]; uint8_t* p = buffer + 8; p = as_admin_write_header(p, AUTHENTICATE, 2); p = as_admin_write_field_string(p, USER, user); p = as_admin_write_field_string(p, CREDENTIAL, credential); as_status status = as_admin_send(err, fd, buffer, p, deadline_ms); if (status) { return status; } status = as_socket_read_deadline(err, fd, buffer, HEADER_SIZE, deadline_ms); if (status) { return status; } status = buffer[RESULT_CODE]; if (status) { as_error_set_message(err, status, as_error_string(status)); } return status; }
static bool as_scan_parse_records_async(as_event_command* cmd) { as_error err; as_event_executor* executor = cmd->udata; // udata is overloaded to contain executor. uint8_t* p = cmd->buf; uint8_t* end = p + cmd->len; while (p < end) { as_msg* msg = (as_msg*)p; as_msg_swap_header_from_be(msg); if (msg->result_code) { // Special case - if we scan a set name that doesn't exist on a // node, it will return "not found". if (msg->result_code == AEROSPIKE_ERR_RECORD_NOT_FOUND) { as_event_query_complete(cmd); return true; } as_error_set_message(&err, msg->result_code, as_error_string(msg->result_code)); as_event_response_error(cmd, &err); return true; } p += sizeof(as_msg); if (msg->info3 & AS_MSG_INFO3_LAST) { as_event_query_complete(cmd); return true; } if (! executor->valid) { as_error_set_message(&err, AEROSPIKE_ERR_CLIENT_ABORT, ""); as_event_response_error(cmd, &err); return true; } if (as_scan_parse_record_async(cmd, &p, msg, &err) != AEROSPIKE_OK) { as_event_response_error(cmd, &err); return true; } } return false; }
static as_status as_command_parse_udf_error(as_error* err, as_status status, as_val* val) { if (val && val->type == AS_STRING) { char* begin = ((as_string*)val)->value; char* p = strrchr(begin, ':'); if (p) { p = strrchr(++p, ':'); if (p) { int code = atoi(++p); if (code > 0) { return as_error_set_message(err, code, begin); } } } return as_error_set_message(err, status, begin); } return as_error_set_message(err, status, as_error_string(status)); }
static as_status as_admin_read_blocks(as_error* err, int fd, uint64_t deadline_ms, as_admin_parse_fn parse_fn, as_vector* list) { as_status status = AEROSPIKE_OK; uint8_t* buf = 0; size_t capacity = 0; while (true) { // Read header as_proto proto; status = as_socket_read_deadline(err, fd, (uint8_t*)&proto, sizeof(as_proto), deadline_ms); if (status) { break; } as_proto_swap_from_be(&proto); size_t size = proto.sz; if (size > 0) { // Prepare buffer if (size > capacity) { as_command_free(buf, capacity); capacity = size; buf = as_command_init(capacity); } // Read remaining message bytes in group status = as_socket_read_deadline(err, fd, buf, size, deadline_ms); if (status) { break; } status = parse_fn(err, buf, size, list); if (status != AEROSPIKE_OK) { if (status == AEROSPIKE_QUERY_END) { status = AEROSPIKE_OK; } else { as_error_set_message(err, status, as_error_string(status)); } break; } } } as_command_free(buf, capacity); return status; }
bool as_event_command_parse_result(as_event_command* cmd) { as_msg* msg = (as_msg*)cmd->buf; as_msg_swap_header_from_be(msg); uint8_t* p = cmd->buf + sizeof(as_msg); as_status status = msg->result_code; switch (status) { case AEROSPIKE_OK: { as_record rec; if (msg->n_ops < 1000) { as_record_inita(&rec, msg->n_ops); } else { as_record_init(&rec, msg->n_ops); } rec.gen = msg->generation; rec.ttl = cf_server_void_time_to_ttl(msg->record_ttl); p = as_command_ignore_fields(p, msg->n_fields); as_command_parse_bins(&rec, p, msg->n_ops, cmd->deserialize); as_event_response_complete(cmd); ((as_async_record_command*)cmd)->listener(0, &rec, cmd->udata, cmd->event_loop); as_event_command_release(cmd); as_record_destroy(&rec); break; } case AEROSPIKE_ERR_UDF: { as_error err; as_command_parse_udf_failure(p, &err, msg, status); as_event_response_error(cmd, &err); break; } default: { as_error err; as_error_set_message(&err, status, as_error_string(status)); as_event_response_error(cmd, &err); break; } } return true; }
bool as_event_command_parse_header(as_event_command* cmd) { as_msg* msg = (as_msg*)cmd->buf; if (msg->result_code == AEROSPIKE_OK) { as_event_response_complete(cmd); ((as_async_write_command*)cmd)->listener(0, cmd->udata, cmd->event_loop); as_event_command_release(cmd); } else { as_error err; as_error_set_message(&err, msg->result_code, as_error_string(msg->result_code)); as_event_response_error(cmd, &err); } return true; }
/** * @return AEROSPIKE_OK if successful. Otherwise an error occurred. */ as_status aerospike_udf_put( aerospike * as, as_error * err, const as_policy_info * policy, const char * filename, as_udf_type type, as_bytes * content) { if (type != AS_UDF_TYPE_LUA) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid udf type: %d", type); } as_error_reset(err); if (! policy) { policy = &as->config.policies.info; } char* command = NULL; as_string filename_string; const char* filebase = as_basename(&filename_string, filename); uint32_t encoded_len = cf_b64_encoded_len(content->size); char* content_base64 = malloc(encoded_len + 1); cf_b64_encode(content->value, content->size, content_base64); content_base64[encoded_len] = 0; if (! asprintf(&command, "udf-put:filename=%s;content=%s;content-len=%d;udf-type=%s;", filebase, content_base64, encoded_len, as_udf_type_str[type])) { as_string_destroy(&filename_string); free(content_base64); return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Udf put asprintf failed"); } as_string_destroy(&filename_string); char* response = 0; as_status status = aerospike_info_any(as, err, policy, command, &response); free(command); free(content_base64); if (status) { return status; } free(response); return AEROSPIKE_OK; }
static as_status as_admin_read_list(aerospike* as, as_error* err, const as_policy_admin* policy, uint8_t* command, uint8_t* end, as_admin_parse_fn parse_fn, as_vector* list) { int timeout_ms = (policy)? policy->timeout : as->config.policies.admin.timeout; if (timeout_ms <= 0) { timeout_ms = DEFAULT_TIMEOUT; } uint64_t deadline_ms = as_socket_deadline(timeout_ms); as_cluster* cluster = as->cluster; as_node* node = as_node_get_random(cluster); if (! node) { return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to find server node."); } int fd; as_status status = as_node_get_connection(err, node, deadline_ms, &fd); if (status) { as_node_release(node); return status; } status = as_admin_send(err, fd, command, end, deadline_ms); if (status) { as_node_close_connection(node, fd); as_node_release(node); return status; } status = as_admin_read_blocks(err, fd, deadline_ms, parse_fn, list); if (status) { as_node_close_connection(node, fd); as_node_release(node); return status; } as_node_put_connection(node, fd); as_node_release(node); return status; }
bool as_event_command_parse_success_failure(as_event_command* cmd) { as_msg* msg = (as_msg*)cmd->buf; as_msg_swap_header_from_be(msg); uint8_t* p = cmd->buf + sizeof(as_msg); as_status status = msg->result_code; switch (status) { case AEROSPIKE_OK: { as_error err; as_val* val = 0; status = as_command_parse_success_failure_bins(&p, &err, msg, &val); if (status == AEROSPIKE_OK) { as_event_response_complete(cmd); ((as_async_value_command*)cmd)->listener(0, val, cmd->udata, cmd->event_loop); as_event_command_release(cmd); as_val_destroy(val); } else { as_event_response_error(cmd, &err); } break; } case AEROSPIKE_ERR_UDF: { as_error err; as_command_parse_udf_failure(p, &err, msg, status); as_event_response_error(cmd, &err); break; } default: { as_error err; as_error_set_message(&err, status, as_error_string(status)); as_event_response_error(cmd, &err); break; } } return true; }
static as_status as_scan_parse_records(uint8_t* buf, size_t size, as_scan_task* task, as_error* err) { uint8_t* p = buf; uint8_t* end = buf + size; as_status status; while (p < end) { as_msg* msg = (as_msg*)p; as_msg_swap_header_from_be(msg); if (msg->result_code) { // Special case - if we scan a set name that doesn't exist on a // node, it will return "not found" - we unify this with the // case where OK is returned and no callbacks were made. [AKG] // We are sending "no more records back" to the caller which will // send OK to the main worker thread. if (msg->result_code == AEROSPIKE_ERR_RECORD_NOT_FOUND) { return AEROSPIKE_NO_MORE_RECORDS; } return as_error_set_message(err, msg->result_code, as_error_string(msg->result_code)); } p += sizeof(as_msg); if (msg->info3 & AS_MSG_INFO3_LAST) { return AEROSPIKE_NO_MORE_RECORDS; } status = as_scan_parse_record(&p, msg, task); if (status != AEROSPIKE_OK) { return status; } if (ck_pr_load_32(task->error_mutex)) { err->code = AEROSPIKE_ERR_SCAN_ABORTED; return err->code; } } return AEROSPIKE_OK; }
as_status as_command_parse_header(as_error* err, int fd, uint64_t deadline_ms, void* user_data) { // Read header as_proto_msg* msg = user_data; as_status status = as_socket_read_deadline(err, fd, (uint8_t*)msg, sizeof(as_proto_msg), deadline_ms); if (status) { return status; } // Ensure that there is no data left to read. as_proto_swap_from_be(&msg->proto); as_msg_swap_header_from_be(&msg->m); size_t size = msg->proto.sz - msg->m.header_sz; if (size > 0) { as_log_warn("Unexpected data received from socket after a write: fd=%d size=%zu", fd, size); // Verify size is not corrupted. if (size > 100000) { // The socket will be closed on this error, so we don't have to worry about emptying it. return as_error_update(err, AEROSPIKE_ERR_CLIENT, "Unexpected data received from socket after a write: fd=%d size=%zu", fd, size); } // Empty socket. uint8_t* buf = cf_malloc(size); status = as_socket_read_deadline(err, fd, buf, size, deadline_ms); cf_free(buf); if (status) { return status; } } if (msg->m.result_code) { return as_error_set_message(err, msg->m.result_code, as_error_string(msg->m.result_code)); } return msg->m.result_code; }
void as_event_timeout(as_event_command* cmd) { if (cmd->pipe_listener != NULL) { as_pipe_timeout(cmd); return; } as_error err; as_error_set_message(&err, AEROSPIKE_ERR_TIMEOUT, as_error_string(AEROSPIKE_ERR_TIMEOUT)); // Command has timed out. // Stop watcher if it has been initialized. if (cmd->state > AS_ASYNC_STATE_UNREGISTERED) { as_event_stop_watcher(cmd, cmd->conn); } // Assume timer has already been stopped. // Do not put connection back in pool. as_event_release_async_connection(cmd); as_event_error_callback(cmd, &err); }
static as_status as_admin_write_privileges(uint8_t** p, as_error* err, as_privilege** privileges, int privileges_size) { uint8_t* q = *p + FIELD_HEADER_SIZE; *q++ = (uint8_t)privileges_size; for (int i = 0; i < privileges_size; i++) { as_privilege* priv = privileges[i]; *q++ = (uint8_t)priv->code; if (priv->code >= AS_PRIVILEGE_READ) { q = as_admin_write_string(q, priv->ns); q = as_admin_write_string(q, priv->set); } else { if (priv->ns[0] || priv->set[0]) { return as_error_set_message(err, AEROSPIKE_ERR_PARAM, "Admin privilege has namespace/set scope which is invalid."); } } } as_admin_write_field_header(*p, PRIVILEGES, (int)(q - *p - FIELD_HEADER_SIZE)); *p = q; return AEROSPIKE_OK; }
as_status as_command_parse_success_failure(as_error* err, int fd, uint64_t deadline_ms, void* user_data) { // Read header as_proto_msg msg; as_status status = as_socket_read_deadline(err, fd, (uint8_t*)&msg, sizeof(as_proto_msg), deadline_ms); if (status) { return status; } as_proto_swap_from_be(&msg.proto); as_msg_swap_header_from_be(&msg.m); size_t size = msg.proto.sz - msg.m.header_sz; uint8_t* buf = 0; if (size > 0) { // Read remaining message bytes. buf = as_command_init(size); status = as_socket_read_deadline(err, fd, buf, size, deadline_ms); if (status) { as_command_free(buf, size); return status; } } as_val** val = user_data; // Parse result code and record. status = msg.m.result_code; switch (status) { case AEROSPIKE_OK: { uint8_t* p = buf; status = as_command_parse_success_failure_bins(&p, err, &msg.m, val); if (status != AEROSPIKE_OK) { if (val) { *val = 0; } } break; } case AEROSPIKE_ERR_UDF: { status = as_command_parse_udf_failure(buf, err, &msg.m, status); if (val) { *val = 0; } break; } default: as_error_set_message(err, status, as_error_string(status)); if (val) { *val = 0; } break; } as_command_free(buf, size); return status; }
static as_status as_scan_generic( aerospike* as, as_error* err, const as_policy_scan* policy, const as_scan* scan, aerospike_scan_foreach_callback callback, void* udata, uint64_t* task_id_ptr) { as_error_reset(err); if (! policy) { policy = &as->config.policies.scan; } as_cluster* cluster = as->cluster; as_nodes* nodes = as_nodes_reserve(cluster); uint32_t n_nodes = nodes->size; if (n_nodes == 0) { as_nodes_release(nodes); return as_error_set_message(err, AEROSPIKE_ERR_SERVER, "Scan command failed because cluster is empty."); } // Reserve each node in cluster. for (uint32_t i = 0; i < n_nodes; i++) { as_node_reserve(nodes->array[i]); } uint64_t task_id; if (task_id_ptr) { if (*task_id_ptr == 0) { *task_id_ptr = cf_get_rand64() / 2; } task_id = *task_id_ptr; } else { task_id = cf_get_rand64() / 2; } // Create scan command as_buffer argbuffer; uint16_t n_fields = 0; size_t size = as_scan_command_size(scan, &n_fields, &argbuffer); uint8_t* cmd = as_command_init(size); size = as_scan_command_init(cmd, policy, scan, task_id, n_fields, &argbuffer); // Initialize task. uint32_t error_mutex = 0; as_scan_task task; task.cluster = as->cluster; task.policy = policy; task.scan = scan; task.callback = callback; task.udata = udata; task.err = err; task.error_mutex = &error_mutex; task.task_id = task_id; task.cmd = cmd; task.cmd_size = size; as_status status = AEROSPIKE_OK; if (scan->concurrent) { uint32_t n_wait_nodes = n_nodes; task.complete_q = cf_queue_create(sizeof(as_scan_complete_task), true); // Run node scans in parallel. for (uint32_t i = 0; i < n_nodes; i++) { // Stack allocate task for each node. It should be fine since the task // only needs to be valid within this function. as_scan_task* task_node = alloca(sizeof(as_scan_task)); memcpy(task_node, &task, sizeof(as_scan_task)); task_node->node = nodes->array[i]; int rc = as_thread_pool_queue_task(&cluster->thread_pool, as_scan_worker, task_node); if (rc) { // Thread could not be added. Abort entire scan. if (ck_pr_fas_32(task.error_mutex, 1) == 0) { status = as_error_update(task.err, AEROSPIKE_ERR_CLIENT, "Failed to add scan thread: %d", rc); } // Reset node count to threads that were run. n_wait_nodes = i; break; } } // Wait for tasks to complete. for (uint32_t i = 0; i < n_wait_nodes; i++) { as_scan_complete_task complete; cf_queue_pop(task.complete_q, &complete, CF_QUEUE_FOREVER); if (complete.result != AEROSPIKE_OK && status == AEROSPIKE_OK) { status = complete.result; } } // Release temporary queue. cf_queue_destroy(task.complete_q); } else { task.complete_q = 0; // Run node scans in series. for (uint32_t i = 0; i < n_nodes && status == AEROSPIKE_OK; i++) { task.node = nodes->array[i]; status = as_scan_command_execute(&task); } } // Release each node in cluster. for (uint32_t i = 0; i < n_nodes; i++) { as_node_release(nodes->array[i]); } // Release nodes array. as_nodes_release(nodes); // Free command memory. as_command_free(cmd, size); // If user aborts query, command is considered successful. if (status == AEROSPIKE_ERR_CLIENT_ABORT) { status = AEROSPIKE_OK; } // If completely successful, make the callback that signals completion. if (callback && status == AEROSPIKE_OK) { callback(NULL, udata); } return status; }
as_status as_command_execute(as_cluster* cluster, as_error* err, as_command_node* cn, uint8_t* command, size_t command_len, uint32_t timeout_ms, uint32_t retry, as_parse_results_fn parse_results_fn, void* parse_results_data ) { uint64_t deadline_ms = as_socket_deadline(timeout_ms); uint32_t sleep_between_retries_ms = 0; uint32_t failed_nodes = 0; uint32_t failed_conns = 0; uint32_t iterations = 0; bool release_node; // Execute command until successful, timed out or maximum iterations have been reached. while (true) { as_node* node; if (cn->node) { node = cn->node; release_node = false; } else { node = as_node_get(cluster, cn->ns, cn->digest, cn->write, cn->replica); release_node = true; } if (!node) { as_error_set_message(err, AEROSPIKE_ERR_INVALID_NODE, "Invalid node"); failed_nodes++; sleep_between_retries_ms = 10; goto Retry; } int fd; as_status status = as_node_get_connection(err, node, deadline_ms, &fd); if (status) { if (release_node) { as_node_release(node); } failed_conns++; sleep_between_retries_ms = 1; goto Retry; } // Send command. status = as_socket_write_deadline(err, fd, command, command_len, deadline_ms); if (status) { // Socket errors are considered temporary anomalies. Retry. // Close socket to flush out possible garbage. Do not put back in pool. as_node_close_connection(node, fd); if (release_node) { as_node_release(node); } sleep_between_retries_ms = 0; goto Retry; } // Parse results returned by server. status = parse_results_fn(err, fd, deadline_ms, parse_results_data); if (status == AEROSPIKE_OK) { // Reset error code if retry had occurred. if (iterations > 0) { as_error_reset(err); } } else { switch (status) { case AEROSPIKE_ERR_TIMEOUT: as_node_close_connection(node, fd); if (release_node) { as_node_release(node); } return as_error_update(err, AEROSPIKE_ERR_TIMEOUT, "Timeout: timeout=%d iterations=%u failedNodes=%u failedConns=%u", timeout_ms, ++iterations, failed_nodes, failed_conns); // Close socket on errors that can leave unread data in socket. case AEROSPIKE_ERR_QUERY_ABORTED: case AEROSPIKE_ERR_SCAN_ABORTED: case AEROSPIKE_ERR_CLIENT_ABORT: case AEROSPIKE_ERR_CLIENT: as_node_close_connection(node, fd); if (release_node) { as_node_release(node); } err->code = status; return status; default: err->code = status; break; } } // Put connection back in pool. as_node_put_connection(node, fd); // Release resources. if (release_node) { as_node_release(node); } return status; Retry: // Check if max retries reached. if (++iterations > retry) { break; } // Check for client timeout. if (deadline_ms > 0) { int remaining_ms = (int)(deadline_ms - cf_getms() - sleep_between_retries_ms); if (remaining_ms <= 0) { break; } // Reset timeout in send buffer (destined for server). *(uint32_t*)(command + 22) = cf_swap_to_be32(remaining_ms); } if (sleep_between_retries_ms > 0) { // Sleep before trying again. usleep(sleep_between_retries_ms * 1000); } } // Retries have been exhausted. Return last error. // Fill in timeout stats if timeout occurred. if (err->code == AEROSPIKE_ERR_TIMEOUT) { as_error_update(err, AEROSPIKE_ERR_TIMEOUT, "Timeout: timeout=%d iterations=%u failedNodes=%u failedConns=%u", timeout_ms, iterations, failed_nodes, failed_conns); } return err->code; }
static as_status as_cluster_seed_nodes(as_cluster* cluster, as_error* err, bool enable_warnings) { // Add all nodes at once to avoid copying entire array multiple times. as_vector nodes_to_add; as_vector_inita(&nodes_to_add, sizeof(as_node*), 64); as_vector addresses; as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5); as_node_info node_info; as_error error_local; as_error_init(&error_local); // AEROSPIKE_ERR_TIMEOUT doesn't come with a message; make sure it's initialized. as_status status = AEROSPIKE_OK; as_seeds* seeds = as_seeds_reserve(cluster); for (uint32_t i = 0; i < seeds->size; i++) { as_seed* seed = &seeds->array[i]; as_vector_clear(&addresses); status = as_lookup(&error_local, seed->name, seed->port, &addresses); if (status != AEROSPIKE_OK) { if (enable_warnings) { as_log_warn("Failed to lookup %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } continue; } for (uint32_t i = 0; i < addresses.size; i++) { struct sockaddr_in* addr = as_vector_get(&addresses, i); status = as_lookup_node(cluster, &error_local, addr, &node_info); if (status == AEROSPIKE_OK) { as_host host; if (as_strncpy(host.name, seed->name, sizeof(host.name))) { as_log_warn("Hostname has been truncated: %s", host.name); } host.port = seed->port; as_node* node = as_cluster_find_node_in_vector(&nodes_to_add, node_info.name); if (node) { as_close(node_info.fd); as_node_add_address(node, &host, addr); } else { node = as_node_create(cluster, &host, addr, &node_info); as_address* a = as_node_get_address_full(node); as_log_info("Add node %s %s:%d", node->name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); as_vector_append(&nodes_to_add, &node); } } else { if (enable_warnings) { as_log_warn("Failed to connect to seed %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } } } } as_seeds_release(seeds); if (nodes_to_add.size > 0) { as_cluster_add_nodes(cluster, &nodes_to_add); status = AEROSPIKE_OK; } else { status = as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to seed cluster"); } as_vector_destroy(&nodes_to_add); as_vector_destroy(&addresses); return status; }
static as_status protocols_parse(as_config_tls* tlscfg, uint16_t* oprotocols, as_error* errp) { // In case we don't make it all the way through ... *oprotocols = AS_TLS_PROTOCOL_NONE; // If no protocol_spec is provided, use a default value. if (! tlscfg->protocols) { *oprotocols = AS_TLS_PROTOCOL_DEFAULT; return AEROSPIKE_OK; } uint16_t protocols = AS_TLS_PROTOCOL_NONE; char const* delim = " \t"; char* saveptr = NULL; for (char* tok = strtok_r(tlscfg->protocols, delim, &saveptr); tok != NULL; tok = strtok_r(NULL, delim, &saveptr)) { char act = '\0'; uint16_t current; // Is there a +/- prefix? if ((*tok == '+') || (*tok == '-')) { act = *tok; ++tok; } if (strcasecmp(tok, "SSLv2") == 0) { return as_error_set_message(errp, AEROSPIKE_ERR_TLS_ERROR, "SSLv2 not supported (RFC 6176)"); } else if (strcasecmp(tok, "SSLv3") == 0) { return as_error_set_message(errp, AEROSPIKE_ERR_TLS_ERROR, "SSLv3 not supported"); } else if (strcasecmp(tok, "TLSv1") == 0) { current = AS_TLS_PROTOCOL_TLSV1; } else if (strcasecmp(tok, "TLSv1.1") == 0) { current = AS_TLS_PROTOCOL_TLSV1_1; } else if (strcasecmp(tok, "TLSv1.2") == 0) { current = AS_TLS_PROTOCOL_TLSV1_2; } else if (strcasecmp(tok, "all") == 0) { current = AS_TLS_PROTOCOL_ALL; } else { return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "unknown TLS protocol %s", tok); } if (act == '-') { protocols &= ~current; } else if (act == '+') { protocols |= current; } else { if (protocols != AS_TLS_PROTOCOL_NONE) { return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "TLS protocol %s overrides already set parameters." "Check if a +/- prefix is missing ...", tok); } protocols = current; } } *oprotocols = protocols; return AEROSPIKE_OK; }
as_status as_tls_context_setup(as_config_tls* tlscfg, as_tls_context* ctx, as_error* errp) { // Clear the destination, in case we don't make it. ctx->ssl_ctx = NULL; ctx->pkey = NULL; ctx->cert_blacklist = NULL; ctx->log_session_info = tlscfg->log_session_info; ctx->for_login_only = tlscfg->for_login_only; as_tls_check_init(); pthread_mutex_init(&ctx->lock, NULL); if (tlscfg->cert_blacklist) { ctx->cert_blacklist = cert_blacklist_read(tlscfg->cert_blacklist); if (! ctx->cert_blacklist) { as_tls_context_destroy(ctx); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "Failed to read certificate blacklist: %s", tlscfg->cert_blacklist); } } uint16_t protocols = AS_TLS_PROTOCOL_NONE; as_status status = protocols_parse(tlscfg, &protocols, errp); if (status != AEROSPIKE_OK) { as_tls_context_destroy(ctx); return status; } const SSL_METHOD* method = NULL; // If the selected protocol set is a single protocol we can use a specific method. if (protocols == AS_TLS_PROTOCOL_TLSV1) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = TLSv1_client_method(); #endif } else if (protocols == AS_TLS_PROTOCOL_TLSV1_1) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = TLSv1_1_client_method(); #endif } else if (protocols == AS_TLS_PROTOCOL_TLSV1_2) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = TLSv1_2_client_method(); #endif } else { // Multiple protocols are enabled, use a flexible method. #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = SSLv23_client_method(); #endif } ctx->ssl_ctx = SSL_CTX_new(method); if (ctx->ssl_ctx == NULL) { as_tls_context_destroy(ctx); unsigned long errcode = ERR_get_error(); char errbuf[1024]; ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "Failed to create new TLS context: %s", errbuf); } /* always disable SSLv2, as per RFC 6176 */ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); // Turn off non-enabled protocols. if (! (protocols & AS_TLS_PROTOCOL_TLSV1)) { SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); } if (! (protocols & AS_TLS_PROTOCOL_TLSV1_1)) { SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); } if (! (protocols & AS_TLS_PROTOCOL_TLSV1_2)) { SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); } if (tlscfg->cafile || tlscfg->capath) { int rv = SSL_CTX_load_verify_locations(ctx->ssl_ctx, tlscfg->cafile, tlscfg->capath); if (rv != 1) { as_tls_context_destroy(ctx); char errbuf[1024]; unsigned long errcode = ERR_get_error(); if (errcode != 0) { ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "Failed to load CAFile: %s", errbuf); } return as_error_set_message(errp, AEROSPIKE_ERR_TLS_ERROR, "Unknown failure loading CAFile"); } } if (tlscfg->certfile) { int rv = SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, tlscfg->certfile); if (rv != 1) { // We seem to be seeing this bug: // https://groups.google.com/ // forum/#!topic/mailing.openssl.users/nRvRzmKnEQA // If the rv is not 1 check the error stack; if it doesn't have an // error assume we are OK. // unsigned long errcode = ERR_peek_error(); if (errcode != SSL_ERROR_NONE) { // There *was* an error after all. as_tls_context_destroy(ctx); unsigned long errcode = ERR_get_error(); char errbuf[1024]; ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "SSL_CTX_use_certificate_chain_file failed: %s", errbuf); } } } if (tlscfg->keyfile) { bool ok = false; FILE *fh = fopen(tlscfg->keyfile, "r"); if (fh == NULL) { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "failed to open key file %s: %s", tlscfg->keyfile, strerror(errno)); } else { EVP_PKEY *pkey = PEM_read_PrivateKey(fh, NULL, password_cb, tlscfg->keyfile_pw); if (pkey == NULL) { unsigned long errcode = ERR_get_error(); if (ERR_GET_REASON(errcode) == PEM_R_BAD_PASSWORD_READ) { if (tlscfg->keyfile_pw == NULL) { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "key file %s requires a password", tlscfg->keyfile); } else { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "password for key file %s too long", tlscfg->keyfile); } } else if (ERR_GET_REASON(errcode) == EVP_R_BAD_DECRYPT) { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "invalid password for key file %s", tlscfg->keyfile); } else { char errbuf[1024]; ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "PEM_read_PrivateKey failed: %s", errbuf); } } else { ctx->pkey = pkey; SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey); ok = true; } fclose(fh); } if (!ok) { as_tls_context_destroy(ctx); return AEROSPIKE_ERR_TLS_ERROR; } } if (tlscfg->cipher_suite) { int rv = SSL_CTX_set_cipher_list(ctx->ssl_ctx, tlscfg->cipher_suite); if (rv != 1) { as_tls_context_destroy(ctx); return as_error_set_message(errp, AEROSPIKE_ERR_TLS_ERROR, "no compatible cipher found"); } // It's bogus that we have to create an SSL just to get the // cipher list, but SSL_CTX_get_ciphers doesn't appear to // exist ... SSL* ssl = SSL_new(ctx->ssl_ctx); for (int prio = 0; true; ++prio) { char const * cipherstr = SSL_get_cipher_list(ssl, prio); if (!cipherstr) { break; } as_log_info("cipher %d: %s", prio+1, cipherstr); } SSL_free(ssl); } if (tlscfg->crl_check || tlscfg->crl_check_all) { X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new(); unsigned long flags = X509_V_FLAG_CRL_CHECK; if (tlscfg->crl_check_all) { flags |= X509_V_FLAG_CRL_CHECK_ALL; } X509_VERIFY_PARAM_set_flags(param, flags); SSL_CTX_set1_param(ctx->ssl_ctx, param); X509_VERIFY_PARAM_free(param); } SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, verify_callback); manage_sigpipe(); return AEROSPIKE_OK; }
as_status as_command_parse_result(as_error* err, int fd, uint64_t deadline_ms, void* user_data) { // Read header as_proto_msg msg; as_status status = as_socket_read_deadline(err, fd, (uint8_t*)&msg, sizeof(as_proto_msg), deadline_ms); if (status) { return status; } as_proto_swap_from_be(&msg.proto); as_msg_swap_header_from_be(&msg.m); size_t size = msg.proto.sz - msg.m.header_sz; uint8_t* buf = 0; if (size > 0) { // Read remaining message bytes. buf = as_command_init(size); status = as_socket_read_deadline(err, fd, buf, size, deadline_ms); if (status) { as_command_free(buf, size); return status; } } // Parse result code and record. status = msg.m.result_code; as_command_parse_result_data* data = user_data; switch (status) { case AEROSPIKE_OK: { if (data->record) { as_record* rec = *data->record; if (rec) { if (msg.m.n_ops > rec->bins.capacity) { if (rec->bins._free) { free(rec->bins.entries); } rec->bins.capacity = msg.m.n_ops; rec->bins.size = 0; rec->bins.entries = malloc(sizeof(as_bin) * msg.m.n_ops); rec->bins._free = true; } } else { rec = as_record_new(msg.m.n_ops); *data->record = rec; } rec->gen = msg.m.generation; rec->ttl = cf_server_void_time_to_ttl(msg.m.record_ttl); uint8_t* p = as_command_ignore_fields(buf, msg.m.n_fields); as_command_parse_bins(rec, p, msg.m.n_ops, data->deserialize); } break; } case AEROSPIKE_ERR_UDF: { status = as_command_parse_udf_failure(buf, err, &msg.m, status); break; } default: as_error_set_message(err, status, as_error_string(status)); break; } as_command_free(buf, size); return status; }