/** * \brief Teardown TLS connection with client * \param[in] *C The context of verse server * \return This function returns 1, when TLS teardown was successfull, otherwise * it returns 0. */ int vs_TLS_teardown(struct vContext *C) { struct VStreamConn *stream_conn = CTX_current_stream_conn(C); int ret; v_print_log(VRS_PRINT_DEBUG_MSG, "Try to shut down SSL connection.\n"); ret = SSL_shutdown(stream_conn->io_ctx.ssl); if(ret!=1) { ret = SSL_shutdown(stream_conn->io_ctx.ssl); } switch(ret) { case 1: /* SSL was successfully closed */ v_print_log(VRS_PRINT_DEBUG_MSG, "SSL connection was shut down.\n"); break; case 0: case -1: default: /* some error occured */ v_print_log(VRS_PRINT_DEBUG_MSG, "SSL connection was not able to shut down.\n"); break; } SSL_free(stream_conn->io_ctx.ssl); return 1; }
/** * \brief Establishing Kerberos connection, verifies clients credentials * \param[in] vContextc *C The context of verse server * \param[out] char **u_name user name of client * \return returns 1 if Kerberos authentication was OK 0 if something went wrong */ int vs_kerberos_auth(struct vContext *C, char **u_name){ 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); int flags = 0; int len = MAX_PACKET_SIZE; unsigned char buffer[MAX_PACKET_SIZE]; char *client_principal; krb5_data packet; krb5_error_code krb5err; int flag; /* Make sure socket is blocking */ flag = fcntl(stream_conn->io_ctx.sockfd, F_GETFL, 0); if ((fcntl(stream_conn->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 0; } /* Get KRB_AP_REQ message */ if ((len = recvfrom(stream_conn->io_ctx.sockfd, (char *) buffer, MAX_PACKET_SIZE, flags, NULL, NULL)) < 0) { v_print_log(VRS_PRINT_ERROR, "recvfrom failed.\n"); return 0; } packet.length = len; packet.data = (krb5_pointer) buffer; io_ctx->krb5_auth_ctx = vs_ctx->tcp_io_ctx.krb5_auth_ctx; io_ctx->krb5_ctx = vs_ctx->tcp_io_ctx.krb5_ctx; io_ctx->krb5_ticket = vs_ctx->tcp_io_ctx.krb5_ticket; io_ctx->krb5_keytab = vs_ctx->tcp_io_ctx.krb5_keytab; /* Check authentication info */ if ((krb5err = krb5_rd_req(io_ctx->krb5_ctx, (krb5_auth_context *) &io_ctx->krb5_auth_ctx, &packet, io_ctx->krb5_principal, io_ctx->krb5_keytab, NULL, (krb5_ticket **) &io_ctx->krb5_ticket))) { v_print_log(VRS_PRINT_ERROR, "krb5_rd_req %d: %s\n", (int) krb5err, krb5_get_error_message(io_ctx->krb5_ctx, krb5err)); return 0; } /* Get user name */ if ((krb5err = krb5_unparse_name(io_ctx->krb5_ctx, io_ctx->krb5_ticket->enc_part2->client, &client_principal))) { v_print_log(VRS_PRINT_ERROR, "krb5_unparse_name %d: %s\n", (int) krb5err, krb5_get_error_message(io_ctx->krb5_ctx, krb5err)); return 0; } v_print_log(VRS_PRINT_DEBUG_MSG, "Got authentication info from %s\n", client_principal); *u_name = client_principal; return 1; }
/** * \brief This function do TLS teardown, when it is reuired and it * closes socket. */ void vs_CLOSING(struct vContext *C) { struct VStreamConn *stream_conn = CTX_current_stream_conn(C); #ifdef WITH_OPENSSL if(stream_conn->io_ctx.ssl!=NULL) vs_TLS_teardown(C); #endif close(stream_conn->io_ctx.sockfd); }
/** * \brief do TLS negotiation with client application * \param[in] *C The context of verse server * \return This function returns 1, when TLS connection was established and * it returns 0, when TLS was not established. */ int vs_TLS_handshake(struct vContext *C) { 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); int flag, ret; /* Make sure socket is blocking */ flag = fcntl(stream_conn->io_ctx.sockfd, F_GETFL, 0); if( (fcntl(stream_conn->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 0; } /* Set up SSL */ if( (stream_conn->io_ctx.ssl = SSL_new(vs_ctx->tls_ctx)) == NULL) { v_print_log(VRS_PRINT_ERROR, "Setting up SSL failed.\n"); ERR_print_errors_fp(v_log_file()); SSL_free(stream_conn->io_ctx.ssl); stream_conn->io_ctx.ssl = NULL; close(io_ctx->sockfd); return 0; } /* Bind socket and SSL */ if (SSL_set_fd(stream_conn->io_ctx.ssl, io_ctx->sockfd) == 0) { v_print_log(VRS_PRINT_ERROR, "Failed binding socket descriptor and SSL.\n"); ERR_print_errors_fp(v_log_file()); SSL_free(stream_conn->io_ctx.ssl); stream_conn->io_ctx.ssl = NULL; close(io_ctx->sockfd); return 0; } SSL_set_mode(stream_conn->io_ctx.ssl, SSL_MODE_AUTO_RETRY); /* Do TLS handshake and negotiation */ if( (ret = SSL_accept(stream_conn->io_ctx.ssl)) != 1) { int err = SSL_get_error(stream_conn->io_ctx.ssl, ret); v_print_log(VRS_PRINT_ERROR, "SSL handshake failed: %d -> %d\n", ret, err); ERR_print_errors_fp(v_log_file()); SSL_free(stream_conn->io_ctx.ssl); stream_conn->io_ctx.ssl = NULL; close(io_ctx->sockfd); return 0; } v_print_log(VRS_PRINT_DEBUG_MSG, "SSL handshake succeed.\n"); return 1; }
/** * \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; }
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 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 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; }
/** * \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; }