Blame src/udp-serv.c

Packit Service 4684c1
/*
Packit Service 4684c1
 * Copyright (C) 2011-2012 Free Software Foundation, Inc.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This file is part of GnuTLS.
Packit Service 4684c1
 *
Packit Service 4684c1
 * GnuTLS is free software: you can redistribute it and/or modify
Packit Service 4684c1
 * it under the terms of the GNU General Public License as published by
Packit Service 4684c1
 * the Free Software Foundation, either version 3 of the License, or
Packit Service 4684c1
 * (at your option) any later version.
Packit Service 4684c1
 *
Packit Service 4684c1
 * GnuTLS is distributed in the hope that it will be useful,
Packit Service 4684c1
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 4684c1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 4684c1
 * GNU General Public License for more details.
Packit Service 4684c1
 *
Packit Service 4684c1
 * You should have received a copy of the GNU General Public License
Packit Service 4684c1
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
#include <config.h>
Packit Service 4684c1
Packit Service 4684c1
#include <stdio.h>
Packit Service 4684c1
#if HAVE_SYS_SOCKET_H
Packit Service 4684c1
#include <sys/socket.h>
Packit Service 4684c1
#elif HAVE_WS2TCPIP_H
Packit Service 4684c1
#include <ws2tcpip.h>
Packit Service 4684c1
#endif
Packit Service 4684c1
#include <arpa/inet.h>
Packit Service 4684c1
#ifndef _WIN32
Packit Service 4684c1
#include <netinet/in.h>
Packit Service 4684c1
#endif
Packit Service 4684c1
#include <sys/select.h>
Packit Service 4684c1
#include <stdlib.h>
Packit Service 4684c1
#include <string.h>
Packit Service 4684c1
#include <unistd.h>
Packit Service 4684c1
#include <errno.h>
Packit Service 4684c1
#include <common.h>
Packit Service 4684c1
#include "udp-serv.h"
Packit Service 4684c1
#include "serv-args.h"
Packit Service 4684c1
#include "list.h"
Packit Service 4684c1
Packit Service 4684c1
extern int disable_client_cert;
Packit Service 4684c1
Packit Service 4684c1
typedef struct {
Packit Service 4684c1
	gnutls_session_t session;
Packit Service 4684c1
	int fd;
Packit Service 4684c1
	struct sockaddr_storage *cli_addr;
Packit Service 4684c1
	socklen_t cli_addr_size;
Packit Service 4684c1
} priv_data_st;
Packit Service 4684c1
Packit Service 4684c1
static int pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms);
Packit Service 4684c1
static ssize_t push_func(gnutls_transport_ptr_t p, const void *data,
Packit Service 4684c1
			 size_t size);
Packit Service 4684c1
static ssize_t pull_func(gnutls_transport_ptr_t p, void *data,
Packit Service 4684c1
			 size_t size);
Packit Service 4684c1
Packit Service 4684c1
#define MAX_BUFFER 255		/* Longest string to echo */
Packit Service 4684c1
Packit Service 4684c1
/* record layer indication for a handshake packet */
Packit Service 4684c1
#define HANDSHAKE_CONTENT_TYPE 22
Packit Service 4684c1
/* TLS record content is the first by of the packet */
Packit Service 4684c1
#define RECORD_CONTENT_POS 0
Packit Service 4684c1
/* handshake type is first byte in Handshake packet;
Packit Service 4684c1
 * we have to skip type;version;epoch;sequence_number;
Packit Service 4684c1
 * and length in DTLSPlaintext */
Packit Service 4684c1
#define HANDSHAKE_TYPE_POS 13
Packit Service 4684c1
Packit Service 4684c1
void udp_server(const char *name, int port, int mtu)
Packit Service 4684c1
{
Packit Service 4684c1
	int sock, ret;
Packit Service 4684c1
	struct sockaddr_storage cli_addr;
Packit Service 4684c1
	socklen_t cli_addr_size;
Packit Service 4684c1
	char buffer[MAX_BUFFER];
Packit Service 4684c1
	priv_data_st priv;
Packit Service 4684c1
	gnutls_session_t session;
Packit Service 4684c1
	gnutls_datum_t cookie_key;
Packit Service 4684c1
	gnutls_dtls_prestate_st prestate;
Packit Service 4684c1
	unsigned char sequence[8];
Packit Service 4684c1
Packit Service 4684c1
	ret = gnutls_key_generate(&cookie_key, GNUTLS_COOKIE_KEY_SIZE);
Packit Service 4684c1
	if (ret < 0) {
Packit Service 4684c1
		fprintf(stderr, "Cannot generate key\n");
Packit Service 4684c1
		exit(1);
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	ret = listen_socket(name, port, SOCK_DGRAM);
Packit Service 4684c1
	if (ret < 0) {
Packit Service 4684c1
		fprintf(stderr, "Cannot listen\n");
Packit Service 4684c1
		exit(1);
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	for (;;) {
Packit Service 4684c1
		printf("Waiting for connection...\n");
Packit Service 4684c1
		sock = wait_for_connection();
Packit Service 4684c1
		if (sock < 0)
Packit Service 4684c1
			continue;
Packit Service 4684c1
Packit Service 4684c1
		cli_addr_size = sizeof(cli_addr);
Packit Service 4684c1
		ret =
Packit Service 4684c1
		    recvfrom(sock, buffer, sizeof(buffer)-1, MSG_PEEK,
Packit Service 4684c1
			     (struct sockaddr *) &cli_addr,
Packit Service 4684c1
			     &cli_addr_size);
Packit Service 4684c1
Packit Service 4684c1
		/* only accept a valid client hello */
Packit Service 4684c1
		if (ret > HANDSHAKE_TYPE_POS &&
Packit Service 4684c1
		    buffer[RECORD_CONTENT_POS] == HANDSHAKE_CONTENT_TYPE &&
Packit Service 4684c1
		    buffer[HANDSHAKE_TYPE_POS] == GNUTLS_HANDSHAKE_CLIENT_HELLO) {
Packit Service 4684c1
			if (!HAVE_OPT(NOCOOKIE)) {
Packit Service 4684c1
				memset(&prestate, 0, sizeof(prestate));
Packit Service 4684c1
				ret =
Packit Service 4684c1
				    gnutls_dtls_cookie_verify(&cookie_key,
Packit Service 4684c1
							      &cli_addr,
Packit Service 4684c1
							      cli_addr_size,
Packit Service 4684c1
							      buffer, ret,
Packit Service 4684c1
							      &prestate);
Packit Service 4684c1
				if (ret < 0) {	/* cookie not valid */
Packit Service 4684c1
					priv_data_st s;
Packit Service 4684c1
Packit Service 4684c1
					memset(&s, 0, sizeof(s));
Packit Service 4684c1
					s.fd = sock;
Packit Service 4684c1
					s.cli_addr = (void *) &cli_addr;
Packit Service 4684c1
					s.cli_addr_size = cli_addr_size;
Packit Service 4684c1
Packit Service 4684c1
					printf
Packit Service 4684c1
					    ("Sending hello verify request to %s\n",
Packit Service 4684c1
					     human_addr((struct sockaddr *)
Packit Service 4684c1
							&cli_addr,
Packit Service 4684c1
							cli_addr_size, buffer,
Packit Service 4684c1
							sizeof(buffer)-1));
Packit Service 4684c1
					gnutls_dtls_cookie_send(&cookie_key,
Packit Service 4684c1
								&cli_addr,
Packit Service 4684c1
								cli_addr_size,
Packit Service 4684c1
								&prestate,
Packit Service 4684c1
								(gnutls_transport_ptr_t)
Packit Service 4684c1
								&s, push_func);
Packit Service 4684c1
Packit Service 4684c1
					/* discard peeked data */
Packit Service 4684c1
					recvfrom(sock, buffer, sizeof(buffer)-1, 0,
Packit Service 4684c1
						 (struct sockaddr *) &cli_addr,
Packit Service 4684c1
						 &cli_addr_size);
Packit Service 4684c1
					continue;
Packit Service 4684c1
				}
Packit Service 4684c1
			}
Packit Service 4684c1
			printf("Accepted connection from %s\n",
Packit Service 4684c1
			       human_addr((struct sockaddr *)
Packit Service 4684c1
					  &cli_addr, sizeof(cli_addr),
Packit Service 4684c1
					  buffer, sizeof(buffer)-1));
Packit Service 4684c1
		} else
Packit Service 4684c1
			continue;
Packit Service 4684c1
Packit Service 4684c1
		session = initialize_session(1);
Packit Service 4684c1
		if (!HAVE_OPT(NOCOOKIE))
Packit Service 4684c1
			gnutls_dtls_prestate_set(session, &prestate);
Packit Service 4684c1
Packit Service 4684c1
		if (mtu)
Packit Service 4684c1
			gnutls_dtls_set_mtu(session, mtu);
Packit Service 4684c1
Packit Service 4684c1
		priv.session = session;
Packit Service 4684c1
		priv.fd = sock;
Packit Service 4684c1
		priv.cli_addr = &cli_addr;
Packit Service 4684c1
		priv.cli_addr_size = cli_addr_size;
Packit Service 4684c1
Packit Service 4684c1
		gnutls_transport_set_ptr(session, &priv;;
Packit Service 4684c1
		gnutls_transport_set_push_function(session, push_func);
Packit Service 4684c1
		gnutls_transport_set_pull_function(session, pull_func);
Packit Service 4684c1
		gnutls_transport_set_pull_timeout_function(session,
Packit Service 4684c1
							   pull_timeout_func);
Packit Service 4684c1
Packit Service 4684c1
		do {
Packit Service 4684c1
			ret = gnutls_handshake(session);
Packit Service 4684c1
		}
Packit Service 4684c1
		while (ret == GNUTLS_E_AGAIN
Packit Service 4684c1
		       || ret == GNUTLS_E_INTERRUPTED);
Packit Service 4684c1
Packit Service 4684c1
		if (ret < 0) {
Packit Service 4684c1
			fprintf(stderr, "Error in handshake(): %s\n",
Packit Service 4684c1
				gnutls_strerror(ret));
Packit Service 4684c1
			gnutls_deinit(session);
Packit Service 4684c1
			continue;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		for (;;) {
Packit Service 4684c1
			do {
Packit Service 4684c1
				ret =
Packit Service 4684c1
				    gnutls_record_recv_seq(session, buffer,
Packit Service 4684c1
							   sizeof(buffer)-1,
Packit Service 4684c1
							   sequence);
Packit Service 4684c1
				if (ret ==
Packit Service 4684c1
				    GNUTLS_E_HEARTBEAT_PING_RECEIVED)
Packit Service 4684c1
					gnutls_heartbeat_pong(session, 0);
Packit Service 4684c1
			}
Packit Service 4684c1
			while (ret == GNUTLS_E_INTERRUPTED
Packit Service 4684c1
			       || ret == GNUTLS_E_AGAIN
Packit Service 4684c1
			       || ret == GNUTLS_E_HEARTBEAT_PING_RECEIVED);
Packit Service 4684c1
Packit Service 4684c1
			if (ret == GNUTLS_E_REHANDSHAKE) {
Packit Service 4684c1
				fprintf(stderr,
Packit Service 4684c1
					"*** Received hello message\n");
Packit Service 4684c1
				do {
Packit Service 4684c1
					ret = gnutls_handshake(session);
Packit Service 4684c1
				}
Packit Service 4684c1
				while (ret == GNUTLS_E_INTERRUPTED ||
Packit Service 4684c1
				       ret == GNUTLS_E_AGAIN);
Packit Service 4684c1
Packit Service 4684c1
				if (ret == 0)
Packit Service 4684c1
					continue;
Packit Service 4684c1
			}
Packit Service 4684c1
			if (ret < 0) {
Packit Service 4684c1
				fprintf(stderr, "Error in recv(): %s\n",
Packit Service 4684c1
					gnutls_strerror(ret));
Packit Service 4684c1
				break;
Packit Service 4684c1
			}
Packit Service 4684c1
			if (ret == 0) {
Packit Service 4684c1
				printf("EOF\n\n");
Packit Service 4684c1
				break;
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			buffer[ret] = 0;
Packit Service 4684c1
			printf
Packit Service 4684c1
			    ("received[%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x]: %s\n",
Packit Service 4684c1
			     sequence[0], sequence[1], sequence[2],
Packit Service 4684c1
			     sequence[3], sequence[4], sequence[5],
Packit Service 4684c1
			     sequence[6], sequence[7], buffer);
Packit Service 4684c1
Packit Service 4684c1
			if (check_command(session, buffer, disable_client_cert) == 0) {
Packit Service 4684c1
				/* reply back */
Packit Service 4684c1
				ret =
Packit Service 4684c1
				    gnutls_record_send(session, buffer,
Packit Service 4684c1
						       ret);
Packit Service 4684c1
				if (ret < 0) {
Packit Service 4684c1
					fprintf(stderr,
Packit Service 4684c1
						"Error in send(): %s\n",
Packit Service 4684c1
						gnutls_strerror(ret));
Packit Service 4684c1
					break;
Packit Service 4684c1
				}
Packit Service 4684c1
			}
Packit Service 4684c1
		}
Packit Service 4684c1
		gnutls_deinit(session);
Packit Service 4684c1
	}
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Wait for data to be received within a timeout period in milliseconds
Packit Service 4684c1
 */
Packit Service 4684c1
static int pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms)
Packit Service 4684c1
{
Packit Service 4684c1
	fd_set rfds;
Packit Service 4684c1
	struct timeval tv;
Packit Service 4684c1
	priv_data_st *priv = ptr;
Packit Service 4684c1
	struct sockaddr_in cli_addr;
Packit Service 4684c1
	socklen_t cli_addr_size;
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	char c;
Packit Service 4684c1
Packit Service 4684c1
	FD_ZERO(&rfds);
Packit Service 4684c1
	FD_SET(priv->fd, &rfds);
Packit Service 4684c1
Packit Service 4684c1
	tv.tv_sec = ms / 1000;
Packit Service 4684c1
	tv.tv_usec = (ms % 1000) * 1000;
Packit Service 4684c1
Packit Service 4684c1
	ret = select(priv->fd + 1, &rfds, NULL, NULL, &tv;;
Packit Service 4684c1
Packit Service 4684c1
	if (ret <= 0)
Packit Service 4684c1
		return ret;
Packit Service 4684c1
Packit Service 4684c1
	/* only report ok if the next message is from the peer we expect
Packit Service 4684c1
	 * from 
Packit Service 4684c1
	 */
Packit Service 4684c1
	cli_addr_size = sizeof(cli_addr);
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    recvfrom(priv->fd, &c, 1, MSG_PEEK,
Packit Service 4684c1
		     (struct sockaddr *) &cli_addr, &cli_addr_size);
Packit Service 4684c1
	if (ret > 0) {
Packit Service 4684c1
		if (cli_addr_size == priv->cli_addr_size
Packit Service 4684c1
		    && memcmp(&cli_addr, priv->cli_addr,
Packit Service 4684c1
			      sizeof(cli_addr)) == 0)
Packit Service 4684c1
			return 1;
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static ssize_t push_func(gnutls_transport_ptr_t p, const void *data,
Packit Service 4684c1
			 size_t size)
Packit Service 4684c1
{
Packit Service 4684c1
	priv_data_st *priv = p;
Packit Service 4684c1
Packit Service 4684c1
	return sendto(priv->fd, data, size, 0, (struct sockaddr*)priv->cli_addr,
Packit Service 4684c1
		      priv->cli_addr_size);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static ssize_t pull_func(gnutls_transport_ptr_t p, void *data, size_t size)
Packit Service 4684c1
{
Packit Service 4684c1
	priv_data_st *priv = p;
Packit Service 4684c1
	struct sockaddr_in cli_addr;
Packit Service 4684c1
	socklen_t cli_addr_size;
Packit Service 4684c1
	char buffer[64];
Packit Service 4684c1
	int ret;
Packit Service 4684c1
Packit Service 4684c1
	cli_addr_size = sizeof(cli_addr);
Packit Service 4684c1
	ret =
Packit Service 4684c1
	    recvfrom(priv->fd, data, size, 0,
Packit Service 4684c1
		     (struct sockaddr *) &cli_addr, &cli_addr_size);
Packit Service 4684c1
	if (ret == -1)
Packit Service 4684c1
		return ret;
Packit Service 4684c1
Packit Service 4684c1
	if (cli_addr_size == priv->cli_addr_size
Packit Service 4684c1
	    && memcmp(&cli_addr, priv->cli_addr, sizeof(cli_addr)) == 0)
Packit Service 4684c1
		return ret;
Packit Service 4684c1
Packit Service 4684c1
	printf("Denied connection from %s\n",
Packit Service 4684c1
	       human_addr((struct sockaddr *)
Packit Service 4684c1
			  &cli_addr, sizeof(cli_addr), buffer,
Packit Service 4684c1
			  sizeof(buffer)));
Packit Service 4684c1
Packit Service 4684c1
	gnutls_transport_set_errno(priv->session, EAGAIN);
Packit Service 4684c1
	return -1;
Packit Service 4684c1
}