示例#1
0
/**
 * \brief Main Verse server loop. Server waits for connect attempts, responds to attempts
 * and creates per connection threads
 */
int vs_main_listen_loop(VS_CTX *vs_ctx)
{
	struct vContext *C;
	struct timeval start, tv;
	fd_set set;
	int count, tmp, i, ret;
	int sockfd;

	/* Allocate context for server */
	C = (struct vContext*)calloc(1, sizeof(struct vContext));
	/* Set up client context, connection context and IO context */
	CTX_server_ctx_set(C, vs_ctx);
	CTX_client_ctx_set(C, NULL);
	CTX_current_dgram_conn_set(C, NULL);

	/* Get time of the start of the server */
	gettimeofday(&start, NULL);

	/* 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 */
	srand(start.tv_sec - start.tv_usec);
#endif

	/* "Never ending" listen loop */
	while(vs_ctx->state == SERVER_STATE_READY) {
		/* Debug print */
		gettimeofday(&tv, NULL);
		if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
			if(tv.tv_sec==start.tv_sec)
				v_print_log(VRS_PRINT_DEBUG_MSG, "\t+0s");
			else
				v_print_log(VRS_PRINT_DEBUG_MSG, "\t+%lds", (long int)(tv.tv_sec - start.tv_sec));
#ifdef WSLAY
			v_print_log_simple(VRS_PRINT_DEBUG_MSG,
					"\tServer listen on (TCP port: %d, WebSocket port: %d)\n",
					vs_ctx->tcp_io_ctx.host_addr.port,
					vs_ctx->ws_io_ctx.host_addr.port);
#else
			v_print_log_simple(VRS_PRINT_DEBUG_MSG,
					"\tServer listen on TCP port: %d\n",
					vs_ctx->tcp_io_ctx.host_addr.port);
#endif
		}

		/* Set up set of sockets */
		FD_ZERO(&set);
		FD_SET(vs_ctx->tcp_io_ctx.sockfd, &set);
		/* When Verse is compiled with support of WebSocket, then listen on
		 * WebSocket port too */
#ifdef WSLAY
		FD_SET(vs_ctx->ws_io_ctx.sockfd, &set);
		sockfd = (vs_ctx->tcp_io_ctx.sockfd > vs_ctx->ws_io_ctx.sockfd) ?
				vs_ctx->tcp_io_ctx.sockfd : vs_ctx->ws_io_ctx.sockfd;
#else
		sockfd = vs_ctx->tcp_io_ctx.sockfd;
#endif

		/* We will wait one second for connect attempt, then debug print will
		 * be print again */
		tv.tv_sec = 1;
		tv.tv_usec = 0;


		/* Wait for event on listening sockets */
		if( (ret = select(sockfd+1, &set, NULL, NULL, &tv)) == -1 ) {
			int err = errno;
			if(err==EINTR) {
				break;
			} else {
				if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR,
						"%s:%s():%d select(): %s\n",
						__FILE__,
						__FUNCTION__,
						__LINE__,
						strerror(err));
				return -1;
			}
			/* Was event on the listen socket */
		} else if(ret>0) {
			if (FD_ISSET(vs_ctx->tcp_io_ctx.sockfd, &set))
			{
				v_print_log(VRS_PRINT_DEBUG_MSG, "TCP Connection attempt\n");
				CTX_io_ctx_set(C, &vs_ctx->tcp_io_ctx);
				vs_new_stream_conn(C, vs_tcp_conn_loop);
#ifdef WSLAY
			} else if(FD_ISSET(vs_ctx->ws_io_ctx.sockfd, &set)) {
				v_print_log(VRS_PRINT_DEBUG_MSG, "WebSocket Connection attempt\n");
				CTX_io_ctx_set(C, &vs_ctx->ws_io_ctx);
				vs_new_stream_conn(C, vs_websocket_loop);
#endif
			}
		}
	}

	count = 0;
	while( (vs_ctx->state == SERVER_STATE_CLOSING) && (count < VRS_TIMEOUT) ) {
		v_print_log(VRS_PRINT_DEBUG_MSG, "Wait for Server state CLOSED\n");

		/* Check if there are still some pending connection */
		tmp = 0;
		for(i=0; i<vs_ctx->max_sessions; i++) {
			if(vs_ctx->vsessions[i] != NULL) {
				if(vs_ctx->vsessions[i]->stream_conn != NULL &&
					vs_ctx->vsessions[i]->stream_conn->host_state != TCP_SERVER_STATE_LISTEN ) {
					tmp++;
				}
				/* TODO: cancel thread with closed connection to speed up server exit
					pthread_kill(vs_ctx->vsessions[i]->tcp_thread, SIGALRM);
					pthread_join(vs_ctx->vsessions[i]->tcp_thread, NULL);
				*/

			}
		}
		if(tmp==0) {
			vs_ctx->state = SERVER_STATE_CLOSED;
		} else {
			sleep(1);
		}
	}

	free(C);

	return 1;
}
示例#2
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;
}
示例#3
0
/**
 * \brief This connection tries to handle new connection attempt. When this
 * attempt is successful, then it creates new thread
 */
static int vs_new_stream_conn(struct vContext *C, void *(*conn_loop)(void*))
{
	VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *current_session = NULL;
	socklen_t addr_len;
	int ret, i;
	static uint32 last_session_id = 0;

	/* Try to find free session */
	for(i=0; i< vs_ctx->max_sessions; i++) {
		if(vs_ctx->vsessions[i]->stream_conn->host_state == TCP_SERVER_STATE_LISTEN) {
			/* TODO: lock mutex here */
			current_session = vs_ctx->vsessions[i];
			current_session->stream_conn->host_state = TCP_SERVER_STATE_RESPOND_METHODS;
			current_session->session_id = last_session_id++;
			/* TODO: unlock mutex here */
			break;
		}
	}

	if(current_session != NULL) {
		struct vContext *new_C;
		int flag;

		/* Try to accept client connection (do TCP handshake) */
		if(io_ctx->host_addr.ip_ver==IPV4) {
			/* Prepare IPv4 variables for TCP handshake */
			struct sockaddr_in *client_addr4 = &current_session->stream_conn->io_ctx.peer_addr.addr.ipv4;
			current_session->stream_conn->io_ctx.peer_addr.ip_ver = IPV4;
			addr_len = sizeof(current_session->stream_conn->io_ctx.peer_addr.addr.ipv4);

			/* Try to do TCP handshake */
			if( (current_session->stream_conn->io_ctx.sockfd = accept(io_ctx->sockfd, (struct sockaddr*)client_addr4, &addr_len)) == -1) {
				if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "accept(): %s\n", strerror(errno));
				return 0;
			}

			/* Save the IPv4 of the client as string in verse session */
			inet_ntop(AF_INET, &client_addr4->sin_addr, current_session->peer_hostname, INET_ADDRSTRLEN);
		} else if(io_ctx->host_addr.ip_ver==IPV6) {
			/* Prepare IPv6 variables for TCP handshake */
			struct sockaddr_in6 *client_addr6 = &current_session->stream_conn->io_ctx.peer_addr.addr.ipv6;
			current_session->stream_conn->io_ctx.peer_addr.ip_ver = IPV6;
			addr_len = sizeof(current_session->stream_conn->io_ctx.peer_addr.addr.ipv6);

			/* Try to do TCP handshake */
			if( (current_session->stream_conn->io_ctx.sockfd = accept(io_ctx->sockfd, (struct sockaddr*)client_addr6, &addr_len)) == -1) {
				if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "accept(): %s\n", strerror(errno));
				return 0;
			}

			/* Save the IPv6 of the client as string in verse session */
			inet_ntop(AF_INET6, &client_addr6->sin6_addr, current_session->peer_hostname, INET6_ADDRSTRLEN);
		}

		/* Set to this socket flag "no delay" */
		flag = 1;
		if(setsockopt(current_session->stream_conn->io_ctx.sockfd,
				IPPROTO_TCP, TCP_NODELAY, &flag, (socklen_t)sizeof(flag)) == -1)
		{
			if(is_log_level(VRS_PRINT_ERROR)) {
				v_print_log(VRS_PRINT_ERROR,
						"setsockopt: TCP_NODELAY: %d\n",
						strerror(errno));
			}
			return -1;;
		}

		CTX_current_session_set(C, current_session);
		CTX_current_stream_conn_set(C, current_session->stream_conn);
		CTX_io_ctx_set(C, &current_session->stream_conn->io_ctx);

		if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
			v_print_log(VRS_PRINT_DEBUG_MSG, "New connection from: ");
			v_print_addr_port(VRS_PRINT_DEBUG_MSG, &(current_session->stream_conn->io_ctx.peer_addr));
			v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n");
		}

		/* Duplicate verse context for new thread */
		new_C = (struct vContext*)calloc(1, sizeof(struct vContext));
		memcpy(new_C, C, sizeof(struct vContext));

		/* Try to initialize thread attributes */
		if( (ret = pthread_attr_init(&current_session->tcp_thread_attr)) !=0 ) {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "pthread_attr_init(): %s\n", strerror(errno));
			return 0;
		}

		/* Try to set thread attributes as detached */
		if( (ret = pthread_attr_setdetachstate(&current_session->tcp_thread_attr, PTHREAD_CREATE_DETACHED)) != 0) {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "pthread_attr_setdetachstate(): %s\n", strerror(errno));
			return 0;
		}

		/* Try to create new thread */
		if((ret = pthread_create(&current_session->tcp_thread, &current_session->tcp_thread_attr, conn_loop, (void*)new_C)) != 0) {
			if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "pthread_create(): %s\n", strerror(errno));
		}

		/* Destroy thread attributes */
		pthread_attr_destroy(&current_session->tcp_thread_attr);
	} else {
		int tmp_sockfd;
		v_print_log(VRS_PRINT_DEBUG_MSG, "Number of session slot: %d reached\n", vs_ctx->max_sessions);
		/* TODO: return some error. Not only accept and close connection. */
		/* Try to accept client connection (do TCP handshake) */
		if(io_ctx->host_addr.ip_ver == IPV4) {
			/* Prepare IP6 variables for TCP handshake */
			struct sockaddr_in client_addr4;

			addr_len = sizeof(client_addr4);

			/* Try to do TCP handshake */
			if( (tmp_sockfd = accept(io_ctx->sockfd, (struct sockaddr*)&client_addr4, &addr_len)) == -1) {
				if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "accept(): %s\n", strerror(errno));
				return 0;
			}
			/* Close connection (no more free session slots) */
			close(tmp_sockfd);
		} else if(io_ctx->host_addr.ip_ver == IPV6) {
			/* Prepare IP6 variables for TCP handshake */
			struct sockaddr_in6 client_addr6;
			addr_len = sizeof(client_addr6);

			/* Try to do TCP handshake */
			if( (tmp_sockfd = accept(io_ctx->sockfd, (struct sockaddr*)&client_addr6, &addr_len)) == -1) {
				if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "accept(): %s\n", strerror(errno));
				return 0;
			}
			/* Close connection (no more free session slots) */
			close(tmp_sockfd);
		}
		/* TODO: Fix this */
		sleep(1);
	}

	return 1;
}