Beispiel #1
0
/**
 * \brief This function handles node commands, when payload packet was received.
 *
 * This function also detects packet loss of previous not received payload
 * packets. All node commands are unpacked from the buffer and added to incoming queue.
 *
 * \param[in]	*C	The verse context.
 *
 * \return		This function returns RECEIVE_PACKET_DELAYED, when delayd packet
 * was received, otherwise it returns RECEIVE_PACKET_SUCCESS.
 */
static int handle_node_commands(struct vContext *C)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	struct Ack_Nak_Cmd ack_nak_cmd;

	/* Note: when ACK and NAK command are add to the AckNak history,
	 * then this vector of commands is automatically compressed. */

	/* Was any packet lost since last receiving of packet? */
	if(r_packet->header.payload_id > vconn->last_r_pay+1) {
		v_print_log(VRS_PRINT_DEBUG_MSG, "Packet(s) lost: %d - %d\n",
				vconn->last_r_pay+1, r_packet->header.payload_id-1);
		/* Add NAK command to the list of ACK NAK commands */
		ack_nak_cmd.id = CMD_NAK_ID;
		ack_nak_cmd.pay_id = vconn->last_r_pay+1;
		v_ack_nak_history_add_cmd(&vconn->ack_nak, &ack_nak_cmd);
	/* Was some delayed packet received? */
	} else if(r_packet->header.payload_id < vconn->last_r_pay+1) {
		if(is_log_level(VRS_PRINT_WARNING))
			v_print_log(VRS_PRINT_WARNING, "Received unordered packet: %d, expected: %d\n", r_packet->header.payload_id, vconn->last_r_pay+1);
		/* Drop this packet */
		return RECEIVE_PACKET_UNORDERED;
	}

	/* ADD ACK command to the list of ACK NAK commands */
	ack_nak_cmd.id = CMD_ACK_ID;
	ack_nak_cmd.pay_id = r_packet->header.payload_id;
	v_ack_nak_history_add_cmd(&vconn->ack_nak, &ack_nak_cmd);

	/* Check if there are really node commands */
	if(r_packet->data!=NULL) {
		/* Unpack node commands and put them to the queue of incoming commands */
		v_cmd_unpack((char*)r_packet->data, r_packet->data_size, vsession->in_queue);
	}

	return RECEIVE_PACKET_SUCCESS;
}
Beispiel #2
0
/**
 * \brief This function set size of receive window (flow control), which is sent
 * to the peer.
 *
 * This function sets C->dgram_conn->rwin_host
 *
 * \param[in]	*C	Verse context
 *
 */
static void set_host_rwin(struct vContext *C)
{
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VSession *vsession = CTX_current_session(C);
	long int free_space;

	switch(dgram_conn->fc_meth) {
	case FC_NONE:
		dgram_conn->rwin_host = 0xFFFFFFFF;
		break;
	case FC_TCP_LIKE:
		/* Compute free space in incomming queue */
		free_space = (long int)vsession->in_queue->max_size - (long int)vsession->in_queue->size;
		dgram_conn->rwin_host = (uint32)(free_space > 0) ? free_space : 0;
		/*printf(">>> max_size: %d, size: %d -> free_size: %ld -> window %d<<<\n",
				vsession->in_queue->max_size, vsession->in_queue->size, free_space, dgram_conn->rwin_host);*/
		break;
	case FC_RESERVED:
		v_print_log(VRS_PRINT_WARNING, "FC_RESERVED should never be used\n");
		dgram_conn->rwin_host = 0xFFFFFFFF;
		break;
	}
}
Beispiel #3
0
/**
 * \brief This function is called, when server is in NEGOTIATE newhost state
 */
int vs_NEGOTIATE_newhost_loop(struct vContext *C)
{
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VMessage *r_message = CTX_r_message(C);
	int i, buffer_pos=0;

	/* Reset content of received message */
	memset(r_message, 0, sizeof(struct VMessage));

	/* Unpack Verse message header */
	buffer_pos += v_unpack_message_header(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	/* Unpack all system commands */
	buffer_pos += v_unpack_message_system_commands(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	v_print_receive_message(C);

	/* Process all system commands */
	for(i=0; i<MAX_SYSTEM_COMMAND_COUNT && r_message->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) {
		switch(r_message->sys_cmd[i].cmd.id) {
		case CMD_CONFIRM_L_ID:
			if(r_message->sys_cmd[i].negotiate_cmd.feature==FTR_HOST_URL) {
				if(r_message->sys_cmd[i].negotiate_cmd.count == 1) {
					if(vsession->host_url!=NULL &&
							strcmp(vsession->host_url, (char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str) == 0) {
						return 1;
					}
				}
			}
			break;
		}
	}

	return 0;
}
Beispiel #4
0
static int vs_RESPOND_krb_auth_loop(struct vContext *C, const char *u_name) {
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VMessage *r_message = CTX_r_message(C);
	struct VMessage *s_message = CTX_s_message(C);
	int i, cmd_rank = 0, client_name_proposed = 0, client_version_proposed = 0;
	unsigned short buffer_pos = 0;
	int user_id;

    /* Reset content of received message */
    memset(r_message, 0, sizeof(struct VMessage));

    /* Unpack Verse message header */
    buffer_pos += v_unpack_message_header(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

    /* Unpack all system commands */
    buffer_pos += v_unpack_message_system_commands(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

    v_print_receive_message(C);

    for(i=0; i<MAX_SYSTEM_COMMAND_COUNT && r_message->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) {
            switch(r_message->sys_cmd[i].cmd.id) {
            case CMD_CHANGE_L_ID:
                    /* Client could propose client name and version */
                    if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_NAME) {
                            if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
                                    /* Only first proposed client name will be used */
                                    vsession->client_name = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
                                    client_name_proposed = 1;
                            }
                    } else if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_VERSION) {
                            if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
                                    /* Only first proposed client name will be used */
                                    vsession->client_version = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
                                    client_version_proposed = 1;
                            }
                    }
                    break;
            default:
                    v_print_log(VRS_PRINT_WARNING,
                                    "This command id: %d is not supported in this state\n",
                                    r_message->sys_cmd[i].cmd.id);
                    break;
            }
    }

    buffer_pos = VERSE_MESSAGE_HEADER_SIZE;

	/* Send confirmation about client name */
	if (client_name_proposed == 1) {
		v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID,
				FTR_CLIENT_NAME, vsession->client_name, NULL);
	}

	/* Send confirmation about client version only in situation, when
	 * client proposed client name too */
	if (client_version_proposed == 1) {
		if (client_name_proposed == 1) {
			v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++,
					CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION,
					vsession->client_version, NULL);
		} else {
			/* Client version without client name is not allowed */
			v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++,
					CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION,
					NULL);
		}
	}

	/* Do user authentication */
	if ((user_id = vs_krb_make_user(C, u_name)) != -1) {
        long int avatar_id;

        pthread_mutex_lock(&vs_ctx->data.mutex);
        avatar_id = vs_create_avatar_node(vs_ctx, vsession, user_id);
        pthread_mutex_unlock(&vs_ctx->data.mutex);

        if(avatar_id == -1) {
                v_print_log(VRS_PRINT_ERROR, "Failed to create avatar node\n");
                return 0;
        }

        /* Save user_id to the session and send it in
         * connect_accept command */
        vsession->user_id = user_id;
        vsession->avatar_id = avatar_id;

        s_message->sys_cmd[cmd_rank].ua_succ.id = CMD_USER_AUTH_SUCCESS;
        s_message->sys_cmd[cmd_rank].ua_succ.user_id = user_id;
        s_message->sys_cmd[cmd_rank].ua_succ.avatar_id = avatar_id;
        cmd_rank++;

        /* Generate random string for coockie */
        vsession->peer_cookie.str = (char*)calloc((COOKIE_SIZE+1), sizeof(char));
        for(i=0; i<COOKIE_SIZE; i++) {
                /* Generate only printable characters (debug prints) */
                vsession->peer_cookie.str[i] = 32 + (char)((float)rand()*94.0/RAND_MAX);
        }
        vsession->peer_cookie.str[COOKIE_SIZE] = '\0';
        /* Set up negotiate command of the host cookie */
        v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CHANGE_R_ID, FTR_COOKIE,
                        vsession->peer_cookie.str, NULL);

        /* Load DED from configuration and save it to the session */
        vsession->ded.str = strdup(vs_ctx->ded);
        v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CHANGE_L_ID, FTR_DED,
                        vsession->ded.str, NULL);

        buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]);

        s_message->header.len = io_ctx->buf_size = buffer_pos;
        s_message->header.version = VRS_VERSION;
        /* Pack header to the beginning of the buffer */
        v_pack_message_header(s_message, io_ctx->buf);

        v_print_send_message(C);

        return 1;
	} else {

		s_message->sys_cmd[0].ua_fail.id = CMD_USER_AUTH_FAILURE;
		s_message->sys_cmd[0].ua_fail.count = 0;

		s_message->sys_cmd[1].cmd.id = CMD_RESERVED_ID;

		buffer_pos += v_pack_stream_system_commands(s_message,
				&io_ctx->buf[buffer_pos]);

		s_message->header.len = io_ctx->buf_size = buffer_pos;
		s_message->header.version = VRS_VERSION;
		/* Pack header to the beginning of the buffer */
		v_pack_message_header(s_message, io_ctx->buf);

		v_print_send_message(C);

		return 1;
	}
	return 0;
}
Beispiel #5
0
/**
 * \brief This function process received message and send respond to the client
 * if client sent CMD_USER_AUTH_REQUEST with method type UA_METHOD_NONE. The
 * response contains list of supported authentication methods (only Password
 * method is supported now)
 * \param[in]	*C	The pointer at Verse context
 * \return		The function returns 1, when response was sent to the client
 * and it returns 0, when all needs were not meet or error occurred.
 */
int vs_RESPOND_methods_loop(struct vContext *C)
{
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VMessage *r_message = CTX_r_message(C);
	struct VMessage *s_message = CTX_s_message(C);
	int i, auth_req = 0,
			client_name_proposed = 0,
			client_version_proposed = 0;
	unsigned short buffer_pos = 0;

	/* Reset content of received message */
	memset(r_message, 0, sizeof(struct VMessage));

	/* Unpack Verse message header */
	buffer_pos += v_unpack_message_header(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	/* Unpack all system commands */
	buffer_pos += v_unpack_message_system_commands(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	v_print_receive_message(C);

	for(i=0; i<MAX_SYSTEM_COMMAND_COUNT && r_message->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) {
		switch(r_message->sys_cmd[i].cmd.id) {
		case CMD_USER_AUTH_REQUEST:
			if(r_message->sys_cmd[i].ua_req.method_type == VRS_UA_METHOD_NONE) {
				auth_req = 1;
			} else {
				v_print_log(VRS_PRINT_WARNING,
						"This auth method id: %d is not supported in this state\n",
						r_message->sys_cmd[i].ua_req.method_type);
			}
			break;
		case CMD_CHANGE_L_ID:
			/* Client could propose client name and version */
			if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_NAME) {
				if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
					/* Only first proposed client name will be used */
					vsession->client_name = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
					client_name_proposed = 1;
				}
			} else if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_VERSION) {
				if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
					/* Only first proposed client name will be used */
					vsession->client_version = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
					client_version_proposed = 1;
				}
			}
			break;
		default:
			v_print_log(VRS_PRINT_WARNING,
					"This command id: %d is not supported in this state\n",
					r_message->sys_cmd[i].cmd.id);
			break;
		}
	}

	if(auth_req == 1) {
		int cmd_rank = 0;
		/* VRS_UA_METHOD_NONE is not supported method. Send list of
		 * supported methods. Current implementation supports
		 * only PASSWORD method now. */

		s_message->sys_cmd[cmd_rank].ua_fail.id = CMD_USER_AUTH_FAILURE;
		/* List of supported methods */
		s_message->sys_cmd[cmd_rank].ua_fail.count = 1;
		s_message->sys_cmd[cmd_rank].ua_fail.method[0] = VRS_UA_METHOD_PASSWORD;
		cmd_rank++;

		/* Send confirmation about client name */
		if(client_name_proposed == 1) {
			v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_NAME,
					vsession->client_name, NULL);
		}

		/* Send confirmation about client version only in situation, when
		 * client proposed client name too */
		if(client_version_proposed == 1) {
			if(client_name_proposed == 1) {
				v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION,
						vsession->client_version, NULL);
			} else {
				/* Client version without client name is not allowed */
				v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION,
						NULL);
			}
		}

		buffer_pos = VERSE_MESSAGE_HEADER_SIZE;

		/* Pack system commands to the buffer */
		buffer_pos += v_pack_stream_system_commands(s_message,&io_ctx->buf[buffer_pos]);

		s_message->header.len = io_ctx->buf_size = buffer_pos;
		s_message->header.version = VRS_VERSION;
		/* Pack header to the beginning of the buffer */
		v_pack_message_header(s_message, io_ctx->buf);

		/* Debug print of command to be send */
		if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
			v_print_send_message(C);
		}

		return 1;
	}

	return 0;
}
Beispiel #6
0
int vs_RESPOND_userauth_loop(struct vContext *C)
{
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VMessage *r_message = CTX_r_message(C);
	struct VMessage *s_message = CTX_s_message(C);
	int i, cmd_rank = 0;
	unsigned short buffer_pos = 0;

	/* Reset content of received message */
	memset(r_message, 0, sizeof(struct VMessage));

	/* Unpack Verse message header */
	buffer_pos += v_unpack_message_header(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	/* Unpack all system commands */
	buffer_pos += v_unpack_message_system_commands(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	v_print_receive_message(C);

	for(i=0; i<MAX_SYSTEM_COMMAND_COUNT && r_message->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) {
		if(r_message->sys_cmd[i].cmd.id == CMD_USER_AUTH_REQUEST) {
			if(r_message->sys_cmd[i].ua_req.method_type == VRS_UA_METHOD_PASSWORD) {
				int user_id;

				/* Do user authentication */
				if((user_id = vs_user_auth(C,
						r_message->sys_cmd[i].ua_req.username,
						r_message->sys_cmd[i].ua_req.data)) != -1)
				{
					long int avatar_id;

					pthread_mutex_lock(&vs_ctx->data.mutex);
					avatar_id = vs_create_avatar_node(vs_ctx, vsession, user_id);
					pthread_mutex_unlock(&vs_ctx->data.mutex);

					if(avatar_id == -1) {
						v_print_log(VRS_PRINT_ERROR, "Failed to create avatar node\n");
						return 0;
					}

					buffer_pos = VERSE_MESSAGE_HEADER_SIZE;

					/* Save user_id to the session and send it in
					 * connect_accept command */
					vsession->user_id = user_id;
					vsession->avatar_id = avatar_id;

					s_message->sys_cmd[cmd_rank].ua_succ.id = CMD_USER_AUTH_SUCCESS;
					s_message->sys_cmd[cmd_rank].ua_succ.user_id = user_id;
					s_message->sys_cmd[cmd_rank].ua_succ.avatar_id = avatar_id;
					cmd_rank++;

					/* Generate random string for coockie */
					vsession->peer_cookie.str = (char*)calloc((COOKIE_SIZE+1), sizeof(char));
					for(i=0; i<COOKIE_SIZE; i++) {
						/* Generate only printable characters (debug prints) */
						vsession->peer_cookie.str[i] = 32 + (char)((float)rand()*94.0/RAND_MAX);
					}
					vsession->peer_cookie.str[COOKIE_SIZE] = '\0';
					/* Set up negotiate command of the host cookie */
					v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CHANGE_R_ID, FTR_COOKIE,
							vsession->peer_cookie.str, NULL);

					/* Load DED from configuration and save it to the session */
					vsession->ded.str = strdup(vs_ctx->ded);
					v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CHANGE_L_ID, FTR_DED,
							vsession->ded.str, NULL);

					buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]);

					s_message->header.len = io_ctx->buf_size = buffer_pos;
					s_message->header.version = VRS_VERSION;
					/* Pack header to the beginning of the buffer */
					v_pack_message_header(s_message, io_ctx->buf);

					v_print_send_message(C);

					return 1;
				} else {

					buffer_pos = VERSE_MESSAGE_HEADER_SIZE;

					s_message->sys_cmd[0].ua_fail.id = CMD_USER_AUTH_FAILURE;
					s_message->sys_cmd[0].ua_fail.count = 0;

					s_message->sys_cmd[1].cmd.id = CMD_RESERVED_ID;

					buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]);

					s_message->header.len = io_ctx->buf_size = buffer_pos;
					s_message->header.version = VRS_VERSION;
					/* Pack header to the beginning of the buffer */
					v_pack_message_header(s_message, io_ctx->buf);

					v_print_send_message(C);

					return 1;
				}
			}
		}
	}
	return 0;
}
Beispiel #7
0
/**
 * \brief WebSocket callback function for received message
 */
void vs_ws_recv_msg_callback(wslay_event_context_ptr wslay_ctx,
		const struct wslay_event_on_msg_recv_arg *arg,
		void *user_data)
{
	struct vContext *C = (struct vContext*)user_data;
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct VSession *session = CTX_current_session(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	int ret;

	if(!wslay_is_ctrl_frame(arg->opcode)) {

		/* Verse server uses only binary message for communication */
		if(arg->opcode == WSLAY_BINARY_FRAME) {
		    struct wslay_event_msg msgarg;

#if DEBUG_WEB_SOCKET
		    unsigned int i;

			v_print_log(VRS_PRINT_DEBUG_MSG,
					"WS Callback received binary message\n");

			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Binary dump\n");

			/* Print dump of received data */
			for(i=0; i<arg->msg_length; i++) {
				v_print_log_simple(VRS_PRINT_DEBUG_MSG, "%d,", arg->msg[i]);
			}
			v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n");
#endif

			/* Copy received data to IO context */
			memcpy(io_ctx->buf,	arg->msg, arg->msg_length);
			io_ctx->buf_size = arg->msg_length;

			if(session->stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) {
				if(v_STREAM_handle_messages(C) != 0) {
					/* When some payload data were received, then poke data thread */
					sem_post(vs_ctx->data.sem);
				} else {
					/* End connection */
					session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;

					/* Try to close connection with WebSocket client */
					wslay_event_queue_close(wslay_ctx,
							WSLAY_CODE_PROTOCOL_ERROR,
							(uint8_t*)"Wrong command",	/* Close message */
							13);	/* The length of close message */
					return;
				}
			} else {
				if( vs_handle_handshake(C) == -1 ) {
					/* End connection */
					session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;

					/* Try to close connection with WebSocket client */
					wslay_event_queue_close(wslay_ctx,
							WSLAY_CODE_PROTOCOL_ERROR,
							(uint8_t*)"Wrong command",	/* Close message */
							13);	/* The length of close message */
					return;
				}

				/* During handshake send response immediately. */

				/* TODO: optionally send message fragmented, when it is needed using:
				 * wslay_event_queue_fragmented_msg() */
				msgarg.opcode = WSLAY_BINARY_FRAME;
				msgarg.msg = (uint8_t*)io_ctx->buf;
				msgarg.msg_length = io_ctx->buf_size;

				/* Queue message for sending */
				if((ret = wslay_event_queue_msg(wslay_ctx, &msgarg)) != 0) {
					v_print_log(VRS_PRINT_ERROR,
							"Unable to queue message to WebSocket: %d\n", ret);
					return;
				} else {
					v_print_log(VRS_PRINT_DEBUG_MSG,
							"WebSocket message successfully queued\n");
				}
			}

		} else if(arg->opcode == WSLAY_TEXT_FRAME) {
			v_print_log(VRS_PRINT_ERROR, "WebSocket text frame is not supported\n");
			return;
		}

	} else {
		/* Print opcode of control message */
		v_print_log(VRS_PRINT_DEBUG_MSG,
				"WS Callback Received Ctrl Message: opcode: %d\n",
				arg->opcode);

		/* Is it closing message? */
		if(arg->opcode & WSLAY_CONNECTION_CLOSE) {

			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Close message with code: %d, message: %s\n",
					arg->status_code,
					arg->msg);

			/* When this control message was received at second time, then
			 * switch to the state CLOSED. Otherwise switch to the state
			 * CLOSING */
			if(session->stream_conn->host_state == TCP_SERVER_STATE_CLOSING) {
				session->stream_conn->host_state = TCP_SERVER_STATE_CLOSED;
			} else {
				session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;
				/* When server wasn't in the state closing, then send
				 * confirmation to the client, that this connection will be
				 * closed */
				wslay_event_queue_close(wslay_ctx,
						WSLAY_CODE_NORMAL_CLOSURE,
						(uint8_t*)"Closing connection",
						15);
			}
		}
	}
}
Beispiel #8
0
/**
 * \brief This function is the callback function for received Change_L
 * commands in REQUEST state.
 */
static int vc_REQUEST_CHANGE_L_cb(struct vContext *C, struct Generic_Cmd *cmd)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct Negotiate_Cmd *change_l_cmd = (struct Negotiate_Cmd*)cmd;
	int value_rank;

	if(change_l_cmd->feature == FTR_TOKEN) {
		/* This block of code checks, if received token is the same as the
		 * negotiated token. When token is not the same, then the client
		 * will not response to the received packet. */
		if( vsession->peer_token.str != NULL &&
				change_l_cmd->count > 0)
		{
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Remote TOKEN: %s proposed\n",
					change_l_cmd->value[0].string8.str);
			return 1;
		} else {
			v_print_log(VRS_PRINT_WARNING, "Peer proposed wrong TOKEN\n");
			return 0;
		}
	}

	/* Server proposes it's own Flow Control */
	if(change_l_cmd->feature == FTR_FC_ID) {
		int ret = 0;

		for(value_rank=0; value_rank<change_l_cmd->count; value_rank++) {
			/* Is value in "list" of supported methods */
			if(change_l_cmd->value[value_rank].uint8 == FC_NONE ||
					change_l_cmd->value[value_rank].uint8 == FC_TCP_LIKE)
			{
				/* It will try to use first found supported method, but ... */
				if(dgram_conn->fc_meth == FC_RESERVED) {
					/* Flow Control has not been proposed yet */
				} else {
					/* Server has to propose same FC methods for client
					 * and server. Client can't use different method then
					 * server and vice versa. */
					if(dgram_conn->fc_meth != change_l_cmd->value[value_rank].uint8) {
						v_print_log(VRS_PRINT_WARNING,
								"Skipping proposed local FC :%d; it is not teh same as proposed remote FC: %d\n",
								change_l_cmd->value[value_rank].uint8,
								dgram_conn->fc_meth);
						continue;
					}
				}
				dgram_conn->fc_meth = change_l_cmd->value[value_rank].uint8;
				v_print_log(VRS_PRINT_DEBUG_MSG,
						"Local Flow Control ID: %d proposed\n",
						change_l_cmd->value[0].uint8);
				ret = 1;
				break;
			} else {
				v_print_log(VRS_PRINT_ERROR,
						"Skipping unsupported Flow Control method: %d\n",
						change_l_cmd->value[value_rank].uint8);
				continue;
			}
		}

		return ret;
	}

	/* Server proposes it's own scale of Flow Control Window */
	if(change_l_cmd->feature == FTR_RWIN_SCALE) {
		if(change_l_cmd->count >= 1) {
			dgram_conn->rwin_peer_scale = change_l_cmd->value[0].uint8;
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Scale of peer RWIN: %d proposed\n",
					dgram_conn->rwin_peer_scale);
			return 1;
		} else {
			v_print_log(VRS_PRINT_ERROR,
					"At last on value of RWIN scale has to be proposed\n");
			return 0;
		}
	}

	/* Ignore unknown feature */
	return 1;
}
Beispiel #9
0
void* vc_main_dgram_loop(void *arg)
{
	struct vContext *C = (struct vContext*)arg;
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = vsession->dgram_conn;
	struct VStreamConn *stream_conn = CTX_current_stream_conn(C);
	struct VPacket *r_packet, *s_packet;
	char error = 0;
	const uint8 *ret_val = 0;

	if(dgram_conn==NULL) {
		/* Create new datagrame connection */
		if((dgram_conn = vc_create_client_dgram_conn(C))==NULL) {
			goto finish;
		}
		vsession->dgram_conn = dgram_conn;
	}

	CTX_current_dgram_conn_set(C, dgram_conn);
	CTX_io_ctx_set(C, &dgram_conn->io_ctx);

#if (defined WITH_OPENSSL) && OPENSSL_VERSION_NUMBER>=0x10000000
	/* If negotiated security is DTLS, then try to do DTLS handshake */
	if(dgram_conn->io_ctx.flags & SOCKET_SECURED) {
		if(vc_create_dtls_connection(C) == 0) {
			CTX_current_dgram_conn_set(C, NULL);
			CTX_io_ctx_set(C, NULL);
			free(dgram_conn);
			ret_val = &vrs_conn_term_error;
			goto finish;
		}
	}
#endif

	/* Packet structure for receiving */
	r_packet = (struct VPacket*)malloc(sizeof(struct VPacket));
	CTX_r_packet_set(C, r_packet);

	/* Packet structure for sending */
	s_packet = (struct VPacket*)malloc(sizeof(struct VPacket));
	CTX_s_packet_set(C, s_packet);

	/* Run loop of the first phase of the handshake */
	if((error = vc_REQUEST_loop(C)) == STATE_EXIT_ERROR) {
		/* Do not confirm proposed URL, when client was not able to connect
		 * to the server */
		CTX_io_ctx_set(C, &stream_conn->io_ctx);
		vc_NEGOTIATE_newhost(C, NULL);
		CTX_io_ctx_set(C, &dgram_conn->io_ctx);
		ret_val = &vrs_conn_term_error;
		goto end;
	}

	/* When server responded in the first phase of the handshake, then run loop of the second
	 * phase of the handshake */
	if((error = vc_PARTOPEN_loop(C)) ==  STATE_EXIT_ERROR) {
		/* Do not confirm proposed URL, when client was not able to connect
		 * to the server */
		CTX_io_ctx_set(C, &stream_conn->io_ctx);
		vc_NEGOTIATE_newhost(C, NULL);
		CTX_io_ctx_set(C, &dgram_conn->io_ctx);
		ret_val = &vrs_conn_term_error;
		goto end;
	} else {
		struct Connect_Accept_Cmd *conn_accept;

		/* Put connect accept command to queue -> call callback function */
		conn_accept = v_Connect_Accept_create(vsession->avatar_id, vsession->user_id);
		v_in_queue_push(vsession->in_queue, (struct Generic_Cmd*)conn_accept);

		/* Send confirmation of the URL to the server */
		CTX_io_ctx_set(C, &stream_conn->io_ctx);
		vc_NEGOTIATE_newhost(C, vsession->host_url);
		CTX_io_ctx_set(C, &dgram_conn->io_ctx);

		/* TCP connection could be closed now */
		vsession->stream_conn->host_state = TCP_CLIENT_STATE_CLOSING;
	}

	/* Main loop for data exchange */
	if((error = vc_OPEN_loop(C)) == STATE_EXIT_ERROR) {
		ret_val = &vrs_conn_term_error;
		goto end;
	}

	/* Closing loop */
	if((error = vc_CLOSING_loop(C)) == STATE_EXIT_ERROR) {
		ret_val = &vrs_conn_term_error;
		goto end;
	}

	/* TODO: distinguish between terminating connection by server and client */
	ret_val = &vrs_conn_term_server;

end:

	/* CLOSED state */
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Client state: CLOSED\n");
		printf("%c[%dm", 27, 0);
	}

	free(r_packet);
	free(s_packet);

#if (defined WITH_OPENSSL) && OPENSSL_VERSION_NUMBER>=0x10000000
	if(dgram_conn->io_ctx.flags & SOCKET_SECURED) {
		vc_destroy_dtls_connection(C);
	}
#endif

	v_conn_dgram_destroy(dgram_conn);

	CTX_current_dgram_conn_set(C, NULL);

finish:
	pthread_exit((void*)ret_val);
	return NULL;
}
Beispiel #10
0
int vc_create_dtls_connection(struct vContext *C)
{
	struct VC_CTX *vc_ctx = CTX_client_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct timeval timeout;
	int ret = 0;

	v_print_log(VRS_PRINT_DEBUG_MSG, "Try to do DTLS handshake at UDP socket: %d\n",
			dgram_conn->io_ctx.sockfd);

	/* Create ssl for new connection */
	if( (dgram_conn->io_ctx.ssl = SSL_new(vc_ctx->dtls_ctx)) == NULL) {
		v_print_log(VRS_PRINT_ERROR, "SSL_new(%p)\n", (void*)vc_ctx->dtls_ctx);
		return 0;
	}

	/* Set state of bio as connected */
	if(dgram_conn->io_ctx.peer_addr.ip_ver == IPV4) {
		ret = BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &dgram_conn->io_ctx.peer_addr.addr.ipv6);
	} else if(dgram_conn->io_ctx.peer_addr.ip_ver == IPV6) {
		ret = BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &dgram_conn->io_ctx.peer_addr.addr.ipv4);
	}

	/* When BIO_ctrl was called with bad arguments, then it returns 0 */
	if(ret==0) {
		v_print_log(VRS_PRINT_ERROR, "BIO_ctrl()\n");
		SSL_free(dgram_conn->io_ctx.ssl);
		return 0;
	}

	/* Set and activate timeouts */
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;
	BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
	BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout);

	/* Bind ssl and bio */
	SSL_set_bio(dgram_conn->io_ctx.ssl, dgram_conn->io_ctx.bio, dgram_conn->io_ctx.bio);

	/* Try to do DTLS handshake */
again:
	if ((ret = SSL_connect(dgram_conn->io_ctx.ssl)) <= 0) {
		int err = SSL_get_error(dgram_conn->io_ctx.ssl, ret);
		if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
			gettimeofday(&timeout, NULL);
			if((timeout.tv_sec - vsession->peer_token.tv.tv_sec) > VRS_TIMEOUT) {
				v_print_log(VRS_PRINT_ERROR, "Token timed out\n");
				return 0;
			}
			usleep(1000);
			goto again;
		}
		ERR_print_errors_fp(stderr);
		v_print_log(VRS_PRINT_ERROR, "SSL_connect() failed: %d -> %d\n", ret, err);
		SSL_free(dgram_conn->io_ctx.ssl);
		dgram_conn->io_ctx.ssl = NULL;
		dgram_conn->io_ctx.bio = NULL;
		return 0;
	} else {
		v_print_log(VRS_PRINT_DEBUG_MSG, "DTLS handshake finished\n");

		v_print_log(VRS_PRINT_DEBUG_MSG, "Current cipher: %s\n",
				SSL_CIPHER_get_name(SSL_get_current_cipher(dgram_conn->io_ctx.ssl)));
	}

	return 1;
}
Beispiel #11
0
/**
 * \brief This command handle received packets in OPEN state at client and
 * server in the same way.
 */
int handle_packet_in_OPEN_state(struct vContext *C)
{
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	int ret, first_sys_index, i;

	/* Does packet contains node commands? */
	if(r_packet->header.flags & PAY_FLAG) {
		ret = handle_node_commands(C);
		if(ret == RECEIVE_PACKET_UNORDERED)
			return ret;
		r_packet->acked=0;
	}

	/* Compute real RWIN of Flow Control */
	vconn->rwin_peer = r_packet->header.window << vconn->rwin_peer_scale;

	/* Was packet received with any ACK or NAK command? */
	if(r_packet->header.flags & ACK_FLAG) {
		ret = handle_ack_nak_commands(C);
	}

	/* Handle other system commands */
	if(ret>=0) {
		first_sys_index = ret;
	} else {
		first_sys_index = 0;
	}

	for(i=first_sys_index;
			i<MAX_SYSTEM_COMMAND_COUNT && r_packet->sys_cmd[i].cmd.id != CMD_RESERVED_ID;
			i++)
	{
		if(r_packet->sys_cmd[i].cmd.id == CMD_CHANGE_L_ID &&
				r_packet->sys_cmd[i].negotiate_cmd.feature == FTR_FPS &&
				r_packet->sys_cmd[i].negotiate_cmd.count > 0)
		{
			vsession->fps_host = vsession->fps_peer = r_packet->sys_cmd[i].negotiate_cmd.value->real32;
			vsession->tmp_flags |= SYS_CMD_NEGOTIATE_FPS;
		}

		if(r_packet->sys_cmd[i].cmd.id == CMD_CONFIRM_L_ID &&
				r_packet->sys_cmd[i].negotiate_cmd.feature == FTR_FPS &&
				r_packet->sys_cmd[i].negotiate_cmd.count > 0)
		{
			vsession->fps_peer = r_packet->sys_cmd[i].negotiate_cmd.value->real32;
		}
	}

	/* Was packet received with valid ANK ID? */
	if(r_packet->header.flags & ANK_FLAG) {
		/* Remove appropriate ACK and NAK commands form the history of ACK
		 * and NAK commands */
		v_ack_nak_history_remove_cmds(&vconn->ack_nak, r_packet->header.ank_id);
	}

	/* Does peer want to finish this connection? */
	if(r_packet->header.flags & FIN_FLAG) {
		/* Change host state. Main connection loop will do the rest. */
		vconn->host_state = UDP_CLIENT_STATE_CLOSING;
	}

	return RECEIVE_PACKET_SUCCESS;
}
Beispiel #12
0
/**
 * \brief This function is called, when acknowledgment packet was received.
 *
 * This function processes all ACK and NAK commands and it add not obsolete
 * commands from the history of sent packets to the out queue again. This
 * function also removes positively and negatively acknowledged packets from
 * history of sent packets. ANK ID is updated.
 *
 * \param[in] *C	The verse context.
 *
 * \return	This function returns index of last ACK command in sequence of
 * system command, when ACK and NAK commands are the beginning of system
 * commands, otherwise it returns -2. When no ACK or NAK command was found,
 * then -1 is returned;
 */
static int handle_ack_nak_commands(struct vContext *C)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct VPacket *r_packet = CTX_r_packet(C);
	struct VSent_Packet *sent_packet;
	struct VSent_Command *sent_cmd, *sent_cmd_prev;
	unsigned long int rtt = ULONG_MAX;
	struct timeval tv;
	uint32 ack_id, nak_id;
	int i, ret=-1;

	gettimeofday(&tv, NULL);

	/* Compute SRTT */
	if(r_packet->sys_cmd[0].cmd.id==CMD_ACK_ID) {
		unsigned long int tmp;
		int i=0;

		/* Try to find the smallest RTT from acknowledged packets */
		for(i=0; r_packet->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) {
			if(r_packet->sys_cmd[i].cmd.id==CMD_ACK_ID) {
				sent_packet = v_packet_history_find_packet(&vconn->packet_history,
					r_packet->sys_cmd[i].ack_cmd.pay_id);
				if(sent_packet!=NULL) {
					tmp = packet_rtt(sent_packet, &tv);
					if(tmp<rtt) rtt=tmp;
				}
			}
		}

		if(rtt<ULONG_MAX) {
			/* Computation of SRTT as described in RFC */
			if(vconn->srtt==0) {
				vconn->srtt = rtt;
			} else {
				vconn->srtt = RTT_ALPHA*vconn->srtt + (1-RTT_ALPHA)*rtt;
			}
			v_print_log(VRS_PRINT_DEBUG_MSG, "RTT: %d [us]\n", rtt);
			v_print_log(VRS_PRINT_DEBUG_MSG, "SRTT: %d [us]\n", vconn->srtt);
		}
	}


	/* Process all ACK and NAK commands. ACK and NAK commands should be first
	 * and there should not be other system commands between ACK and NAK
	 * commands. */
	for(i=0;
			r_packet->sys_cmd[i].cmd.id == CMD_NAK_ID ||
					r_packet->sys_cmd[i].cmd.id == CMD_ACK_ID;
			i++) {

		if(r_packet->sys_cmd[i].cmd.id == CMD_ACK_ID) {
			/* Check if ACK and NAK commands are the first system commands */
			if(ret!=-2 && ret==i-1) {
				ret = i;
			} else {
				ret = -2;
			}
			/* If this is not the last ACK command in the sequence of
			 * ACK/NAK commands, then remove all packets from history of
			 * sent packet, that are in following sub-sequence of ACK
			 * commands */
			if(r_packet->sys_cmd[i+1].cmd.id == CMD_NAK_ID ||
					r_packet->sys_cmd[i+1].cmd.id == CMD_ACK_ID)
			{
				/* Remove all acknowledged payload packets from the history
				 * of sent payload packets */
				for(ack_id = r_packet->sys_cmd[i].ack_cmd.pay_id;
						ack_id < r_packet->sys_cmd[i+1].nak_cmd.pay_id;
						ack_id++)
				{
					v_packet_history_rem_packet(C, ack_id);
				}
			} else {
				/* Remove this acknowledged payload packets from the history
				 * of sent payload packets */
				v_packet_history_rem_packet(C, r_packet->sys_cmd[i].ack_cmd.pay_id);
				/* This is the last ACK command in the sequence of ACK/NAK
				 * commands. Update ANK ID. */
				vconn->ank_id = r_packet->sys_cmd[i].ack_cmd.pay_id;
			}
		} else if(r_packet->sys_cmd[i].cmd.id == CMD_NAK_ID) {
			/* Check if ACK and NAK commands are the first system commands */
			if(ret!=-2 && ret==i-1) {
				ret = i;
			} else {
				ret = -2;
			}
			/* Go through the sub-sequence of NAk commands and try to re-send
			 * not-obsolete data from these packets */
			for(nak_id = r_packet->sys_cmd[i].nak_cmd.pay_id;
					nak_id < r_packet->sys_cmd[i+1].ack_cmd.pay_id;
					nak_id++)
			{
				/* Add not obsolete data of lost packet to the outgoing queue */
				/* Update ANK ID */
				sent_packet = v_packet_history_find_packet(&vconn->packet_history, nak_id);
				if(sent_packet != NULL) {
					v_print_log(VRS_PRINT_DEBUG_MSG, "Try to re-send packet: %d\n", nak_id);
					sent_cmd = sent_packet->cmds.last;

					/* Go through all commands in command list and add not
					 * obsolete commands to the outgoing queue */
					while(sent_cmd != NULL) {
						sent_cmd_prev = sent_cmd->prev;
						if(sent_cmd->vbucket != NULL && sent_cmd->vbucket->data != NULL) {

							/* Try to add command back to the outgoing command queue */
							if(v_out_queue_push_head(vsession->out_queue,
									sent_cmd->prio,
									(struct Generic_Cmd*)sent_cmd->vbucket->data) == 1)
							{
								/* Remove bucket from the history of sent commands too */
								v_hash_array_remove_item(&vconn->packet_history.cmd_hist[sent_cmd->id]->cmds, sent_cmd->vbucket->data);

								/* When command was added back to the queue,
								 * then delete only sent command */
								v_list_free_item(&sent_packet->cmds, sent_cmd);

							}
						}
						sent_cmd = sent_cmd_prev;
					}

					/* When all not obsolete commands are added to outgoing
					 * queue, then this packet could be removed from packet
					 * history*/
					v_packet_history_rem_packet(C, nak_id);
				}
			}
		}
	}

	return ret;
}
Beispiel #13
0
/**
 * \brief This function send packets in OPEN and CLOSEREQ state.
 */
int send_packet_in_OPEN_CLOSEREQ_state(struct vContext *C)
{
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VPacket *s_packet = CTX_s_packet(C);
	struct VSent_Packet *sent_packet = NULL;
	unsigned short buffer_pos = 0;
	struct timeval tv;
	int ret, keep_alive_packet = -1, full_packet = 0;
	int error_num;
	uint16 swin, prio_win, sent_size = 0;
	uint32 rwin;
	int cmd_rank = 0;

	/* Verse packet header */
	s_packet->header.version = 1;

	/* Clear header flags */
	s_packet->header.flags = 0;

	/* Check if it is necessary to send payload packet */
	ret = check_pay_flag(C);
	if(ret!=0) {
		s_packet->header.flags |= PAY_FLAG;
		if(ret==2) {
			keep_alive_packet = 1;
		}
	}

	/* When server is in CLOSEREQ state, then FIN flag should be set up */
	if(vconn->host_state == UDP_SERVER_STATE_CLOSEREQ) {
		s_packet->header.flags |= FIN_FLAG;
	}

	/* Check if it is necessary to send acknowledgment of received payload
	 * packet */
	ret = check_ack_nak_flag(C);
	if(ret==1) {
		s_packet->header.flags |= ACK_FLAG;

		/* Update last acknowledged Payload packet */
		vconn->last_acked_pay = vconn->last_r_pay;

		/* Add ACK and NAK commands from the list of ACK and NAK commands to the
		 * packet (only max count of ACK and NAK commands could be added to
		 * the packet) */
		for(cmd_rank = 0;
				cmd_rank < vconn->ack_nak.count &&
				cmd_rank < MAX_SYSTEM_COMMAND_COUNT;
				cmd_rank++)
		{
			s_packet->sys_cmd[cmd_rank].ack_cmd.id = vconn->ack_nak.cmds[cmd_rank].id;
			s_packet->sys_cmd[cmd_rank].ack_cmd.pay_id = vconn->ack_nak.cmds[cmd_rank].pay_id;
		}
		s_packet->sys_cmd[cmd_rank].cmd.id = CMD_RESERVED_ID;

	}

	/* If there is no need to send Payload or AckNak packet, then cancel
	 * sending of packet */
	if(! ((s_packet->header.flags & PAY_FLAG) ||
			(s_packet->header.flags & ACK_FLAG)) ) return SEND_PACKET_CANCELED;

	s_packet->header.flags |= ANK_FLAG;
	s_packet->header.ank_id = vconn->ank_id;

	/* Compute current windows for flow control and congestion control */
	set_host_rwin(C);
	set_host_cwin(C);

	/* Set window of flow control that will sent to receiver */
	rwin = vconn->rwin_host >> vconn->rwin_host_scale;
	s_packet->header.window = (unsigned short)(rwin > 0xFFFF) ? 0xFFFF : rwin;
	/*printf("\t---real window: %d---\n", s_packet->header.window);*/

	/* Compute how many data could be sent to not congest receiver */
	rwin = vconn->rwin_peer - vconn->sent_size;

	/* Select smallest window for sending (congestion control window or flow control window)*/
	swin = (vconn->cwin < rwin) ? vconn->cwin : rwin;

	/* Set up Payload ID, when there is need to send payload packet */
	if(s_packet->header.flags & PAY_FLAG)
		s_packet->header.payload_id = vconn->host_id + vconn->count_s_pay;
	else
		s_packet->header.payload_id  = 0;

	/* Set up AckNak ID, when there are some ACK or NAK command in the packet */
	if(s_packet->header.flags & ACK_FLAG)
		s_packet->header.ack_nak_id = vconn->count_s_ack;
	else
		s_packet->header.ack_nak_id = 0;

	/* When negotiated and used FPS is different, then pack negotiate command
	 * for FPS */
	if(vsession->fps_host != vsession->fps_peer) {
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_L_ID, FTR_FPS, &vsession->fps_host, NULL);
	} else {
		if(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS) {
			cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
					CMD_CONFIRM_L_ID, FTR_FPS, &vsession->fps_peer, NULL);
			/* Send confirmation only once for received system command */
			vsession->tmp_flags &= ~SYS_CMD_NEGOTIATE_FPS;
		}
	}

	v_print_send_packet(C);

	/* Fill buffer */
	buffer_pos += v_pack_packet_header(s_packet, &io_ctx->buf[buffer_pos]);
	buffer_pos += v_pack_dgram_system_commands(s_packet, &io_ctx->buf[buffer_pos]);

	/* When this is not pure keep alive packet */
	if(s_packet->header.flags & PAY_FLAG) {

		sent_packet = v_packet_history_add_packet(&vconn->packet_history, s_packet->header.payload_id);

		assert(sent_packet != NULL);

		if(keep_alive_packet != 1) {
			real32 prio_sum_high, prio_sum_low, r_prio;
			uint32 prio_count;
			int16 prio, max_prio, min_prio;
			uint16 tot_cmd_size;

			/* Print outgoing command with green color */
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 32);
			}

			max_prio = v_out_queue_get_max_prio(vsession->out_queue);
			min_prio = v_out_queue_get_min_prio(vsession->out_queue);

			prio_sum_high = v_out_queue_get_prio_sum_high(vsession->out_queue);
			prio_sum_low = v_out_queue_get_prio_sum_low(vsession->out_queue);

			v_print_log(VRS_PRINT_DEBUG_MSG, "Packing prio queues, cmd count: %d\n", v_out_queue_get_count(vsession->out_queue));

			/* Go through all priorities and pick commands from priority queues */
			for(prio = max_prio; prio >= min_prio; prio--)
			{
				/* TODO: Add better check here */
				if(prio <= VRS_DEFAULT_PRIORITY && buffer_pos >= vconn->io_ctx.mtu) {
					break;
				}

				prio_count = v_out_queue_get_count_prio(vsession->out_queue, prio);

				if(prio_count > 0) {
					r_prio = v_out_queue_get_prio(vsession->out_queue, prio);

					/* Compute size of buffer that could be occupied by
					 * commands from this queue */
					if(prio >= VRS_DEFAULT_PRIORITY) {
						prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_high;
					} else {
						prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_low;
					}

					/* Debug print */
					v_print_log(VRS_PRINT_DEBUG_MSG, "Queue: %d, count: %d, r_prio: %6.3f, prio_win: %d\n",
							prio, prio_count, r_prio, prio_win);

					/* Get total size of commands that were stored in queue (sent_size) */
					tot_cmd_size = 0;
					/* Pack commands from queues with high priority to the buffer */
					buffer_pos = pack_prio_queue(C, sent_packet, buffer_pos, prio, prio_win, &tot_cmd_size);
					sent_size += tot_cmd_size;
				}
			}

			/* Use default color for output */
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%dm", 27, 0);
			}
		} else {
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 32);
				v_print_log(VRS_PRINT_DEBUG_MSG, "Keep alive packet\n");
				printf("%c[%dm", 27, 0);
			}
		}
	}

	/* Update sent_size */
	vconn->sent_size += sent_size;

	io_ctx->buf_size = buffer_pos;

	/* Send buffer */
	ret = v_send_packet(io_ctx, &error_num);

	if(ret==SEND_PACKET_SUCCESS) {
		gettimeofday(&tv, NULL);

		/* Update time of sending last payload packet */
		if(s_packet->header.flags & PAY_FLAG) {
			vconn->tv_pay_send.tv_sec = tv.tv_sec;
			vconn->tv_pay_send.tv_usec = tv.tv_usec;
			/* Store time of sending packet in history of sent packets. It is
			 * used for computing RTT and SRTT */
			if(sent_packet != NULL) {
				sent_packet->tv.tv_sec = tv.tv_sec;
				sent_packet->tv.tv_usec = tv.tv_usec;
			}
		}

		/* Update time of sending last acknowledgment packet */
		if(s_packet->header.flags & ACK_FLAG) {
			vconn->tv_ack_send.tv_sec = tv.tv_sec;
			vconn->tv_ack_send.tv_usec = tv.tv_usec;
		}

		/* Update counter of sent packets */
		if(s_packet->header.flags & PAY_FLAG) vconn->count_s_pay++;
		if(s_packet->header.flags & ACK_FLAG) vconn->count_s_ack++;

		/* If the packet was sent full and there are some pending data to send
		 * then modify returned value*/
		if(full_packet == 1) {
			ret = SEND_PACKET_FULL;
		}
	} else {
		/* When packet wasn't sent, then remove this packet from history */
		if(sent_packet != NULL) {
			v_packet_history_rem_packet(C, s_packet->header.payload_id);
		}
	}

	/*v_print_packet_history(&vconn->packet_history);*/

	return ret;
}
Beispiel #14
0
/**
 * \brief This function packs and compress command to the packet from one
 * priority queue.
 *
 * \param[in]	*C	The verse context
 * \param[in]	*sent_packet	The pointer at structure with send packet
 * \param[in]	buffer_pos		The curent size of buffer of sent packet
 * \param[in]	prio			The priority of sub queue
 * \param[in]	prio_win		The window size of current prio queue
 * \param[out]	tot_cmd_size	The total size of commands that were poped from prio queue
 */
static int pack_prio_queue(struct vContext *C,
		struct VSent_Packet *sent_packet,
		int buffer_pos,
		uint8 prio,
		uint16 prio_win,
		uint16 *tot_cmd_size)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct Generic_Cmd *cmd;
	int ret, last_cmd_count = 0;
	uint16 cmd_count, cmd_len, cmd_size, sum_len=0;
	int8 cmd_share;
	uint8  last_cmd_id = CMD_RESERVED_ID;

	while( (v_out_queue_get_count_prio(vsession->out_queue, prio) > 0) &&
			(sum_len < prio_win) &&
			(buffer_pos < vconn->io_ctx.mtu))
	{
		cmd_count = 0;
		cmd_share = 0;

		/* Compute how many commands could be compressed to the packet
		 * and compute right length of compressed commands. */
		cmd_len = ((prio_win - sum_len)<(vconn->io_ctx.mtu - buffer_pos)) ?
				(prio_win - sum_len) :
				(vconn->io_ctx.mtu - buffer_pos);

		/* Remove command from queue */
		cmd = v_out_queue_pop(vsession->out_queue, prio, &cmd_count, &cmd_share, &cmd_len);

		/* When it is not possible to pop more commands from queue, then break
		 * while loop */
		if(cmd == NULL) {
			break;
		}

		/* Is this command fake command? */
		if(cmd->id < MIN_CMD_ID) {
			if(cmd->id == FAKE_CMD_CONNECT_TERMINATE) {
				struct VS_CTX *vs_ctx = CTX_server_ctx(C);
				if(vs_ctx != NULL) {
					vconn->host_state = UDP_SERVER_STATE_CLOSEREQ;
				} else {
					vconn->host_state = UDP_CLIENT_STATE_CLOSING;
				}
			} else if(cmd->id == FAKE_CMD_FPS) {
				struct Fps_Cmd *fps_cmd = (struct Fps_Cmd*)cmd;
				/* Change value of FPS. It will be sent in negotiate command
				 * until it is confirmed be the peer (server) */
				vsession->fps_host = fps_cmd->fps;
			}
			v_cmd_destroy(&cmd);
		} else {

			/* What was size of command in queue */
			cmd_size = v_cmd_size(cmd);

			if(!(buffer_pos < (vconn->io_ctx.mtu - cmd_size))) {
				/* When there is not enough space for other command,
				 * then push command back to the beginning of queue. */
				v_out_queue_push_head(vsession->out_queue, prio, cmd);
				break;
			} else {

				/* Update total size of commands that were poped from queue */
				*tot_cmd_size += cmd_size;

				/* When compression is not allowed, then add this command as is */
				if( vconn->host_cmd_cmpr == CMPR_NONE) {
					cmd_count = 0;
					cmd_len = cmd_size;
					/* Debug print */
					v_print_log(VRS_PRINT_DEBUG_MSG, "Cmd: %d, count: %d, length: %d\n",
							cmd->id, cmd_count, cmd_len);
					/* Add command to the buffer */
					buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, cmd_len, 0);
				} else {
					/* When command compression is allowed and was ID of command changed? */
					if( (cmd->id != last_cmd_id) || (last_cmd_count <= 0) )	{
						/* When this command is alone, then use default command size */
						if(cmd_count == 0) {
							cmd_len = cmd_size;
						} else {
							/* FIXME: do not recompute command length here, but do it right,
							 * when command is added to the queue */
							cmd_len = v_cmds_len(cmd, cmd_count, cmd_share, cmd_len);
						}
						/* Debug print */
						v_print_log(VRS_PRINT_DEBUG_MSG, "Cmd: %d, count: %d, length: %d\n",
								cmd->id, cmd_count, cmd_len);
						/* Add command to the buffer */
						buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, cmd_len, cmd_share);
						/* Set up current count of commands in the line */
						last_cmd_count = cmd_count;
						/* Update summary of commands length */
						sum_len += cmd_len;
					} else {
						buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, 0, cmd_share);
					}
				}

				/* Print command */
				v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd);

				/* TODO: remove command alias here (layer value set/unset) */

				/* Add command to the packet history */
				ret = v_packet_history_add_cmd(&vconn->packet_history, sent_packet, cmd, prio);
				assert(ret==1);

				/* Update last command id */
				last_cmd_id = cmd->id;
				/* Decrement counter of commands in queue */
				last_cmd_count--;
			}
		}
	}

	return buffer_pos;
}
Beispiel #15
0
/**
 * \brief This function removes packet with id from history of sent packets.
 * It removes all its commands from the command history too.
 *
 * \param[in]	*C		The verse context.
 * \param[in]	id		The ID of packet, the will be removed from the history.
 *
 * \return		This function returns 1, then packet with id was found in the history
 * and it returns 0 otherwise.
 */
int v_packet_history_rem_packet(struct vContext *C, uint32 id)
{
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct VPacket_History *history = &dgram_conn->packet_history;
	struct VPacket *r_packet = CTX_r_packet(C);
	struct VSent_Packet *sent_packet;
	int ret = 0, is_fake_cmd_received = 0;

	sent_packet = v_packet_history_find_packet(history, id);

	if(sent_packet != NULL) {
		struct VSent_Command *sent_cmd;

		v_print_log(VRS_PRINT_DEBUG_MSG, "Removing packet: %d from history\n", sent_packet->id);

		/* Go through the whole list of sent commands and free them from the
		 * hashed linked list */
		sent_cmd = sent_packet->cmds.first;
		while(sent_cmd != NULL) {
			/* Remove own command from hashed linked list if it wasn't already
			 * removed, when command was obsoleted by some newer packet */
			if(sent_cmd->vbucket!=NULL) {
				struct Generic_Cmd *cmd = (struct Generic_Cmd*)sent_cmd->vbucket->data;

				/* Bucket has to include some data */
				assert(sent_cmd->vbucket->data!=NULL);

				/* Decrease total size of commands that were sent and wasn't acknowladged yet*/
				dgram_conn->sent_size -= v_cmd_size(cmd);

				/* Put fake command for create/destroy commands at verse server */
				if(vs_ctx != NULL) {
					struct VSession *vsession = CTX_current_session(C);
					struct Generic_Cmd *fake_cmd = NULL;

					switch(cmd->id) {
					case CMD_NODE_CREATE:
						fake_cmd = v_fake_node_create_ack_create(UINT32(cmd->data[UINT16_SIZE + UINT32_SIZE]));
						break;
					case CMD_NODE_DESTROY:
						fake_cmd = v_fake_node_destroy_ack_create(UINT32(cmd->data[0]));
						break;
					case CMD_TAGGROUP_CREATE:
						fake_cmd = v_fake_taggroup_create_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]));
						break;
					case CMD_TAGGROUP_DESTROY:
						fake_cmd = v_fake_taggroup_destroy_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]));
						break;
					case CMD_TAG_CREATE:
						fake_cmd = v_tag_create_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]),
								UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE]));
						break;
					case CMD_TAG_DESTROY:
						fake_cmd = v_tag_destroy_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]),
								UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE]));
						break;
					case CMD_LAYER_CREATE:
						fake_cmd = v_fake_layer_create_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE]));
						break;
					case CMD_LAYER_DESTROY:
						fake_cmd = v_fake_layer_destroy_ack_create(UINT32(cmd->data[0]),
								UINT16(cmd->data[UINT32_SIZE]));
						break;
					default:
						break;
					}

					if(fake_cmd != NULL) {
						is_fake_cmd_received = 1;
						/* Push command to the queue of incomming commands */
						v_in_queue_push(vsession->in_queue, fake_cmd);
						/* Print content of fake command */
						v_fake_cmd_print(VRS_PRINT_DEBUG_MSG, fake_cmd);
					}
				}

				/* Remove command from the history of sent commands */
				ret = v_hash_array_remove_item(&history->cmd_hist[sent_cmd->id]->cmds, sent_cmd->vbucket->data);

				if(ret == 1) {
					/* Destroy command */
					v_cmd_destroy(&cmd);
				} else {
					v_print_log(VRS_PRINT_ERROR, "Unable to remove command id: %d\n", sent_cmd->id);
					ret = 0;
				}
			}
			sent_cmd = sent_cmd->next;
		}

		/* Free linked list of sent commands */
		v_list_free(&sent_packet->cmds);

		/* Remove packet itself from the linked list of sent packet */
		v_list_rem_item(&history->packets, sent_packet);
		free(sent_packet);

		ret = 1;
	} else {
		/* When acknowledged payload packet is not found in history, then it
		 * is OK, because it is probably keep alive packet without any node
		 * commands */
		v_print_log(VRS_PRINT_DEBUG_MSG, "Packet with id: %d, not found in history\n", id);
	}

	/* When pure ack packet caused adding some fake commands to the queue, then
	 * poke server data thread */
	if( (vs_ctx != NULL) && (is_fake_cmd_received == 1) && (r_packet->data == NULL)) {
		sem_post(vs_ctx->data.sem);
	}

	return ret;
}
Beispiel #16
0
/**
 * \brief WebSocket callback function for recived message
 */
void vs_ws_recv_msg_callback(wslay_event_context_ptr ctx,
		const struct wslay_event_on_msg_recv_arg *arg,
		void *user_data)
{
	struct vContext *C = (struct vContext*)user_data;
	struct VSession *session = CTX_current_session(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);

	(void)ctx;

	if(!wslay_is_ctrl_frame(arg->opcode)) {
		/* Verse server uses binary message for communication */
		if(arg->opcode == WSLAY_BINARY_FRAME) {
		    struct wslay_event_msg msgarg;

			v_print_log(VRS_PRINT_DEBUG_MSG,
					"WS Callback received binary message\n");

			/* Copy received data to IO context */
			memcpy(io_ctx->buf,	arg->msg, arg->msg_length);
			io_ctx->buf_size = arg->msg_length;

			if(session->stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) {
				if(v_STREAM_handle_messages(C) == 0) {
					/* TODO: end connection */
					return;
				}
			} else {

				if( vs_handle_handshake(C, NULL) == -1 ) {
					/* TODO: end connection */
					return;
				}

			    msgarg.opcode = WSLAY_BINARY_FRAME;
			    msgarg.msg = (uint8_t*)io_ctx->buf;
			    msgarg.msg_length = io_ctx->buf_size;
			    wslay_event_queue_msg(ctx, &msgarg);
			}

		} else if(arg->opcode == WSLAY_TEXT_FRAME) {
			v_print_log(VRS_PRINT_ERROR, "WebSocket text frame is not supported\n");
			return;
		}

	} else {
		/* Print opcode of control message */
		v_print_log(VRS_PRINT_DEBUG_MSG,
				"WS Callback Received Ctrl Message: opcode: %d\n",
				arg->opcode);
		/* Is it closing message? */
		if(arg->opcode & WSLAY_CONNECTION_CLOSE) {
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Close message with code: %d, message: %s\n",
					arg->status_code,
					arg->msg);
			if(session->stream_conn->host_state == TCP_SERVER_STATE_CLOSING) {
				session->stream_conn->host_state = TCP_SERVER_STATE_CLOSED;
			} else {
				session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;
				wslay_event_queue_close(ctx,
						WSLAY_CODE_NORMAL_CLOSURE,
						(uint8_t*)"Closing connection",
						15);
			}
		}
	}

}
Beispiel #17
0
/**
 * \brief The function with websocket infinite loop
 */
void *vs_websocket_loop(void *arg)
{
	/* The vContext is passed as *user_data* in callback functions. */
	struct vContext *C = (struct vContext*)arg;
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VStreamConn *stream_conn = CTX_current_stream_conn(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VMessage *r_message=NULL, *s_message=NULL;
	wslay_event_context_ptr wslay_ctx;
	fd_set read_set, write_set;
	struct timeval tv;
	int ret, flags;
	unsigned int int_size;

	struct wslay_event_callbacks callbacks = {
			vs_recv_ws_callback_data,
			vs_send_ws_callback_data,
			NULL,
			NULL,
			NULL,
			NULL,
			vs_ws_recv_msg_callback
	};

	/* Set socket blocking */
	flags = fcntl(io_ctx->sockfd, F_GETFL, 0);
	fcntl(io_ctx->sockfd, F_SETFL, flags & ~O_NONBLOCK);

	http_handshake(io_ctx->sockfd);

	/* Try to get size of TCP buffer */
	int_size = sizeof(int_size);
	getsockopt(io_ctx->sockfd, SOL_SOCKET, SO_RCVBUF,
			(void *)&stream_conn->socket_buffer_size, &int_size);

	r_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));
	s_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));
	CTX_r_message_set(C, r_message);
	CTX_s_message_set(C, s_message);

	stream_conn->host_state = TCP_SERVER_STATE_RESPOND_METHODS;

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: RESPOND_methods\n");
		printf("%c[%dm", 27, 0);
	}

	/* Set non-blocking */
	flags = fcntl(io_ctx->sockfd, F_GETFL, 0);
	fcntl(io_ctx->sockfd, F_SETFL, flags | O_NONBLOCK);

	wslay_event_context_server_init(&wslay_ctx, &callbacks, C);

	/* "Never ending" loop */
	while(vsession->stream_conn->host_state != TCP_SERVER_STATE_CLOSED)
	{
		if(vs_ctx->state != SERVER_STATE_READY) {
			vsession->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;
			/* Try to close connection with websocket client */
			wslay_event_queue_close(wslay_ctx,
					WSLAY_CODE_GOING_AWAY,
					(uint8_t*)"Server shutdown",	/* Close message */
					15);	/* The length of close message s*/
		}

		/* Initialize read set */
		FD_ZERO(&read_set);
		FD_SET(io_ctx->sockfd, &read_set);

		/* Initialize write set */
		FD_ZERO(&write_set);
		FD_SET(io_ctx->sockfd, &write_set);

		/* Set timeout for select() */
		if(stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) {
			/* Use negotiated FPS */
			tv.tv_sec = 0;
			tv.tv_usec = 1000000/vsession->fps_host;
		} else {
			/* User have to send something in 30 seconds */
			tv.tv_sec = VRS_TIMEOUT;
			tv.tv_usec = 0;
		}

		if( (ret = select(io_ctx->sockfd+1,
				&read_set,
				&write_set,
				NULL,			/* Exception*/
				&tv)) == -1) {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "%s:%s():%d select(): %s\n",
					__FILE__, __FUNCTION__,  __LINE__, strerror(errno));
			goto end;
			/* Was event on the listen socket */
		} else if(ret>0){
			if(FD_ISSET(io_ctx->sockfd, &read_set)) {
				if( wslay_event_recv(wslay_ctx) != 0 ) {
					goto end;
				}
			} else if (FD_ISSET(io_ctx->sockfd, &write_set)) {
				if( wslay_event_send(wslay_ctx) != 0 ) {
					goto end;
				}
			}
		}

		if(stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) {
			/* Check if there is any command in outgouing queue
			 * and eventually pack these commands to buffer */
			if((ret = v_STREAM_pack_message(C)) == 0 ) {
				goto end;
			}

			/* When at least one command was packed to buffer, then
			 * queue this buffer to WebSocket layer */
			if(ret == 1) {
				struct wslay_event_msg msgarg;
			    msgarg.opcode = WSLAY_BINARY_FRAME;
			    msgarg.msg = (uint8_t*)io_ctx->buf;
			    msgarg.msg_length = io_ctx->buf_size;
			    wslay_event_queue_msg(wslay_ctx, &msgarg);
			}
		}
	}

end:
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSING\n");
		printf("%c[%dm", 27, 0);
	}

	/* Set up TCP CLOSING state (non-blocking) */
	vs_CLOSING(C);

	/* Receive and Send messages are not neccessary any more */
	if(r_message!=NULL) {
		free(r_message);
		r_message = NULL;
		CTX_r_message_set(C, NULL);
	}
	if(s_message!=NULL) {
		free(s_message);
		s_message = NULL;
		CTX_s_message_set(C, NULL);
	}

	/* TCP connection is considered as CLOSED, but it is not possible to use
	 * this connection for other client */
	stream_conn->host_state = TCP_SERVER_STATE_CLOSED;

	/* NULL pointer at stream connection */
	CTX_current_stream_conn_set(C, NULL);

	/* Set TCP connection to CLOSED */
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSED\n");
		printf("%c[%dm", 27, 0);
	}


	pthread_mutex_lock(&vs_ctx->data.mutex);
	/* Unsubscribe this session (this avatar) from all nodes */
	vs_node_free_avatar_reference(vs_ctx, vsession);
	/* Try to destroy avatar node */
	vs_node_destroy_avatar_node(vs_ctx, vsession);
	pthread_mutex_unlock(&vs_ctx->data.mutex);

	/* This session could be used again for authentication */
	stream_conn->host_state=TCP_SERVER_STATE_LISTEN;

	/* Clear session flags */
	vsession->flags = 0;

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: LISTEN\n");
		printf("%c[%dm", 27, 0);
	}

	free(C);
	C = NULL;

	pthread_exit(NULL);
	return NULL;
}
Beispiel #18
0
/**
 * \brief This function handles messages received during verse handshake
 * and it can create new thread for datagram connection.
 */
int vs_handle_handshake(struct vContext *C, char *u_name)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VStreamConn *stream_conn = CTX_current_stream_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	int ret;

	/* Make sure, that buffer contains at least Verse message
	 * header. If this condition is not reached, then somebody tries
	 * to do some very bad things! .. Close this connection. */
	if(io_ctx->buf_size < VERSE_MESSAGE_HEADER_SIZE) {
		if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "received buffer too small %d\n",
				io_ctx->buf_size);
		return -1;
	}

	switch(stream_conn->host_state) {
	case TCP_SERVER_STATE_RESPOND_METHODS:
		ret = vs_RESPOND_methods_loop(C);
		if(ret==1) {
			stream_conn->host_state = TCP_SERVER_STATE_RESPOND_USRAUTH;
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 31);
				v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: RESPOND_userauth\n");
				printf("%c[%dm", 27, 0);
			}
		} else {
			return -1;
		}
		break;
	case TCP_SERVER_STATE_RESPOND_USRAUTH:
		ret = vs_RESPOND_userauth_loop(C);
		if(ret==1) {
			stream_conn->host_state = TCP_SERVER_STATE_NEGOTIATE_COOKIE_DED;

			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 31);
				v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: NEGOTIATE_cookie_ded\n");
				printf("%c[%dm", 27, 0);
			}
		} else {
			vsession->usr_auth_att++;
			if(vsession->usr_auth_att >= MAX_USER_AUTH_ATTEMPTS) {
				return -1;
			}
		}
		break;
#ifdef WITH_KERBEROS
	case TCP_SERVER_STATE_RESPOND_KRB_AUTH:
		ret = vs_RESPOND_krb_auth_loop(C, u_name);
		if (ret == 1) {
			stream_conn->host_state = TCP_SERVER_STATE_NEGOTIATE_COOKIE_DED;

			if (is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 31);
				v_print_log(VRS_PRINT_DEBUG_MSG,
						"Server TCP state: NEGOTIATE_cookie_ded\n");
				printf("%c[%dm", 27, 0);
			}
		} else {
			return -1;
		}
		break;
#endif
	case TCP_SERVER_STATE_NEGOTIATE_COOKIE_DED:
		ret = vs_NEGOTIATE_cookie_ded_loop(C);
		if(ret==1) {
			stream_conn->host_state = TCP_SERVER_STATE_NEGOTIATE_NEWHOST;

			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 31);
				v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: NEGOTIATE_newhost\n");
				printf("%c[%dm", 27, 0);
			}
		} else {
			return -1;
		}
		break;
	case TCP_SERVER_STATE_NEGOTIATE_NEWHOST:
		/* Wait VERSE_TIMEOT seconds to confirming proposed URL */
		ret = vs_NEGOTIATE_newhost_loop(C);
		if(ret == 1) {
			if(vsession->flags & VRS_TP_UDP) {
				/* When URL was confirmed, then go to the end state */
				return -1;
			} else if(vsession->flags & VRS_TP_TCP) {
				stream_conn->host_state = TCP_SERVER_STATE_STREAM_OPEN;

				if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
					printf("%c[%d;%dm", 27, 1, 31);
					v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: STREAM_OPEN\n");
					printf("%c[%dm", 27, 0);
				}

				vs_STREAM_OPEN_tcp_loop(C);
				return -1;
			} else if(vsession->flags & VRS_TP_WEBSOCKET) {
				stream_conn->host_state = TCP_SERVER_STATE_STREAM_OPEN;

				if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
					printf("%c[%d;%dm", 27, 1, 31);
					v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: STREAM_OPEN\n");
					printf("%c[%dm", 27, 0);
				}
			}
		} else {
			/* When thread was not confirmed, then try to cancel
			 * UDP thread*/
			if(pthread_cancel(vsession->udp_thread) != 0) {
				v_print_log(VRS_PRINT_DEBUG_MSG, "UDP thread was not canceled\n");
			}
			return -1;
		}
		break;

	}

	return 1;
}
Beispiel #19
0
/* Create new UDP connection to the server */
struct VDgramConn *vc_create_client_dgram_conn(struct vContext *C)
{
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = NULL;
	struct addrinfo hints, *result, *rp;
	int sockfd = -1;
	int flag, ret;
	struct VURL url;

	/* Seed random number generator,  */
#ifdef __APPLE__
	sranddev();
	/* Other BSD based systems probably support this or similar function too. */
#else
	/* Other systems have to use this evil trick */
	struct timeval tv;
	gettimeofday(&tv, NULL);
	srand(tv.tv_sec - tv.tv_usec);
#endif

	if (v_url_parse(vsession->host_url, &url) != 1) {
		goto end;
	} else {
		/* v_print_url(VRS_PRINT_DEBUG_MSG, &url); */
	}

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;		/* Allow IPv4 or IPv6 */
	hints.ai_socktype = SOCK_DGRAM;		/* Allow datagram protocol */
	hints.ai_flags = 0;					/* No flags required */
	hints.ai_protocol = IPPROTO_UDP;	/* Allow UDP protocol only */

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		if(url.ip_ver==IPV6) {
			v_print_log(VRS_PRINT_DEBUG_MSG,
						"Try to connect to: [%s]:%s\n",
						url.node, url.service);
		} else {
			v_print_log(VRS_PRINT_DEBUG_MSG,
						"Try to connect to: %s:%s\n",
						url.node, url.service);
		}
	}

	if( (ret = getaddrinfo(url.node, url.service, &hints, &result)) !=0 ) {
		v_print_log(VRS_PRINT_ERROR,
					"getaddrinfo(): %s\n", gai_strerror(ret));
		goto end;
	}

	/* Try to use addrinfo from getaddrinfo() */
	for(rp=result; rp!=NULL; rp=rp->ai_next) {
		if( (sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) {
			v_print_log(VRS_PRINT_ERROR, "socket(): %s\n", strerror(errno));
			continue;
		}
		else {
			/* Try to "connect" to this address ... the client will be able to send and
			 * receive packets only from this address. */
			if(connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1)
				break;
			close(sockfd);
			sockfd = -1;
		}
	}

	if(rp == NULL) {
		if(is_log_level(VRS_PRINT_ERROR)) {
			if(url.ip_ver==IPV6) {
				v_print_log(VRS_PRINT_ERROR,
							"Could not connect to the [%s]:%s\n",
							url.node, url.service);
			} else {
				v_print_log(VRS_PRINT_ERROR,
							"Could not connect to the %s:%s\n",
							url.node, url.service);
			}
		}
		freeaddrinfo(result);
		goto end;
	}

	if( (dgram_conn = (struct VDgramConn*)calloc(1, sizeof(struct VDgramConn))) == NULL) {
		v_print_log(VRS_PRINT_ERROR, "calloc(): %s\n", strerror(errno));
		freeaddrinfo(result);
		if(sockfd != -1) {
			close(sockfd);
		}
		goto end;
	}

	/* Initialize datagram connection */
	v_conn_dgram_init(dgram_conn);

	/* Use first successfully assigned socket */
	dgram_conn->io_ctx.sockfd = sockfd;

	/* Set socket non-blocking */
	flag = fcntl(dgram_conn->io_ctx.sockfd, F_GETFL, 0);
	if( (fcntl(dgram_conn->io_ctx.sockfd, F_SETFL, flag | O_NONBLOCK)) == -1) {
		v_print_log(VRS_PRINT_ERROR, "fcntl(): %s\n", strerror(errno));
		free(dgram_conn);
		dgram_conn = NULL;
		if(sockfd != -1) {
			close(sockfd);
		}
		freeaddrinfo(result);
		goto end;
	}

	/* Set socket to reuse address */
	flag = 1;
	if( setsockopt(dgram_conn->io_ctx.sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
		v_print_log(VRS_PRINT_ERROR, "setsockopt(): %s\n", strerror(errno));
		free(dgram_conn);
		if(sockfd != -1) {
			close(sockfd);
		}
		dgram_conn = NULL;
		goto end;
	}

	/* Set address of peer and host */
	if(rp->ai_family==AF_INET) {
		/* Address type of client */
		dgram_conn->io_ctx.host_addr.ip_ver = IPV4;
		dgram_conn->io_ctx.host_addr.protocol = UDP;
		/* Address of peer */
		dgram_conn->io_ctx.peer_addr.ip_ver = IPV4;
		dgram_conn->io_ctx.peer_addr.protocol = UDP;
		dgram_conn->io_ctx.peer_addr.port = ntohs(((struct sockaddr_in*)rp->ai_addr)->sin_port);
		memcpy(&dgram_conn->io_ctx.peer_addr.addr.ipv4, rp->ai_addr, rp->ai_addrlen);
		/* Address of peer (reference in connection) */
		dgram_conn->peer_address.ip_ver = IPV4;
		dgram_conn->peer_address.protocol = UDP;
		dgram_conn->peer_address.port = ntohs(((struct sockaddr_in*)rp->ai_addr)->sin_port);
		memcpy(&dgram_conn->peer_address.addr.ipv4, rp->ai_addr, rp->ai_addrlen);
	}
	else if(rp->ai_family==AF_INET6) {
		/* Address type of client */
		dgram_conn->io_ctx.host_addr.ip_ver = IPV6;
		dgram_conn->io_ctx.host_addr.protocol = UDP;
		/* Address of peer */
		dgram_conn->io_ctx.peer_addr.ip_ver = IPV6;
		dgram_conn->io_ctx.peer_addr.protocol = UDP;
		dgram_conn->io_ctx.peer_addr.port = ntohs(((struct sockaddr_in6*)rp->ai_addr)->sin6_port);
		memcpy(&dgram_conn->io_ctx.peer_addr.addr.ipv6, rp->ai_addr, rp->ai_addrlen);
		/* Address of peer (reference in connection) */
		dgram_conn->peer_address.ip_ver = IPV6;
		dgram_conn->peer_address.protocol = UDP;
		dgram_conn->peer_address.port = ntohs(((struct sockaddr_in6*)rp->ai_addr)->sin6_port);
		memcpy(&dgram_conn->peer_address.addr.ipv6, rp->ai_addr, rp->ai_addrlen);
	}

	freeaddrinfo(result);

	/* When DTLS was negotiated, then set flag */
	if(url.security_protocol == VRS_SEC_DATA_TLS) {
#if (defined WITH_OPENSSL) && OPENSSL_VERSION_NUMBER>=0x10000000
		dgram_conn->io_ctx.flags |= SOCKET_SECURED;
#else
		v_print_log(VRS_PRINT_ERROR,
					"Server tries to force client to use secured connection, but it is not supported\n");
		goto end;
#endif
	}

#ifdef WITH_OPENSSL
	/* Create BIO, connect and set to already connected */
	if( (dgram_conn->io_ctx.bio = BIO_new_dgram(dgram_conn->io_ctx.sockfd, BIO_CLOSE)) == NULL) {
		v_print_log(VRS_PRINT_ERROR, "BIO_new_dgram()\n");
		goto end;
	}

	/* Try to do PMTU discovery */
	if( BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL) < 0) {
		v_print_log(VRS_PRINT_ERROR, "BIO_ctrl()\n");
		goto end;
	}

	/* Try to get MTU from the bio */
	ret = BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
	if(ret > 0) {
		dgram_conn->io_ctx.mtu = ret;
		v_print_log(VRS_PRINT_DEBUG_MSG, "PMTU: %d\n", dgram_conn->io_ctx.mtu);
	} else {
		dgram_conn->io_ctx.mtu = DEFAULT_MTU;
		v_print_log(VRS_PRINT_DEBUG_MSG, "Default MTU: %d\n", dgram_conn->io_ctx.mtu);
	}
#else
	dgram_conn->io_ctx.mtu = DEFAULT_MTU;
#endif

	/* Set up necessary flag for V_CTX (client will be able to send and receive packets only to/from server) */
	dgram_conn->io_ctx.flags |= SOCKET_CONNECTED;

	dgram_conn->host_id = (unsigned int)rand();

end:
	v_url_clear(&url);
	
	return dgram_conn;
}
Beispiel #20
0
/**
 * \brief This function is never ending loop of server state STREAM_OPEN.
 * This loop is used, when Verse server uses TCP for data exchange (not
 * UDP nor WebSocket)
 */
int vs_STREAM_OPEN_tcp_loop(struct vContext *C)
{
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct timeval tv;
	fd_set set;
	int flag, ret, error;

	/* Set socket non-blocking */
	flag = fcntl(io_ctx->sockfd, F_GETFL, 0);
	if( (fcntl(io_ctx->sockfd, F_SETFL, flag | O_NONBLOCK)) == -1) {
		if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "fcntl(): %s\n", strerror(errno));
		return -1;
	}

	/* "Never ending" loop */
	while(1)
	{
		FD_ZERO(&set);
		FD_SET(io_ctx->sockfd, &set);

		/* Use negotiated FPS */
		tv.tv_sec = 0;
		tv.tv_usec = 1000000/vsession->fps_host;

		/* Wait for recieved data */
		if( (ret = select(io_ctx->sockfd+1, &set, NULL, NULL, &tv)) == -1) {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "%s:%s():%d select(): %s\n",
					__FILE__, __FUNCTION__,  __LINE__, strerror(errno));
			goto end;
			/* Was event on the listen socket */
		} else if(ret>0 && FD_ISSET(io_ctx->sockfd, &set)) {

			/* Try to receive data through SSL connection */
			if( v_tcp_read(io_ctx, &error) <= 0 ) {
				goto end;
			}

			if(v_STREAM_handle_messages(C) == 0) {
				goto end;
			}

			/* When some payload data were received, then poke data thread */
			sem_post(vs_ctx->data.sem);
		}

		if( (ret = v_STREAM_pack_message(C)) == 0 ) {
			goto end;
		}

		/* Send command to the client */
		if(ret == 1) {
			if( v_tcp_write(io_ctx, &error) <= 0) {
				goto end;
			}
		}
	}
end:

	/* Set socket blocking again */
	flag = fcntl(io_ctx->sockfd, F_GETFL, 0);
	if( (fcntl(io_ctx->sockfd, F_SETFL, flag & ~O_NONBLOCK)) == -1) {
		if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "fcntl(): %s\n", strerror(errno));
		return -1;
	}

	return 0;
}
Beispiel #21
0
/**
 * \brief This function is the callback function for received Confirm_L
 * commands in REQUEST state.
 */
static int vc_REQUEST_CONFIRM_L_cb(struct vContext *C, struct Generic_Cmd *cmd)
{
	struct VC_CTX *vc_ctx = CTX_client_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C);
	struct Negotiate_Cmd *confirm_l_cmd = (struct Negotiate_Cmd*)cmd;

	if(confirm_l_cmd->feature == FTR_TOKEN) {
		/* This block of code checks if the server confirmed send token. */
		if( vsession->host_token.str != NULL &&
				confirm_l_cmd->count == 1 &&
				strcmp((char*)confirm_l_cmd->value[0].string8.str, vsession->host_token.str)==0 )
		{
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Local TOKEN: %s confirmed\n",
					confirm_l_cmd->value[0].string8.str);
			return 1;
		} else {
			v_print_log(VRS_PRINT_WARNING,
					"COOCKIE: %s not confirmed\n",
					vsession->peer_token.str);
			return 0;
		}
	}

	/* Server should confirm client proposal of Congestion Control (local) */
	if(confirm_l_cmd->feature == FTR_CC_ID) {
		/* TODO: better implementation */
		if(confirm_l_cmd->count == 1 &&	/* Any confirm command has to include only one value */
				confirm_l_cmd->value[0].uint8 == CC_NONE) {	/* list of supported methods */
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Local Congestion Control ID: %d confirmed\n",
					confirm_l_cmd->value[0].uint8);
			dgram_conn->cc_meth = CC_NONE;
			return 1;
		} else {
			v_print_log(VRS_PRINT_ERROR, "Unsupported Congestion Control\n");
			return 0;
		}
	}

	/* Server should confirm client proposal of Flow Control Window scale (local) */
	if(confirm_l_cmd->feature == FTR_RWIN_SCALE) {
		if(confirm_l_cmd->count == 1 ) {
			if(vc_ctx->rwin_scale == confirm_l_cmd->value[0].uint8) {
				v_print_log(VRS_PRINT_DEBUG_MSG,
						"Scale of host RWIN: %d confirmed\n", vc_ctx->rwin_scale);
				dgram_conn->rwin_host_scale = vc_ctx->rwin_scale;
				return 1;
			} else {
				v_print_log(VRS_PRINT_ERROR, "Scale of RWIN: %d != %d wasn't confirmed\n",
						vc_ctx->rwin_scale,
						confirm_l_cmd->value[0].uint8);
				dgram_conn->rwin_host_scale = 0;
				return 0;
			}
		} else {
			v_print_log(VRS_PRINT_ERROR, "One value of RWIN scale should to be confirmed\n");
			return 0;
		}
	}

	/* Server should confirm client proposal of command compression */
	if(confirm_l_cmd->feature == FTR_CMD_COMPRESS) {
		if(confirm_l_cmd->count == 1) {
			if(confirm_l_cmd->value[0].uint8 == CMPR_NONE ||
					confirm_l_cmd->value[0].uint8 == CMPR_ADDR_SHARE)
			{
				v_print_log(VRS_PRINT_DEBUG_MSG, "Local Command Compression: %d confirmed\n",
						confirm_l_cmd->value[0].uint8);
				dgram_conn->host_cmd_cmpr = confirm_l_cmd->value[0].uint8;
				return 1;
			} else {
				v_print_log(VRS_PRINT_ERROR, "Unsupported Command Compress\n");
				return 0;
			}
		}
	}

	/* Ignore unknown feature */
	return 1;
}
Beispiel #22
0
/**
 * \brief This function is called, when server is in NEGOTIATE_cookie_ded state
 *
 * This function can create new thread for datagram connection
 */
int vs_NEGOTIATE_cookie_ded_loop(struct vContext *C)
{
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VMessage *r_message = CTX_r_message(C);
	struct VMessage *s_message = CTX_s_message(C);
	int i, j, ret;
	unsigned short buffer_pos = 0;
	int host_url_proposed = 0,
			host_cookie_proposed = 0,
			peer_cookie_confirmed = 0,
			ded_confirmed = 0,
			client_name_proposed = 0,
			client_version_proposed = 0;
	struct timeval tv;
	struct VURL url;

	/* Reset content of received message */
	memset(r_message, 0, sizeof(struct VMessage));

	/* Unpack Verse message header */
	buffer_pos += v_unpack_message_header(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	/* Unpack all system commands */
	buffer_pos += v_unpack_message_system_commands(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message);

	v_print_receive_message(C);

	/* Process all received system commands */
	for(i=0;
			i<MAX_SYSTEM_COMMAND_COUNT &&
			r_message->sys_cmd[i].cmd.id!=CMD_RESERVED_ID;
			i++)
	{
		switch(r_message->sys_cmd[i].cmd.id) {
		case CMD_CHANGE_R_ID:
			/* Client has to propose url in this state */
			if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_HOST_URL) {
				if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
					if(vsession->host_url!=NULL) {
						free(vsession->host_url);
					}
					/* Only first proposed URL will be used */
					vsession->host_url = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
					/* Check if proposed URL is correct */
					ret = v_parse_url(vsession->host_url, &url);
					if(ret == 1)
						host_url_proposed = 1;
					else
						host_url_proposed = 0;
				}
			/* Client has to propose host cookie in this state */
			} else if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_COOKIE) {
				if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
					if(vsession->host_cookie.str != NULL) {
						free(vsession->host_cookie.str);
					}
					vsession->host_cookie.str = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
					host_cookie_proposed = 1;
				}
			} else {
				v_print_log(VRS_PRINT_WARNING, "This feature id: %d is not supported in this state\n",
						r_message->sys_cmd[i].negotiate_cmd.feature);
			}
			break;
		case CMD_CHANGE_L_ID:
			/* Client could propose client name and version */
			if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_NAME) {
				if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
					/* Only first proposed client name will be used */
					vsession->client_name = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
					client_name_proposed = 1;
				}
			} else if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_VERSION) {
				if(r_message->sys_cmd[i].negotiate_cmd.count > 0) {
					/* Only first proposed client name will be used */
					vsession->client_version = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
					client_version_proposed = 1;
				}
			}
			break;
		case CMD_CONFIRM_R_ID:
			/* Client has to confirm peer cookie in this state */
			if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_COOKIE) {
				if (r_message->sys_cmd[i].negotiate_cmd.count == 1) {
					if(vsession->peer_cookie.str != NULL &&
						strcmp(vsession->peer_cookie.str, (char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str) == 0)
					{
						gettimeofday(&tv, NULL);
						vsession->peer_cookie.tv.tv_sec = tv.tv_sec;
						vsession->peer_cookie.tv.tv_usec = tv.tv_usec;
						peer_cookie_confirmed = 1;
					}
				}
			} else {
				v_print_log(VRS_PRINT_WARNING, "This feature id: %d is not supported in this state\n",
						r_message->sys_cmd[i].negotiate_cmd.feature);
			}
			break;
		case CMD_CONFIRM_L_ID:
			/* Client has to confirm DED in this state */
			if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_DED) {
				if(r_message->sys_cmd[i].negotiate_cmd.count == 1) {
					if(vsession->ded.str != NULL &&
							strcmp(vsession->ded.str, (char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str) == 0)
					{
						ded_confirmed = 1;
					} else {
						printf("%s != %s\n", vsession->ded.str, (char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str);
					}
				}
			}
			break;
		default:
			v_print_log(VRS_PRINT_WARNING, "This command id: %d is not supported in this state\n",
					r_message->sys_cmd[i].cmd.id);
			break;
		}
	}


	/* Send response on cookie request */
	if(host_url_proposed==1 &&
			host_cookie_proposed==1 &&
			peer_cookie_confirmed==1 &&
			ded_confirmed==1)
	{
		struct vContext *new_C;
		char trans_proto[4];
		char sec_proto[5];
		int cmd_rank = 0;

		buffer_pos = VERSE_MESSAGE_HEADER_SIZE;

		/* Copy address of peer */
		memcpy(&vsession->peer_address, &io_ctx->peer_addr, sizeof(struct VNetworkAddress));

		/* Do not confirm proposed URL */
		v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_R_ID, FTR_HOST_URL, NULL);

		/* Find first unused port from port range */
		for(i=vs_ctx->port_low, j=0; i<vs_ctx->port_high; i++, j++) {
			if(!(vs_ctx->port_list[j].flag & SERVER_PORT_USED)) {
				vsession->dgram_conn->io_ctx.host_addr.port = vs_ctx->port_list[j].port_number;
				vs_ctx->port_list[j].flag |= SERVER_PORT_USED;
				break;
			}
		}

		/* Do not allow unsecure TCP data connection */
		if(url.transport_protocol == VRS_TP_TCP) {
			url.security_protocol = VRS_SEC_DATA_TLS;
		}

		/* Copy settings about data connection to the session */
		vsession->flags |= url.security_protocol;
		vsession->flags |= url.transport_protocol;

		if(vsession->flags & VRS_TP_UDP) {
			strncpy(trans_proto, "udp", 3);
			trans_proto[3] = '\0';

			/* Create copy of new Verse context for new thread */
			new_C = (struct vContext*)calloc(1, sizeof(struct vContext));
			memcpy(new_C, C, sizeof(struct vContext));

			/* Try to create new thread */
			if((ret = pthread_create(&vsession->udp_thread, NULL, vs_main_dgram_loop, (void*)new_C)) != 0) {
				if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "pthread_create(): %s\n", strerror(errno));
				ret = 0;
				goto end;
			}

			/* Wait for datagram thread to be in LISTEN state */
			while(vsession->dgram_conn->host_state != UDP_SERVER_STATE_LISTEN) {
				/* Sleep 1 milisecond */
				usleep(1000);
			}
		} else if(vsession->flags & VRS_TP_TCP) {
			strncpy(trans_proto, "tcp", 3);
			trans_proto[3] = '\0';
		} else if(vsession->flags & VRS_TP_WEBSOCKET) {
			strncpy(trans_proto, "wss", 3);
			trans_proto[3] = '\0';
		}

#if (defined WITH_OPENSSL) && OPENSSL_VERSION_NUMBER>=0x10000000
		if(url.security_protocol==VRS_SEC_DATA_NONE ||
				!(vs_ctx->security_protocol & VRS_SEC_DATA_TLS))
		{
			strncpy(sec_proto, "none", 4);
			sec_proto[4] = '\0';
		} else if(url.security_protocol==VRS_SEC_DATA_TLS) {
			if(vsession->flags & VRS_TP_UDP) {
				strncpy(sec_proto, "dtls", 4);
				sec_proto[4] = '\0';
			} else if((vsession->flags & VRS_TP_TCP) ||
					(vsession->flags & VRS_TP_WEBSOCKET))
			{
				strncpy(sec_proto, "tls", 3);
				sec_proto[3] = '\0';
			}
		} else {
			strncpy(sec_proto, "none", 4);
			sec_proto[4] = '\0';
		}
#else
		strncpy(sec_proto, "none", 4);
		sec_proto[4] = '\0';
#endif

		/* Free proposed and now obsolete URL */
		if(vsession->host_url != NULL) {
			free(vsession->host_url);
			vsession->host_url = NULL;
		}

		/* Set right host URL */
		vsession->host_url = calloc(UCHAR_MAX, sizeof(char));
		if(url.ip_ver==IPV6) {
			sprintf(vsession->host_url, "verse-%s-%s://[%s]:%d",
					trans_proto,
					sec_proto,
					url.node,
					vsession->dgram_conn->io_ctx.host_addr.port);
		} else {
			sprintf(vsession->host_url, "verse-%s-%s://%s:%d",
					trans_proto,
					sec_proto,
					url.node,
					vsession->dgram_conn->io_ctx.host_addr.port);
		}
		v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CHANGE_L_ID, FTR_HOST_URL, vsession->host_url, NULL);

		/* Set time for the host cookie */
		gettimeofday(&tv, NULL);
		vsession->host_cookie.tv.tv_sec = tv.tv_sec;
		vsession->host_cookie.tv.tv_usec = tv.tv_usec;

		/* Send confirmation about host cookie */
		v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_R_ID, FTR_COOKIE,
				vsession->host_cookie.str, NULL);

		/* Send confirmation about client name */
		if(client_name_proposed == 1) {
			v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_NAME,
					vsession->client_name, NULL);
		}

		/* Send confirmation about client version only in situation, when
		 * client proposed client name too */
		if(client_version_proposed == 1) {
			if(client_name_proposed == 1) {
				v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION,
						vsession->client_version, NULL);
			} else {
				/* Client version without client name is not allowed */
				v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION,
						NULL);
			}
		}

		/* Pack all system commands to the buffer */
		buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]);

		/* Update length of message in the header (data in buffer) */
		s_message->header.version = VRS_VERSION;
		s_message->header.len = io_ctx->buf_size = buffer_pos;

		/* Pack header to the beginning of the buffer */
		v_pack_message_header(s_message, io_ctx->buf);

		v_print_send_message(C);

		ret = 1;
	} else {
		ret = 0;
	}

end:
	v_clear_url(&url);

	return ret;
}
Beispiel #23
0
/**
 * \brief This function try to pack message that is going to be
 * sent in STREAM OPEN state
 *
 * \param[in] *C The pointer at context
 *
 * \return This function return 1, when there is something to send,
 * it returns -1, when there is nothing to send and it returns 0, when
 * there is some error
 */
int v_STREAM_pack_message(struct vContext *C)
{
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VStreamConn *conn = CTX_current_stream_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VMessage *s_message = CTX_s_message(C);
	struct Generic_Cmd *cmd, *fake_cmd;
	int ret = -1, queue_size = 0, buffer_pos = 0, prio_cmd_count, cmd_rank=0;
	int8 cmd_share;
	int16 prio, max_prio, min_prio;
	uint16 cmd_count, cmd_len, prio_win, swin, sent_size, tot_cmd_size;
	real32 prio_sum_high, prio_sum_low, r_prio;
	int is_fake_cmd_received = 0;

	/* Is here something to send? */
	if((v_out_queue_get_count(vsession->out_queue) > 0) ||
			(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS))
	{

		/* Get current size of data in TCP outgoing buffer */
#ifdef __linux__
		if( ioctl(io_ctx->sockfd, SIOCOUTQ, &queue_size) == -1 ) {
			v_print_log(VRS_PRINT_ERROR,
					"ioctl(%d, SIOCOUTQ, ...): %s\n",
					io_ctx->sockfd,
					strerror(errno));
			return 0;
		}
#endif

		/* Compute, how many data could be added to the TCP stack? */
		swin = conn->socket_buffer_size - queue_size;

		buffer_pos = VERSE_MESSAGE_HEADER_SIZE;

		s_message->sys_cmd[0].cmd.id = CMD_RESERVED_ID;

		/* When negotiated and used FPS is different, then pack negotiate command
		 * for FPS */
		if(vsession->fps_host != vsession->fps_peer) {
			cmd_rank += v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank,
					CMD_CHANGE_L_ID, FTR_FPS, &vsession->fps_host, NULL);
		} else {
			if(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS) {
				cmd_rank += v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank,
						CMD_CONFIRM_L_ID, FTR_FPS, &vsession->fps_peer, NULL);
				/* Send confirmation only once for received system command */
				vsession->tmp_flags &= ~SYS_CMD_NEGOTIATE_FPS;
			}
		}

		buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]);

		max_prio = v_out_queue_get_max_prio(vsession->out_queue);
		min_prio = v_out_queue_get_min_prio(vsession->out_queue);

		prio_sum_high = v_out_queue_get_prio_sum_high(vsession->out_queue);
		prio_sum_low = v_out_queue_get_prio_sum_low(vsession->out_queue);

		v_print_log(VRS_PRINT_DEBUG_MSG, "Packing prio queues, cmd count: %d\n",
				v_out_queue_get_count(vsession->out_queue));

		/* Go through all priorities and pick commands from priority queues */
		for(prio = max_prio; prio >= min_prio; prio--)
		{
			prio_cmd_count = v_out_queue_get_count_prio(vsession->out_queue, prio);

			if(prio_cmd_count > 0) {

				r_prio = v_out_queue_get_prio(vsession->out_queue, prio);

				/* Compute size of buffer that could be occupied by
				 * commands from this queue */
				if(prio >= VRS_DEFAULT_PRIORITY) {
					prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_high;
				} else {
					prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_low;
				}

				/* Debug print */
				v_print_log(VRS_PRINT_DEBUG_MSG, "Queue: %d, count: %d, r_prio: %6.3f, prio_win: %d\n",
						prio, prio_cmd_count, r_prio, prio_win);

				/* Get total size of commands that were stored in queue (sent_size) */
				sent_size = 0;
				tot_cmd_size = 0;

				while(prio_cmd_count > 0) {
					cmd_share = 0;
					cmd_count = 0;
					cmd_len = prio_win - sent_size;

					/* Pack commands from queues with high priority to the buffer */
					cmd = v_out_queue_pop(vsession->out_queue, prio, &cmd_count, &cmd_share, &cmd_len);
					if(cmd != NULL) {

						/* Is this command fake command? */
						if(cmd->id < MIN_CMD_ID) {
							if(cmd->id == FAKE_CMD_CONNECT_TERMINATE) {
								/* Close connection */
								struct VS_CTX *vs_ctx = CTX_server_ctx(C);
								if(vs_ctx != NULL) {
									vsession->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;
								} else {
									vsession->stream_conn->host_state = TCP_CLIENT_STATE_CLOSING;
								}
							} else if(cmd->id == FAKE_CMD_FPS) {
								struct Fps_Cmd *fps_cmd = (struct Fps_Cmd*)cmd;
								/* Change value of FPS. It will be sent in negotiate command
								 * until it is confirmed be the peer (server) */
								vsession->fps_host = fps_cmd->fps;
							}
						} else {
							buffer_pos += tot_cmd_size = v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, v_cmd_size(cmd), 0);
							if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
								printf("%c[%d;%dm", 27, 1, 32);
								v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd);
								printf("%c[%dm", 27, 0);
							}
							sent_size += tot_cmd_size;
						}

						fake_cmd = v_cmd_fake_ack(cmd);

						if(fake_cmd != NULL) {
							is_fake_cmd_received = 1;
							/* Push command to the queue of incoming commands */
							v_in_queue_push(vsession->in_queue, fake_cmd);
							/* Print content of fake command */
							v_fake_cmd_print(VRS_PRINT_DEBUG_MSG, fake_cmd);
						}

						/* It is not necessary to put cmd to history of sent commands,
						 * when TCP is used. */
						v_cmd_destroy(&cmd);
						prio_cmd_count--;
					} else {
						break;
					}
				}
			}
		}

		s_message->header.len = io_ctx->buf_size = buffer_pos;
		s_message->header.version = VRS_VERSION;

		/* Pack header to the beginning of the buffer */
		v_pack_message_header(s_message, io_ctx->buf);

		/* Debug print of command to be send */
		if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
			v_print_send_message(C);
		}

		/* When pure ack packet caused adding some fake commands to the queue, then
		 * poke server data thread */
		if( (vs_ctx != NULL) && (is_fake_cmd_received == 1)) {
			sem_post(vs_ctx->data.sem);
		}

		ret = 1;

	}

	return ret;
}
Beispiel #24
0
/**
 * \brief Main function for new thread. This thread is created for new
 * connection with client. This thread will try to authenticate new user
 * and negotiate new udp port. */
void *vs_tcp_conn_loop(void *arg)
{
	struct vContext *C = (struct vContext*)arg;
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VStreamConn *stream_conn = CTX_current_stream_conn(C);
	struct VMessage *r_message=NULL, *s_message=NULL;
	struct timeval tv;
	fd_set set;
	int error, ret;
	void *udp_thread_result;
	unsigned int int_size;

	/* Try to get size of TCP buffer */
	int_size = sizeof(int_size);
	getsockopt(io_ctx->sockfd, SOL_SOCKET, SO_RCVBUF,
			(void *)&stream_conn->socket_buffer_size, &int_size);

#ifdef WITH_OPENSSL
	/* Try to do TLS handshake with client */
	if(vs_TLS_handshake(C)!=1) {
		goto end;
	}
#endif

	r_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));
	s_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));
	CTX_r_message_set(C, r_message);
	CTX_s_message_set(C, s_message);

	stream_conn->host_state = TCP_SERVER_STATE_RESPOND_METHODS;

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: RESPOND_methods\n");
		printf("%c[%dm", 27, 0);
	}

	/* "Never ending" loop */
	while(1)
	{
		FD_ZERO(&set);
		FD_SET(io_ctx->sockfd, &set);

		tv.tv_sec = VRS_TIMEOUT;	/* User have to send something in 30 seconds */
		tv.tv_usec = 0;

		if( (ret = select(io_ctx->sockfd+1, &set, NULL, NULL, &tv)) == -1) {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "%s:%s():%d select(): %s\n",
					__FILE__, __FUNCTION__,  __LINE__, strerror(errno));
			goto end;
			/* Was event on the listen socket */
		} else if(ret>0 && FD_ISSET(io_ctx->sockfd, &set)) {

			/* Try to receive data through TCP connection */
			if( v_tcp_read(io_ctx, &error) <= 0 ) {
				goto end;
			}

			/* Handle verse handshake at TCP connection */
			if( (ret = vs_handle_handshake(C)) == -1) {
				goto end;
			}

			/* When there is something to send, then send it to peer */
			if( ret == 1 ) {
				/* Send response message to the client */
				if( (ret = v_tcp_write(io_ctx, &error)) <= 0) {
					goto end;
				}
			}

		} else {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "No response in %d seconds\n", VRS_TIMEOUT);
			goto end;
		}
	}

end:
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSING\n");
		printf("%c[%dm", 27, 0);
	}

	/* Set up TCP CLOSING state (non-blocking) */
	vs_CLOSING(C);

	/* Receive and Send messages are not neccessary any more */
	if(r_message != NULL) {
		free(r_message);
		r_message = NULL;
		CTX_r_message_set(C, NULL);
	}
	if(s_message != NULL) {
		free(s_message);
		s_message = NULL;
		CTX_s_message_set(C, NULL);
	}

	/* TCP connection is considered as CLOSED, but it is not possible to use
	 * this connection for other client */
	stream_conn->host_state = TCP_SERVER_STATE_CLOSED;

	/* NULL pointer at stream connection */
	CTX_current_stream_conn_set(C, NULL);

	/* Set TCP connection to CLOSED */
	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSED\n");
		printf("%c[%dm", 27, 0);
	}

	/* Was udp thread created? */
	if(vsession->udp_thread != 0 ) {
		/* Wait for UDP thread (this is blocking operation) */
		v_print_log(VRS_PRINT_DEBUG_MSG, "Waiting for join with UDP thread ...\n");
		if(pthread_join(vsession->udp_thread, &udp_thread_result) != 0) {
			v_print_log(VRS_PRINT_DEBUG_MSG, "UDP thread was not joined\n");
		}
	}

	pthread_mutex_lock(&vs_ctx->data.mutex);
	/* Unsubscribe this session (this avatar) from all nodes */
	vs_node_free_avatar_reference(vs_ctx, vsession);
	/* Try to destroy avatar node */
	vs_node_destroy_avatar_node(vs_ctx, vsession);
	pthread_mutex_unlock(&vs_ctx->data.mutex);

	/* This session could be used again for authentication */
	stream_conn->host_state = TCP_SERVER_STATE_LISTEN;

	/* Clear session flags */
	vsession->flags = 0;

	if(vsession->peer_cookie.str != NULL) {
		free(vsession->peer_cookie.str);
		vsession->peer_cookie.str = NULL;
	}
	if(vsession->ded.str != NULL) {
		free(vsession->ded.str);
		vsession->ded.str = NULL;
	}
	if(vsession->client_name != NULL) {
		free(vsession->client_name);
		vsession->client_name = NULL;
	}
	if(vsession->client_version != NULL) {
		free(vsession->client_version);
		vsession->client_version = NULL;
	}

	if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
		printf("%c[%d;%dm", 27, 1, 31);
		v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: LISTEN\n");
		printf("%c[%dm", 27, 0);
	}

	free(C);
	C = NULL;

	pthread_exit(NULL);
	return NULL;
}