/** * \brief This function is called, when server is in NEGOTIATE_cookie_ded state * * This function can create new thread for datagram connection */ int vs_NEGOTIATE_cookie_ded_loop(struct vContext *C) { struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); struct VSession *vsession = CTX_current_session(C); struct VMessage *r_message = CTX_r_message(C); struct VMessage *s_message = CTX_s_message(C); int i, j, ret; unsigned short buffer_pos = 0; int host_url_proposed = 0, host_cookie_proposed = 0, peer_cookie_confirmed = 0, ded_confirmed = 0, client_name_proposed = 0, client_version_proposed = 0; struct timeval tv; struct VURL url; /* Reset content of received message */ memset(r_message, 0, sizeof(struct VMessage)); /* Unpack Verse message header */ buffer_pos += v_unpack_message_header(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message); /* Unpack all system commands */ buffer_pos += v_unpack_message_system_commands(&io_ctx->buf[buffer_pos], (io_ctx->buf_size - buffer_pos), r_message); v_print_receive_message(C); /* Process all received system commands */ for(i=0; i<MAX_SYSTEM_COMMAND_COUNT && r_message->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) { switch(r_message->sys_cmd[i].cmd.id) { case CMD_CHANGE_R_ID: /* Client has to propose url in this state */ if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_HOST_URL) { if(r_message->sys_cmd[i].negotiate_cmd.count > 0) { if(vsession->host_url!=NULL) { free(vsession->host_url); } /* Only first proposed URL will be used */ vsession->host_url = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str); /* Check if proposed URL is correct */ ret = v_parse_url(vsession->host_url, &url); if(ret == 1) host_url_proposed = 1; else host_url_proposed = 0; } /* Client has to propose host cookie in this state */ } else if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_COOKIE) { if(r_message->sys_cmd[i].negotiate_cmd.count > 0) { if(vsession->host_cookie.str != NULL) { free(vsession->host_cookie.str); } vsession->host_cookie.str = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str); host_cookie_proposed = 1; } } else { v_print_log(VRS_PRINT_WARNING, "This feature id: %d is not supported in this state\n", r_message->sys_cmd[i].negotiate_cmd.feature); } break; case CMD_CHANGE_L_ID: /* Client could propose client name and version */ if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_NAME) { if(r_message->sys_cmd[i].negotiate_cmd.count > 0) { /* Only first proposed client name will be used */ vsession->client_name = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str); client_name_proposed = 1; } } else if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_CLIENT_VERSION) { if(r_message->sys_cmd[i].negotiate_cmd.count > 0) { /* Only first proposed client name will be used */ vsession->client_version = strdup((char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str); client_version_proposed = 1; } } break; case CMD_CONFIRM_R_ID: /* Client has to confirm peer cookie in this state */ if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_COOKIE) { if (r_message->sys_cmd[i].negotiate_cmd.count == 1) { if(vsession->peer_cookie.str != NULL && strcmp(vsession->peer_cookie.str, (char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str) == 0) { gettimeofday(&tv, NULL); vsession->peer_cookie.tv.tv_sec = tv.tv_sec; vsession->peer_cookie.tv.tv_usec = tv.tv_usec; peer_cookie_confirmed = 1; } } } else { v_print_log(VRS_PRINT_WARNING, "This feature id: %d is not supported in this state\n", r_message->sys_cmd[i].negotiate_cmd.feature); } break; case CMD_CONFIRM_L_ID: /* Client has to confirm DED in this state */ if(r_message->sys_cmd[i].negotiate_cmd.feature == FTR_DED) { if(r_message->sys_cmd[i].negotiate_cmd.count == 1) { if(vsession->ded.str != NULL && strcmp(vsession->ded.str, (char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str) == 0) { ded_confirmed = 1; } else { printf("%s != %s\n", vsession->ded.str, (char*)r_message->sys_cmd[i].negotiate_cmd.value[0].string8.str); } } } break; default: v_print_log(VRS_PRINT_WARNING, "This command id: %d is not supported in this state\n", r_message->sys_cmd[i].cmd.id); break; } } /* Send response on cookie request */ if(host_url_proposed==1 && host_cookie_proposed==1 && peer_cookie_confirmed==1 && ded_confirmed==1) { struct vContext *new_C; char trans_proto[4]; char sec_proto[5]; int cmd_rank = 0; buffer_pos = VERSE_MESSAGE_HEADER_SIZE; /* Copy address of peer */ memcpy(&vsession->peer_address, &io_ctx->peer_addr, sizeof(struct VNetworkAddress)); /* Do not confirm proposed URL */ v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_R_ID, FTR_HOST_URL, NULL); /* Find first unused port from port range */ for(i=vs_ctx->port_low, j=0; i<vs_ctx->port_high; i++, j++) { if(!(vs_ctx->port_list[j].flag & SERVER_PORT_USED)) { vsession->dgram_conn->io_ctx.host_addr.port = vs_ctx->port_list[j].port_number; vs_ctx->port_list[j].flag |= SERVER_PORT_USED; break; } } /* Do not allow unsecure TCP data connection */ if(url.transport_protocol == VRS_TP_TCP) { url.security_protocol = VRS_SEC_DATA_TLS; } /* Copy settings about data connection to the session */ vsession->flags |= url.security_protocol; vsession->flags |= url.transport_protocol; if(vsession->flags & VRS_TP_UDP) { strncpy(trans_proto, "udp", 3); trans_proto[3] = '\0'; /* Create copy of new Verse context for new thread */ new_C = (struct vContext*)calloc(1, sizeof(struct vContext)); memcpy(new_C, C, sizeof(struct vContext)); /* Try to create new thread */ if((ret = pthread_create(&vsession->udp_thread, NULL, vs_main_dgram_loop, (void*)new_C)) != 0) { if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "pthread_create(): %s\n", strerror(errno)); ret = 0; goto end; } /* Wait for datagram thread to be in LISTEN state */ while(vsession->dgram_conn->host_state != UDP_SERVER_STATE_LISTEN) { /* Sleep 1 milisecond */ usleep(1000); } } else if(vsession->flags & VRS_TP_TCP) { strncpy(trans_proto, "tcp", 3); trans_proto[3] = '\0'; } else if(vsession->flags & VRS_TP_WEBSOCKET) { strncpy(trans_proto, "wss", 3); trans_proto[3] = '\0'; } #if (defined WITH_OPENSSL) && OPENSSL_VERSION_NUMBER>=0x10000000 if(url.security_protocol==VRS_SEC_DATA_NONE || !(vs_ctx->security_protocol & VRS_SEC_DATA_TLS)) { strncpy(sec_proto, "none", 4); sec_proto[4] = '\0'; } else if(url.security_protocol==VRS_SEC_DATA_TLS) { if(vsession->flags & VRS_TP_UDP) { strncpy(sec_proto, "dtls", 4); sec_proto[4] = '\0'; } else if((vsession->flags & VRS_TP_TCP) || (vsession->flags & VRS_TP_WEBSOCKET)) { strncpy(sec_proto, "tls", 3); sec_proto[3] = '\0'; } } else { strncpy(sec_proto, "none", 4); sec_proto[4] = '\0'; } #else strncpy(sec_proto, "none", 4); sec_proto[4] = '\0'; #endif /* Free proposed and now obsolete URL */ if(vsession->host_url != NULL) { free(vsession->host_url); vsession->host_url = NULL; } /* Set right host URL */ vsession->host_url = calloc(UCHAR_MAX, sizeof(char)); if(url.ip_ver==IPV6) { sprintf(vsession->host_url, "verse-%s-%s://[%s]:%d", trans_proto, sec_proto, url.node, vsession->dgram_conn->io_ctx.host_addr.port); } else { sprintf(vsession->host_url, "verse-%s-%s://%s:%d", trans_proto, sec_proto, url.node, vsession->dgram_conn->io_ctx.host_addr.port); } v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CHANGE_L_ID, FTR_HOST_URL, vsession->host_url, NULL); /* Set time for the host cookie */ gettimeofday(&tv, NULL); vsession->host_cookie.tv.tv_sec = tv.tv_sec; vsession->host_cookie.tv.tv_usec = tv.tv_usec; /* Send confirmation about host cookie */ v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_R_ID, FTR_COOKIE, vsession->host_cookie.str, NULL); /* Send confirmation about client name */ if(client_name_proposed == 1) { v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_NAME, vsession->client_name, NULL); } /* Send confirmation about client version only in situation, when * client proposed client name too */ if(client_version_proposed == 1) { if(client_name_proposed == 1) { v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION, vsession->client_version, NULL); } else { /* Client version without client name is not allowed */ v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank++, CMD_CONFIRM_L_ID, FTR_CLIENT_VERSION, NULL); } } /* Pack all system commands to the buffer */ buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]); /* Update length of message in the header (data in buffer) */ s_message->header.version = VRS_VERSION; s_message->header.len = io_ctx->buf_size = buffer_pos; /* Pack header to the beginning of the buffer */ v_pack_message_header(s_message, io_ctx->buf); v_print_send_message(C); ret = 1; } else { ret = 0; } end: v_clear_url(&url); return ret; }
/* Create new UDP connection to the server */ struct VDgramConn *vc_create_client_dgram_conn(struct vContext *C) { struct VSession *vsession = CTX_current_session(C); struct VDgramConn *dgram_conn = NULL; struct addrinfo hints, *result, *rp; int sockfd; int flag, ret; struct VURL url; /* 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 */ struct timeval tv; gettimeofday(&tv, NULL); srand(tv.tv_sec - tv.tv_usec); #endif if (v_parse_url(vsession->host_url, &url) != 1) { goto end; } else { /* v_print_url(VRS_PRINT_DEBUG_MSG, &url); */ } memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_DGRAM; /* Allow datagram protocol */ hints.ai_flags = 0; /* No flags required */ hints.ai_protocol = IPPROTO_UDP; /* Allow UDP protocol only */ if(is_log_level(VRS_PRINT_DEBUG_MSG)) { if(url.ip_ver==IPV6) { v_print_log(VRS_PRINT_DEBUG_MSG, "Try to connect to: [%s]:%s\n", url.node, url.service); } else { v_print_log(VRS_PRINT_DEBUG_MSG, "Try to connect to: %s:%s\n", url.node, url.service); } } if( (ret = getaddrinfo(url.node, url.service, &hints, &result)) !=0 ) { if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "getaddrinfo(): %s\n", gai_strerror(ret)); goto end; } /* Try to use addrinfo from getaddrinfo() */ for(rp=result; rp!=NULL; rp=rp->ai_next) { if( (sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) { if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "socket(): %s\n", strerror(errno)); continue; } else { /* Try to "connect" to this address ... the client will be able to send and * receive packets only from this address. */ if(connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1) break; close(sockfd); } } if(rp==NULL) { if(is_log_level(VRS_PRINT_ERROR)) { if(url.ip_ver==IPV6) { v_print_log(VRS_PRINT_ERROR, "Could not connect to the [%s]:%s\n", url.node, url.service); } else { v_print_log(VRS_PRINT_ERROR, "Could not connect to the %s:%s\n", url.node, url.service); } } freeaddrinfo(result); goto end; } if( (dgram_conn = (struct VDgramConn*)calloc(1, sizeof(struct VDgramConn))) == NULL) { if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "malloc(): %s\n", strerror(errno)); freeaddrinfo(result); goto end; } /* Initialize datagram connection */ v_conn_dgram_init(dgram_conn); /* Use first successfully assigned socket */ dgram_conn->io_ctx.sockfd = sockfd; /* Set socket non-blocking */ flag = fcntl(dgram_conn->io_ctx.sockfd, F_GETFL, 0); if( (fcntl(dgram_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)); free(dgram_conn); freeaddrinfo(result); goto end; } /* Set socket to reuse address */ flag = 1; if( setsockopt(dgram_conn->io_ctx.sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) { if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "setsockopt(): %s\n", strerror(errno)); free(dgram_conn); goto end; } /* Set address of peer and host */ if(rp->ai_family==AF_INET) { /* Address type of client */ dgram_conn->io_ctx.host_addr.ip_ver = IPV4; dgram_conn->io_ctx.host_addr.protocol = UDP; /* Address of peer */ dgram_conn->io_ctx.peer_addr.ip_ver = IPV4; dgram_conn->io_ctx.peer_addr.protocol = UDP; dgram_conn->io_ctx.peer_addr.port = ntohs(((struct sockaddr_in*)rp->ai_addr)->sin_port); memcpy(&dgram_conn->io_ctx.peer_addr.addr.ipv4, rp->ai_addr, rp->ai_addrlen); /* Address of peer (reference in connection) */ dgram_conn->peer_address.ip_ver = IPV4; dgram_conn->peer_address.protocol = UDP; dgram_conn->peer_address.port = ntohs(((struct sockaddr_in*)rp->ai_addr)->sin_port); memcpy(&dgram_conn->peer_address.addr.ipv4, rp->ai_addr, rp->ai_addrlen); } else if(rp->ai_family==AF_INET6) { /* Address type of client */ dgram_conn->io_ctx.host_addr.ip_ver = IPV6; dgram_conn->io_ctx.host_addr.protocol = UDP; /* Address of peer */ dgram_conn->io_ctx.peer_addr.ip_ver = IPV6; dgram_conn->io_ctx.peer_addr.protocol = UDP; dgram_conn->io_ctx.peer_addr.port = ntohs(((struct sockaddr_in6*)rp->ai_addr)->sin6_port); memcpy(&dgram_conn->io_ctx.peer_addr.addr.ipv6, rp->ai_addr, rp->ai_addrlen); /* Address of peer (reference in connection) */ dgram_conn->peer_address.ip_ver = IPV6; dgram_conn->peer_address.protocol = UDP; dgram_conn->peer_address.port = ntohs(((struct sockaddr_in6*)rp->ai_addr)->sin6_port); memcpy(&dgram_conn->peer_address.addr.ipv6, rp->ai_addr, rp->ai_addrlen); } freeaddrinfo(result); /* When DTLS was negotiated, then set flag */ if(url.security_protocol == VRS_SEC_DATA_TLS) { #if (defined WITH_OPENSSL) && OPENSSL_VERSION_NUMBER>=0x10000000 dgram_conn->io_ctx.flags |= SOCKET_SECURED; #else v_print_log(VRS_PRINT_ERROR, "Server tries to force client to use secured connection, but it is not supported\n"); goto end; #endif } #ifdef WITH_OPENSSL /* Create BIO, connect and set to already connected */ if( (dgram_conn->io_ctx.bio = BIO_new_dgram(dgram_conn->io_ctx.sockfd, BIO_CLOSE)) == NULL) { v_print_log(VRS_PRINT_ERROR, "BIO_new_dgram()\n"); goto end; } /* Try to do PMTU discovery */ if( BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL) < 0) { v_print_log(VRS_PRINT_ERROR, "BIO_ctrl()\n"); goto end; } /* Try to get MTU from the bio */ ret = BIO_ctrl(dgram_conn->io_ctx.bio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); if(ret > 0) { dgram_conn->io_ctx.mtu = ret; v_print_log(VRS_PRINT_DEBUG_MSG, "PMTU: %d\n", dgram_conn->io_ctx.mtu); } else { dgram_conn->io_ctx.mtu = DEFAULT_MTU; v_print_log(VRS_PRINT_DEBUG_MSG, "Default MTU: %d\n", dgram_conn->io_ctx.mtu); } #else dgram_conn->io_ctx.mtu = DEFAULT_MTU; #endif /* Set up necessary flag for V_CTX (client will be able to send and receive packets only to/from server) */ dgram_conn->io_ctx.flags |= SOCKET_CONNECTED; dgram_conn->host_id = (unsigned int)rand(); end: v_clear_url(&url); return dgram_conn; }