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; }
/** * \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; }