/** * \brief Print all system commands in the message to the log file */ void v_print_message_sys_cmds(const unsigned char level, const struct VMessage *vmessage) { int i=0; while(vmessage->sys_cmd[i].cmd.id != CMD_RESERVED_ID && i < MAX_SYSTEM_COMMAND_COUNT) { switch(vmessage->sys_cmd[i].cmd.id) { v_print_log_simple(level, "\t"); case CMD_USER_AUTH_REQUEST: v_print_user_auth_request(level, (struct User_Authentication_Request*)&vmessage->sys_cmd[i].ua_req); break; case CMD_USER_AUTH_FAILURE: v_print_user_auth_failure(level, (struct User_Authentication_Failure*)&vmessage->sys_cmd[i].ua_fail); break; case CMD_USER_AUTH_SUCCESS: v_print_user_auth_success(level, (struct User_Authentication_Success*)&vmessage->sys_cmd[i].ua_succ); break; case CMD_CHANGE_L_ID: case CMD_CONFIRM_L_ID: case CMD_CHANGE_R_ID: case CMD_CONFIRM_R_ID: v_print_negotiate_cmd(level, (struct Negotiate_Cmd*)&vmessage->sys_cmd[i].negotiate_cmd); break; } v_print_log_simple(level, "\n"); i++; } }
void v_url_print(const int level, struct VURL *url) { v_print_log(level, "URL:\n"); v_print_log_simple(level, "\tscheme: %s\n", url->scheme); /* Print transport protocol */ if(url->transport_protocol == VRS_TP_UDP) { v_print_log_simple(level, "\ttransport_protocol: UDP\n"); } else if(url->transport_protocol == VRS_TP_TCP) { v_print_log_simple(level, "\ttransport_protocol: TCP\n"); } else if(url->transport_protocol == VRS_TP_WEBSOCKET) { v_print_log_simple(level, "\ttransport_protocol: WebSocket\n"); } else { v_print_log_simple(level, "\ttransport_protocol: unknown\n"); } /* Print security protocol */ if(url->security_protocol == VRS_SEC_DATA_NONE) { v_print_log_simple(level, "\tsecurity_protocol: NONE\n"); } else if(url->security_protocol == VRS_SEC_DATA_TLS) { v_print_log_simple(level, "\tsecurity_protocol: DTLS\n"); } else { v_print_log_simple(level, "\tsecurity_protocol: unknown\n"); } /* Print URL */ v_print_log_simple(level, "\tnode: %s\n", url->node); v_print_log_simple(level, "\tservice: %s\n", url->service); }
/** * \brief This function prints history of sent packets * \param[in] *history The history of sent packets. */ void v_print_packet_history(struct VPacket_History *history) { struct VSent_Packet *packet; struct VBucket *vbucket; int cmd_id; v_print_log(VRS_PRINT_DEBUG_MSG, "Packet history:\n\t"); packet = history->packets.first; while(packet!=NULL) { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "%d, ", packet->id); packet = packet->next; } v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); v_print_log(VRS_PRINT_DEBUG_MSG, "Command history:\n"); for(cmd_id=0; cmd_id<=MAX_CMD_ID; cmd_id++) { if(history->cmd_hist[cmd_id] != NULL) { vbucket = history->cmd_hist[cmd_id]->cmds.lb.first; while(vbucket != NULL) { v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)vbucket->data); vbucket = vbucket->next; } } } }
/** * \brief This function add command to the head of the queue */ int v_out_queue_push_head(struct VOutQueue *out_queue, uint8 prio, struct Generic_Cmd *cmd) { struct VBucket *vbucket; int ret = 0; /* Lock mutex */ pthread_mutex_lock(&out_queue->lock); /* Try to find command with the same address, when duplicities are not * allowed in command queue */ if(out_queue->cmds[cmd->id]->flag & REMOVE_HASH_DUPS) { vbucket = v_hash_array_find_item(&out_queue->cmds[cmd->id]->cmds, (void*)cmd); /* Add command from history of sent command to the outgoing queue * only in situation, when there isn't already newer command in the * queue */ if(vbucket==NULL) { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\tRe-sending command: %d\n", cmd->id); v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)cmd); ret = _v_out_queue_push(out_queue, OUT_QUEUE_ADD_HEAD, prio, cmd); } } else { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\tRe-sending command: %d\n", cmd->id); v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)cmd); ret = _v_out_queue_push(out_queue, OUT_QUEUE_ADD_HEAD, prio, cmd); } pthread_mutex_unlock(&out_queue->lock); return ret; }
/* Print network address */ void v_print_addr(const unsigned char level, const struct VNetworkAddress *addr) { if(addr->ip_ver==IPV4) { char str_addr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(addr->addr.ipv4.sin_addr), str_addr, sizeof(str_addr)); v_print_log_simple(level, "%s ", str_addr); } else if(addr->ip_ver==IPV6) { char str_addr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(addr->addr.ipv6.sin6_addr), str_addr, sizeof(str_addr)); v_print_log_simple(level, "%s ", str_addr); } }
/** * \brief This function print content of fake command User_Authenticate */ void v_fake_user_auth_print(const unsigned char level, struct User_Authenticate_Cmd *user_auth) { int i; v_print_log_simple(level, "\tCONNECT_AUTHENTICATE: username: %s, ", user_auth->username); if(user_auth->auth_meth_count > 0) { v_print_log_simple(level, "methods: "); for(i=0; i<user_auth->auth_meth_count; i++) { switch(user_auth->methods[i]) { case VRS_UA_METHOD_NONE: v_print_log_simple(level, "NONE, "); break; case VRS_UA_METHOD_PASSWORD: v_print_log_simple(level, "PASSWORD, "); break; case VRS_UA_METHOD_RESERVED: v_print_log_simple(level, "RESERVED, "); break; default: v_print_log_simple(level, "Unknown, "); break; } } } if(user_auth->data != NULL) { v_print_log_simple(level, "data: %s", user_auth->data); } v_print_log_simple(level, "\n"); }
/** * \brief This function print content of fake command Tag_Create_Ack. */ void v_fake_tag_create_ack_print(const unsigned char level, const struct Generic_Cmd *cmd) { struct Tag_Create_Ack_Cmd *tag_create_ack = (struct Tag_Create_Ack_Cmd *)cmd; v_print_log_simple(level, "\tTag_Create_Ack: Node_ID: %d, TagGroup_ID: %d, Tag_ID %d\n", tag_create_ack->node_id, tag_create_ack->taggroup_id, tag_create_ack->tag_id); }
void v_print_receive_message(struct vContext *C) { struct IO_CTX *io_ctx = CTX_io_ctx(C); struct VMessage *r_message = CTX_r_message(C); if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 34); v_print_log(VRS_PRINT_DEBUG_MSG, "Receive message: "); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "Socket: %d, ", io_ctx->sockfd); v_print_addr_port(VRS_PRINT_DEBUG_MSG, &io_ctx->peer_addr); v_print_message_header(VRS_PRINT_DEBUG_MSG, r_message); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); v_print_message_sys_cmds(VRS_PRINT_DEBUG_MSG, r_message); printf("%c[%dm", 27, 0); } }
/** * \brief This function prints content of fake command Layer_Destroy_Ack. */ void v_fake_layer_destroy_ack_print(const unsigned char level, const struct Generic_Cmd *cmd) { struct Layer_Destroy_Ack_Cmd *layer_destroy_ack = (struct Layer_Destroy_Ack_Cmd *)cmd; v_print_log_simple(level, "\tLayer_Destroy_Ack: Node_ID: %d, Layer_ID: %d\n", layer_destroy_ack->node_id, layer_destroy_ack->layer_id); }
/* Print network address and port */ void v_print_addr_port(const unsigned char level, const struct VNetworkAddress *addr) { unsigned short port; if(addr->ip_ver==IPV4) { char str_addr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(addr->addr.ipv4.sin_addr), str_addr, sizeof(str_addr)); port = ntohs(addr->addr.ipv4.sin_port); v_print_log_simple(level, "%s:%d ", str_addr, port); } else if(addr->ip_ver==IPV6) { char str_addr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(addr->addr.ipv6.sin6_addr), str_addr, sizeof(str_addr)); port = ntohs(addr->addr.ipv6.sin6_port); v_print_log_simple(level, "[%s]:%d ", str_addr, port); } }
/** * \brief This function prints content of the USER_AUTH_FAILURE command */ void v_print_user_auth_failure(const unsigned char level, struct User_Authentication_Failure *ua_fail) { int i; v_print_log_simple(level, "\tUSER_AUTH_FAILURE, "); if(ua_fail->count>0) { v_print_log_simple(level, "Methods: "); for(i=0; i<ua_fail->count; i++) { switch(ua_fail->method[i]) { case VRS_UA_METHOD_RESERVED: v_print_log_simple(level, "Reserved, "); break; case VRS_UA_METHOD_NONE: v_print_log_simple(level, "None, "); break; case VRS_UA_METHOD_PASSWORD: v_print_log_simple(level, "Password, "); break; } } } else { v_print_log_simple(level, "Access not granted."); } v_print_log_simple(level, "\n"); }
/** * \brief Print verse packet header to the log file */ void v_print_packet_header(const unsigned char level, const VPacket *vpacket) { int i; v_print_log_simple(level, "Ver: %d, Flags: ", vpacket->header.version); for(i=7; i>=0; i--) { if((1<<i) & vpacket->header.flags) { switch (1<<i) { case PAY_FLAG: v_print_log_simple(level, "PAY,"); break; case ACK_FLAG: v_print_log_simple(level, "ACK,"); break; case ANK_FLAG: v_print_log_simple(level, "ANK,"); break; case SYN_FLAG: v_print_log_simple(level, "SYN,"); break; case FIN_FLAG: v_print_log_simple(level, "FIN,"); break; } } } v_print_log_simple(level, " Window:%d, PayID:%d, AckNakID:%d, AnkID:%d\n", vpacket->header.window, vpacket->header.payload_id, vpacket->header.ack_nak_id, vpacket->header.ank_id); }
void v_print_receive_packet(struct vContext *C) { struct IO_CTX *io_ctx = CTX_io_ctx(C); struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C); struct VPacket *r_packet = CTX_r_packet(C); if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 34); v_print_log(VRS_PRINT_DEBUG_MSG, "Receive packet: "); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "Socket: %d, ", io_ctx->sockfd); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "bufsize: %d, ", io_ctx->buf_size); v_print_addr_port(VRS_PRINT_DEBUG_MSG, &io_ctx->peer_addr); v_print_packet_header(VRS_PRINT_DEBUG_MSG, r_packet); v_print_log(VRS_PRINT_DEBUG_MSG, "Shift: %d -> Window: %d\n", dgram_conn->rwin_host_scale, (unsigned int)r_packet->header.window << dgram_conn->rwin_host_scale); v_print_packet_sys_cmds(VRS_PRINT_DEBUG_MSG, r_packet); printf("%c[%dm", 27, 0); } }
/* Clear datagram connection */ void v_conn_dgram_clear(struct VDgramConn *dgram_conn) { /* Debug print */ if(is_log_level(VRS_PRINT_DEBUG_MSG)) { v_print_log(VRS_PRINT_DEBUG_MSG, "Free connection "); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "%d ", dgram_conn->host_id); v_print_addr_port(VRS_PRINT_DEBUG_MSG, &dgram_conn->peer_address); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); } #ifdef WITH_OPENSSL if(dgram_conn->io_ctx.bio != NULL) { BIO_vfree(dgram_conn->io_ctx.bio); dgram_conn->io_ctx.bio = NULL; } #endif close(dgram_conn->io_ctx.sockfd); v_conn_dgram_init(dgram_conn); }
/* Destroy datagram connection */ void v_conn_dgram_destroy(struct VDgramConn *dgram_conn) { /* Debug print */ if(is_log_level(VRS_PRINT_DEBUG_MSG)) { v_print_log(VRS_PRINT_DEBUG_MSG, "Free connection "); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "%d ", dgram_conn->host_id); v_print_addr_port(VRS_PRINT_DEBUG_MSG, &dgram_conn->peer_address); v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); } if(dgram_conn->io_ctx.buf) { free(dgram_conn->io_ctx.buf); dgram_conn->io_ctx.buf = NULL; } v_packet_history_destroy(&dgram_conn->packet_history); v_ack_nak_history_clear(&dgram_conn->ack_nak); close(dgram_conn->io_ctx.sockfd); }
void v_url_print(const int level, struct VURL *url) { v_print_log(level, "URL:\n"); v_print_log_simple(level, "\tscheme: %s\n", url->scheme); if(url->transport_protocol==VRS_TP_UDP) { v_print_log_simple(level, "\tdgram_protocol: UDP\n"); } else { v_print_log_simple(level, "\tdgram_protocol: unknow\n"); } if(url->security_protocol==VRS_SEC_DATA_NONE) { v_print_log_simple(level, "\tsecurity_protocol: NONE\n"); } else if(url->security_protocol==VRS_SEC_DATA_TLS) { v_print_log_simple(level, "\tsecurity_protocol: DTLS\n"); } else { v_print_log_simple(level, "\tsecurity_protocol: unknown\n"); } v_print_log_simple(level, "\tnode: %s\n", url->node); v_print_log_simple(level, "\tservice: %s\n", url->service); }
/** * \brief This function prints negotiate commands: Change_L/R and Confirm_L/R */ void v_print_negotiate_cmd(const unsigned char level, struct Negotiate_Cmd *negotiate_cmd) { int i; switch(negotiate_cmd->id) { case CMD_CHANGE_L_ID: v_print_log_simple(level, "\tCHANGE_L COMMAND, "); break; case CMD_CONFIRM_L_ID: v_print_log_simple(level, "\tCONFIRM_L COMMAND, "); break; case CMD_CHANGE_R_ID: v_print_log_simple(level, "\tCHANGE_R COMMAND, "); break; case CMD_CONFIRM_R_ID: v_print_log_simple(level, "\tCONFIRM_R COMMAND, "); break; default: v_print_log_simple(level, "\tNEGOTIATE DEBUG PRINT ERROR: bad command id\n"); break; } v_print_log_simple(level, "count: %d, ", negotiate_cmd->count); switch(negotiate_cmd->feature) { case FTR_FC_ID: v_print_log_simple(level, "feature: FC_ID, "); break; case FTR_CC_ID: v_print_log_simple(level, "feature: CC_ID, "); break; case FTR_HOST_URL: v_print_log_simple(level, "feature: HOST_URL, "); break; case FTR_TOKEN: v_print_log_simple(level, "feature: TOKEN, "); break; case FTR_DED: v_print_log_simple(level, "feature: DED, "); break; case FTR_RWIN_SCALE: v_print_log_simple(level, "feature: RWIN_SCALE, "); break; case FTR_FPS: v_print_log_simple(level, "feature: FPS, "); break; case FTR_CMD_COMPRESS: v_print_log_simple(level, "feature: CMD_COMPRESS, "); break; case FTR_CLIENT_NAME: v_print_log_simple(level, "feature: CLIENT_NAME, "); break; case FTR_CLIENT_VERSION: v_print_log_simple(level, "feature: CLIENT_VERSION, "); break; default: v_print_log_simple(level, "unknown feature, "); break; } for(i=0; i<negotiate_cmd->count; i++) { switch(negotiate_cmd->feature) { case FTR_FC_ID: case FTR_CC_ID: case FTR_RWIN_SCALE: case FTR_CMD_COMPRESS: v_print_log_simple(level, "%d, ", negotiate_cmd->value[i].uint8); break; case FTR_HOST_URL: case FTR_TOKEN: case FTR_DED: case FTR_CLIENT_NAME: case FTR_CLIENT_VERSION: v_print_log_simple(level, "%d:%s, ", negotiate_cmd->value[i].string8.length, negotiate_cmd->value[i].string8.str); break; case FTR_FPS: v_print_log_simple(level, "%6.3f, ", negotiate_cmd->value[i].real32); break; default: break; } } v_print_log_simple(level, "\n"); }
/** * \brief This function print content of fake command Connect_Terminate */ void v_fake_connect_terminate_print(const unsigned char level, struct Connect_Terminate_Cmd *connect_terminate) { v_print_log_simple(level, "\tCONNECT_TERMINATE: error: %d\n", connect_terminate->error_num); }
/** * \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; }
/** * \brief Print verse message header to the log file */ void v_print_message_header(const unsigned char level, const VMessage *vmessage) { v_print_log_simple(level, "Ver: %d, Len: %d", vmessage->header.version, vmessage->header.len); }
/** * \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; }
/** * \brief Load configuration from file to the Verse server context. * * \param vs_ctx The Verse server context. * \param file The pointer at FILE structure; */ void vs_read_config_file(struct VS_CTX *vs_ctx, const char *ini_file_name) { dictionary *ini_dict; ini_dict = iniparser_load((char*) ini_file_name); if(ini_dict != NULL) { char *user_auth_method; char *certificate_file_name; char *ca_certificate_file_name; char *private_key; char *fc_type; #ifdef WITH_MONGODB char *mongodb_server_hostname; int mongodb_server_port; char *mongodb_server_db_name; char *mongodb_user; char *mongodb_pass; #endif int fc_win_scale; int in_queue_max_size; int out_queue_max_size; int tcp_port_number; int ws_port_number; int udp_low_port_number; int udp_high_port_number; int max_session_count; v_print_log(VRS_PRINT_DEBUG_MSG, "Reading config file: %s\n", ini_file_name); #ifdef WITH_OPENSSL /* Try to get TLS port number */ tcp_port_number = iniparser_getint(ini_dict, "Global:TLS_port", -1); if(tcp_port_number != -1) { if(tcp_port_number >= 1024 && tcp_port_number <= 65535) { vs_ctx->tcp_port = tcp_port_number; } else { v_print_log(VRS_PRINT_WARNING, "TLS port: %d out of range: 1024-65535\n", tcp_port_number); } } #else /* Try to get TCP port number */ tcp_port_number = iniparser_getint(ini_dict, "Global:TCP_port", -1); if(tcp_port_number != -1) { if(tcp_port_number >= 1024 && tcp_port_number <= 65535) { vs_ctx->tcp_port = tcp_port_number; } else { v_print_log(VRS_PRINT_WARNING, "TCP port: %d out of range: 1024-65535\n", tcp_port_number); } } #endif /* Try to get WebSocket port number */ ws_port_number = iniparser_getint(ini_dict, "Global:WS_port", -1); if(ws_port_number != -1) { if(ws_port_number >= 1024 && ws_port_number <= 65535) { vs_ctx->ws_port = ws_port_number; } else { v_print_log(VRS_PRINT_WARNING, "WebSocket port: %d out of range: 1024-65535\n", ws_port_number); } } /* Try to get lowest UDP port */ udp_low_port_number = iniparser_getint(ini_dict, "Global:UDP_port_low", -1); if(udp_low_port_number != -1) { if(udp_low_port_number >= 49152 && udp_low_port_number <= 65535) { vs_ctx->port_low = udp_low_port_number; } else { v_print_log(VRS_PRINT_WARNING, "UDP port: %d out of range: 49152-65535\n", udp_low_port_number); } } udp_high_port_number = iniparser_getint(ini_dict, "Global:UDP_port_high", -1); if(udp_high_port_number != -1) { if(udp_high_port_number >= 49152 && udp_high_port_number <= 65535) { vs_ctx->port_high = udp_high_port_number; } else { v_print_log(VRS_PRINT_WARNING, "UDP port: %d out of range: 49152-65535\n", udp_high_port_number); } } max_session_count = iniparser_getint(ini_dict, "Global:MaxSessionCount", -1); if(max_session_count != -1) { vs_ctx->max_sessions = max_session_count; } /* Try to load section [Users] */ user_auth_method = iniparser_getstring(ini_dict, "Users:Method", NULL); if(user_auth_method != NULL && strcmp(user_auth_method, "file") == 0) { char *file_type; v_print_log(VRS_PRINT_DEBUG_MSG, "user_auth_method: %s\n", user_auth_method); file_type = iniparser_getstring(ini_dict, "Users:FileType", NULL); if(file_type != NULL && strcmp(file_type, "csv") == 0) { char *csv_file_name; v_print_log(VRS_PRINT_DEBUG_MSG, "file_type: %s\n", file_type); csv_file_name = iniparser_getstring(ini_dict, "Users:File", NULL); if(csv_file_name !=NULL) { vs_ctx->auth_type = AUTH_METHOD_CSV_FILE; if(vs_ctx->csv_user_file != NULL) { free(vs_ctx->csv_user_file); } vs_ctx->csv_user_file = strdup(csv_file_name); v_print_log(VRS_PRINT_DEBUG_MSG, "csv_file_name: %s\n", csv_file_name); } } } /* Try to load section [Security] */ certificate_file_name = iniparser_getstring(ini_dict, "Security:Certificate", NULL); if(certificate_file_name != NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "certificate_file_name: %s\n", certificate_file_name); if(vs_ctx->public_cert_file != NULL) { free(vs_ctx->public_cert_file); } vs_ctx->public_cert_file = strdup(certificate_file_name); } /* Certificate of certificate authority */ ca_certificate_file_name = iniparser_getstring(ini_dict, "Security:CACertificate", NULL); if(ca_certificate_file_name != NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "ca_certificate_file_name: %s\n", ca_certificate_file_name); vs_ctx->ca_cert_file = strdup(ca_certificate_file_name); } /* Server private key */ private_key = iniparser_getstring(ini_dict, "Security:PrivateKey", NULL); if(private_key != NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "private_key: %s\n", private_key); if(vs_ctx->private_cert_file != NULL) { free(vs_ctx->private_cert_file); } vs_ctx->private_cert_file = strdup(private_key); } /* Type of Flow Control */ fc_type = iniparser_getstring(ini_dict, "FlowControl:Type", NULL); if(fc_type != NULL) { if(strcmp(fc_type, "tcp_like")==0) { v_print_log(VRS_PRINT_DEBUG_MSG, "flow_control: %s\n", fc_type); vs_ctx->fc_meth = FC_TCP_LIKE; } else if(strcmp(fc_type, "none")==0) { v_print_log(VRS_PRINT_DEBUG_MSG, "flow_control type: %s\n", fc_type); vs_ctx->fc_meth = FC_NONE; } } /* Scale of Flow Control window */ fc_win_scale = iniparser_getint(ini_dict, "FlowControl:WinScale", -1); if(fc_win_scale != -1) { if(fc_win_scale >= 0 && fc_win_scale <= 255) { v_print_log(VRS_PRINT_DEBUG_MSG, "flow_control scale: %d\n", fc_win_scale); vs_ctx->rwin_scale = fc_win_scale; } } /* Maximal size of incoming queue */ in_queue_max_size = iniparser_getint(ini_dict, "InQueue:MaxSize", -1); if(in_queue_max_size != -1) { if(in_queue_max_size > 0) { v_print_log(VRS_PRINT_DEBUG_MSG, "in_queue max size: %d\n", in_queue_max_size); vs_ctx->in_queue_max_size = in_queue_max_size; } } /* Maximal size of outgoing queue */ out_queue_max_size = iniparser_getint(ini_dict, "OutQueue:MaxSize", -1); if(out_queue_max_size != -1) { if(out_queue_max_size > 0) { v_print_log(VRS_PRINT_DEBUG_MSG, "in_queue max size: %d\n", out_queue_max_size); vs_ctx->in_queue_max_size = out_queue_max_size; } } #ifdef WITH_MONGODB /* Hostname of MongoDB server */ mongodb_server_hostname = iniparser_getstring(ini_dict, "MongoDB:ServerHostname", NULL); if(mongodb_server_hostname != NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "mongodb server hostname: %s\n", mongodb_server_hostname); vs_ctx->mongodb_server = strdup(mongodb_server_hostname); } /* Port of MongoDB server */ mongodb_server_port = iniparser_getint(ini_dict, "MongoDB:ServerPort", -1); if(mongodb_server_port != -1) { v_print_log(VRS_PRINT_DEBUG_MSG, "mongodb server port: %d\n", mongodb_server_port); vs_ctx->mongodb_port = mongodb_server_port; } /* MongoDB database name used by Verse server */ mongodb_server_db_name = iniparser_getstring(ini_dict, "MongoDB:DatabaseName", NULL); if(mongodb_server_db_name != NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "mongodb server database name: %s\n", mongodb_server_db_name); vs_ctx->mongodb_db_name = strdup(mongodb_server_db_name); } /* Username used for authentication at MongoDB */ mongodb_user = iniparser_getstring(ini_dict, "MongoDB:Username", NULL); if(mongodb_user != NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "mongodb server username: %s\n", mongodb_user); vs_ctx->mongodb_user = strdup(mongodb_user); } /* Password used for authentication at MongoDB */ mongodb_pass = iniparser_getstring(ini_dict, "MongoDB:Password", NULL); if(mongodb_user != NULL) { int i; v_print_log(VRS_PRINT_DEBUG_MSG, "mongodb server password: "******"*"); } v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); vs_ctx->mongodb_pass = strdup(mongodb_pass); } #endif iniparser_freedict(ini_dict); } else { v_print_log(VRS_PRINT_WARNING, "Unable to load config file: %s\n", ini_file_name); } }
/** * \brief WebSocket callback function for received message */ void vs_ws_recv_msg_callback(wslay_event_context_ptr wslay_ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { struct vContext *C = (struct vContext*)user_data; struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct VSession *session = CTX_current_session(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); int ret; if(!wslay_is_ctrl_frame(arg->opcode)) { /* Verse server uses only binary message for communication */ if(arg->opcode == WSLAY_BINARY_FRAME) { struct wslay_event_msg msgarg; #if DEBUG_WEB_SOCKET unsigned int i; v_print_log(VRS_PRINT_DEBUG_MSG, "WS Callback received binary message\n"); v_print_log(VRS_PRINT_DEBUG_MSG, "Binary dump\n"); /* Print dump of received data */ for(i=0; i<arg->msg_length; i++) { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "%d,", arg->msg[i]); } v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); #endif /* Copy received data to IO context */ memcpy(io_ctx->buf, arg->msg, arg->msg_length); io_ctx->buf_size = arg->msg_length; if(session->stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) { if(v_STREAM_handle_messages(C) != 0) { /* When some payload data were received, then poke data thread */ sem_post(vs_ctx->data.sem); } else { /* End connection */ session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; /* Try to close connection with WebSocket client */ wslay_event_queue_close(wslay_ctx, WSLAY_CODE_PROTOCOL_ERROR, (uint8_t*)"Wrong command", /* Close message */ 13); /* The length of close message */ return; } } else { if( vs_handle_handshake(C) == -1 ) { /* End connection */ session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; /* Try to close connection with WebSocket client */ wslay_event_queue_close(wslay_ctx, WSLAY_CODE_PROTOCOL_ERROR, (uint8_t*)"Wrong command", /* Close message */ 13); /* The length of close message */ return; } /* During handshake send response immediately. */ /* TODO: optionally send message fragmented, when it is needed using: * wslay_event_queue_fragmented_msg() */ msgarg.opcode = WSLAY_BINARY_FRAME; msgarg.msg = (uint8_t*)io_ctx->buf; msgarg.msg_length = io_ctx->buf_size; /* Queue message for sending */ if((ret = wslay_event_queue_msg(wslay_ctx, &msgarg)) != 0) { v_print_log(VRS_PRINT_ERROR, "Unable to queue message to WebSocket: %d\n", ret); return; } else { v_print_log(VRS_PRINT_DEBUG_MSG, "WebSocket message successfully queued\n"); } } } else if(arg->opcode == WSLAY_TEXT_FRAME) { v_print_log(VRS_PRINT_ERROR, "WebSocket text frame is not supported\n"); return; } } else { /* Print opcode of control message */ v_print_log(VRS_PRINT_DEBUG_MSG, "WS Callback Received Ctrl Message: opcode: %d\n", arg->opcode); /* Is it closing message? */ if(arg->opcode & WSLAY_CONNECTION_CLOSE) { v_print_log(VRS_PRINT_DEBUG_MSG, "Close message with code: %d, message: %s\n", arg->status_code, arg->msg); /* When this control message was received at second time, then * switch to the state CLOSED. Otherwise switch to the state * CLOSING */ if(session->stream_conn->host_state == TCP_SERVER_STATE_CLOSING) { session->stream_conn->host_state = TCP_SERVER_STATE_CLOSED; } else { session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; /* When server wasn't in the state closing, then send * confirmation to the client, that this connection will be * closed */ wslay_event_queue_close(wslay_ctx, WSLAY_CODE_NORMAL_CLOSURE, (uint8_t*)"Closing connection", 15); } } } }