Ejemplo n.º 1
0
/**
 * \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;
}
Ejemplo n.º 2
0
/* 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;
}