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