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