Пример #1
0
static int
as_read_user_blocks(int fd, uint8_t* buffer, uint64_t deadline_ms, int timeout_ms, as_vector* /*<as_user_roles*>*/ users)
{
	int buffer_size = STACK_BUF_SZ;
	uint8_t* buf = buffer;
	uint64_t proto;
	int status = 0;
	int size;
	
	while (status == 0) {
		if (cf_socket_read_timeout(fd, buf, 8, deadline_ms, timeout_ms)) {
			status = -1;
			break;
		}
		proto = cf_swap_from_be64(*(uint64_t*)buf);
		size = (int)(proto & 0xFFFFFFFFFFFFL);
		
		if (size > 0) {
			if (size > buffer_size) {
				buffer_size = size;
				
				if (buf != buffer) {
					cf_free(buf);
				}
				buf = cf_malloc(size);
			}
			
			if (cf_socket_read_timeout(fd, buf, size, deadline_ms, timeout_ms)) {
				status = -1;
				break;
			}
			
			status = as_parse_users(buf, size, users);
		}
		else {
			break;
		}
	}
	
	if (buf != buffer) {
		cf_free(buf);
	}
	return (status == QUERY_END)? 0 : status;
}
Пример #2
0
static int
as_execute(aerospike* as, const as_policy_admin* policy, uint8_t* buffer, uint8_t* end)
{
	int timeout_ms = (policy)? policy->timeout : as->config.policies.admin.timeout;
	if (timeout_ms <= 0) {
		timeout_ms = DEFAULT_TIMEOUT;
	}
	uint64_t deadline_ms = cf_getms() + timeout_ms;
	as_node* node = as_node_get_random(as->cluster);
	
	if (! node) {
		return CITRUSLEAF_FAIL_CLIENT;
	}
	
	int fd;
	int status = as_node_get_connection(node, &fd);
	
	if (status) {
		as_node_release(node);
		return status;
	}

	if (as_send(fd, buffer, end, deadline_ms, timeout_ms)) {
		cf_close(fd);
		as_node_release(node);
		return CITRUSLEAF_FAIL_TIMEOUT;
	}
	
	if (cf_socket_read_timeout(fd, buffer, HEADER_SIZE, deadline_ms, timeout_ms)) {
		cf_close(fd);
		as_node_release(node);
		return CITRUSLEAF_FAIL_TIMEOUT;
	}
	
	as_node_put_connection(node, fd);
	as_node_release(node);
	return buffer[RESULT_CODE];
}
Пример #3
0
int
as_authenticate(int fd, const char* user, const char* credential, int timeout_ms)
{
	uint8_t buffer[STACK_BUF_SZ];
	uint8_t* p = buffer + 8;

	p = write_header(p, AUTHENTICATE, 2);
	p = write_field_string(p, USER, user);
	p = write_field_string(p, CREDENTIAL, credential);
	
	if (timeout_ms == 0) {
		timeout_ms = DEFAULT_TIMEOUT;
	}
	uint64_t deadline_ms = cf_getms() + timeout_ms;
	
	if (as_send(fd, buffer, p, deadline_ms, timeout_ms)) {
		return CITRUSLEAF_FAIL_TIMEOUT;
	}

	if (cf_socket_read_timeout(fd, buffer, HEADER_SIZE, deadline_ms, timeout_ms)) {
		return CITRUSLEAF_FAIL_TIMEOUT;
	}
	return buffer[RESULT_CODE];
}
Пример #4
0
// 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);
}
Пример #5
0
static void* 
async_receiver_fn(void *thdata)
{
	int 		rv = -1;
	bool 		network_error = false;
	cl_async_work	*workitem = NULL;
	// cl_async_work	*tmpworkitem = NULL;
	as_msg 		msg;
	cf_queue	*q_to_use = NULL;
	cl_cluster_node	*thisnode = NULL;

	uint8_t		rd_stack_buf[STACK_BUF_SZ];	
	uint8_t		*rd_buf = rd_stack_buf;
	size_t		rd_buf_sz = 0;

	uint64_t	acktrid;
	// uint64_t	starttime, endtime;
	int		progress_timeout_ms;
	unsigned int 	thread_id = cf_atomic32_incr(&g_thread_count);

	if (thdata == NULL) {
		q_to_use = g_cl_async_q;
	} else {
		thisnode = (cl_cluster_node *)thdata;
		q_to_use = thisnode->asyncwork_q;
	}
    
	//Infinite loop which keeps picking work items from the list and try to find the end result 
	while(1) {
		network_error = false;
#if ONEASYNCFD
		if(thisnode->dunned == true) {
			do {
				rv = cf_queue_pop(thisnode->asyncwork_q, &workitem, CF_QUEUE_NOWAIT);
				if (rv == CF_QUEUE_OK) {
					cl_cluster_node_put(thisnode);
					free(workitem);
				}
			} while (rv == CF_QUEUE_OK);

			//We want to delete all the workitems of this node
			shash_reduce_delete(g_cl_async_hashtab, cl_del_node_asyncworkitems, thisnode);
			break;
		}
#endif
		//This call will block if there is no element in the queue
		cf_queue_pop(q_to_use, &workitem, CF_QUEUE_FOREVER);
		//TODO: What if the node gets dunned while this pop call is blocked ?
#if ONEASYNCFD
		//cf_debug("Elements remaining in this node's queue=%d, Hash table size=%d",
		//		cf_queue_sz(thisnode->asyncwork_q), shash_get_size(g_cl_async_hashtab));
#endif

		// If we have no progress in 50ms, we should move to the next workitem 
		// and revisit this workitem at a later stage
		progress_timeout_ms = DEFAULT_PROGRESS_TIMEOUT;

		// Read into this fine cl_msg, which is the short header
		rv = cf_socket_read_timeout(workitem->fd, (uint8_t *) &msg, sizeof(as_msg), workitem->deadline, progress_timeout_ms);
		if (rv) {
#if DEBUG
			cf_debug("Citrusleaf: error when reading header from server - rv %d fd %d", rv, workitem->fd);
#endif
			if (rv != ETIMEDOUT) {
				cf_error("Citrusleaf: error when reading header from server - rv %d fd %d",rv,workitem->fd);
				network_error = true;
				goto Error;
			} else {
				goto Retry;
			}

		}
#ifdef DEBUG_VERBOSE
		dump_buf("read header from cluster", (uint8_t *) &msg, sizeof(cl_msg));
#endif
		cl_proto_swap(&msg.proto);
		cl_msg_swap_header(&msg.m);

		// second read for the remainder of the message 
		rd_buf_sz =  msg.proto.sz  - msg.m.header_sz;
		if (rd_buf_sz > 0) {
			if (rd_buf_sz > sizeof(rd_stack_buf)) {
				rd_buf = malloc(rd_buf_sz);
				if (!rd_buf) {
					cf_error("malloc fail: trying %zu",rd_buf_sz);
					rv = -1; 
					goto Error; 
				}
			}

			rv = cf_socket_read_timeout(workitem->fd, rd_buf, rd_buf_sz, workitem->deadline, progress_timeout_ms);
			if (rv) {
				//We already read some part of the message before but failed to read the
				//remaining data for whatever reason (network error or timeout). We cannot
				//reread as we already read partial data. Declare this as error.
				cf_error("Timeout after reading the header but before reading the body");
				goto Error;
			}
#ifdef DEBUG_VERBOSE
			dump_buf("read body from cluster", rd_buf, rd_buf_sz);
#endif	
		}

		rv = CITRUSLEAF_OK;
		goto Ok;

Retry:
		//We are trying to postpone the reading
		if (workitem->deadline && workitem->deadline < cf_getms()) {
			cf_error("async receiver: out of time : deadline %"PRIu64" now %"PRIu64,
					workitem->deadline, cf_getms());
			//cf_error("async receiver: Workitem missed the final deadline");
			rv = CITRUSLEAF_FAIL_TIMEOUT;
			goto Error;
		} else {
			//We have time. Push the element back to the queue to be considered later
			cf_queue_push(q_to_use, &workitem);
		}

		//If we allocated memory in this loop, release it.
		if (rd_buf && (rd_buf != rd_stack_buf)) {
			free(rd_buf);
		}

		cf_atomic_int_incr(&g_async_stats.retries);

		continue;

Error:
		if (network_error == true) {
			/* 
			 * In case of Async work (for XDS), it may be extreme to
			 * dun a node in case of network error. We just cleanup
			 * things and retry to connect to the remote cluster.
			 * The network error may be a transient one.
			 */
		} 

#if ONEASYNCFD
//Do not close FD
#else
		//We do not know the state of FD. It may have pending data to be read.
		//We cannot reuse the FD. So, close it to be on safe side.
		cf_error("async receiver: Closing the fd %d because of error", workitem->fd);
		cf_close(workitem->fd);
		workitem->fd = -1;
#endif
		cf_atomic_int_incr(&g_async_stats.dropouts);
		//Continue down with what we do during an Ok

		//Inform the caller that there is no response from the server for this workitem.
		//No response does not mean that the work is not done. The work might be 
		//successfully completed on the server side, we just didnt get response for it.
		if (g_fail_cb_fn) {
			g_fail_cb_fn(workitem->udata, rv, workitem->starttime);
		}
Ok:
		//rd_buf may not be there during an error condition.
		if (rd_buf && (rv == CITRUSLEAF_OK)) {
			//As of now, async functionality is there only for put call.
			//In put call, we do not get anything back other than the trid field.
			//So, just pass variable to get back the trid and ignore others.
			if (0 != cl_parse(&msg.m, rd_buf, rd_buf_sz, NULL, NULL, NULL, &acktrid, NULL)) {
				rv = CITRUSLEAF_FAIL_UNKNOWN;
			}
			else {
				rv = msg.m.result_code;
				if (workitem->trid != acktrid) {
#if ONEASYNCFD
					//It is likely that we may get response for a different trid.
					//Just delete the correct one from the queue 
					//put back the current workitem back in the queue.
					shash_get(g_cl_async_hashtab, &acktrid, &tmpworkitem);
					cf_queue_delete(q_to_use, &tmpworkitem, true);
					cf_queue_push(q_to_use, &workitem);
					//From now on workitem will be the one for which we got ack
					workitem = tmpworkitem;
#endif
#ifdef DEBUG
					cf_debug("Got reply for a different trid. Expected=%"PRIu64" Got=%"PRIu64" FD=%d",
							workitem->trid, acktrid, workitem->fd);
#endif
				}
			}

			if (g_success_cb_fn) {
				g_success_cb_fn(workitem->udata, rv, workitem->starttime);
			}
		}

		//Remember to put back the FD into the pool, if it is re-usable.
		if (workitem->fd != -1) {
			cl_cluster_node_fd_put(workitem->node, workitem->fd, true);
		}
		//Also decrement the reference count for this node
		cl_cluster_node_put(workitem->node);

#if ONEASYNCFD
		//Delete the item from the global hashtable
		if (shash_delete(g_cl_async_hashtab, &workitem->trid) != SHASH_OK)
		{
#if DEBUG
			cf_debug("Failure while trying to delete trid=%"PRIu64" from hashtable", workitem->trid);
#endif
		}
#endif

		//Push it back into the free pool. If the attempt fails, free it.
		if (cf_queue_push(g_cl_workitems_freepool_q, &workitem) == -1) {
			free(workitem);
		}

		//If we allocated memory in this loop, release it.
		if (rd_buf && (rd_buf != rd_stack_buf)) {
			free(rd_buf);
		}

		// Kick this thread out if its ID is greater than total
		if (thread_id > cf_atomic32_get(g_async_num_threads)) {
			cf_atomic32_decr(&g_thread_count);
			return NULL;
		}
	}//The infnite loop

	return NULL;
}
Пример #6
0
// 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);
}
Пример #7
0
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;
}