/** * \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; }
/** * \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; }
/** * \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 = ¤t_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 = ¤t_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, ¤t_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(¤t_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(¤t_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(¤t_session->tcp_thread, ¤t_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(¤t_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; }