Blame doc/examples/ex-serv-dtls.c

Packit Service 4684c1
/* This example code is placed in the public domain. */
Packit Service 4684c1
Packit Service 4684c1
#ifdef HAVE_CONFIG_H
Packit Service 4684c1
#include <config.h>
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
#include <stdio.h>
Packit Service 4684c1
#include <stdlib.h>
Packit Service 4684c1
#include <errno.h>
Packit Service 4684c1
#include <sys/types.h>
Packit Service 4684c1
#include <sys/socket.h>
Packit Service 4684c1
#include <arpa/inet.h>
Packit Service 4684c1
#include <netinet/in.h>
Packit Service 4684c1
#include <sys/select.h>
Packit Service 4684c1
#include <netdb.h>
Packit Service 4684c1
#include <string.h>
Packit Service 4684c1
#include <unistd.h>
Packit Service 4684c1
#include <gnutls/gnutls.h>
Packit Service 4684c1
#include <gnutls/dtls.h>
Packit Service 4684c1
Packit Service 4684c1
#define KEYFILE "key.pem"
Packit Service 4684c1
#define CERTFILE "cert.pem"
Packit Service 4684c1
#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
Packit Service 4684c1
#define CRLFILE "crl.pem"
Packit Service 4684c1
Packit Service 4684c1
/* This is a sample DTLS echo server, using X.509 authentication.
Packit Service 4684c1
 * Note that error checking is minimal to simplify the example.
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
#define LOOP_CHECK(rval, cmd) \
Packit Service 4684c1
        do { \
Packit Service 4684c1
                rval = cmd; \
Packit Service 4684c1
        } while(rval == GNUTLS_E_AGAIN || rval == GNUTLS_E_INTERRUPTED)
Packit Service 4684c1
Packit Service 4684c1
#define MAX_BUFFER 1024
Packit Service 4684c1
#define PORT 5557
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 *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
static const char *human_addr(const struct sockaddr *sa, socklen_t salen,
Packit Service 4684c1
                              char *buf, size_t buflen);
Packit Service 4684c1
static int wait_for_connection(int fd);
Packit Service 4684c1
Packit Service 4684c1
/* Use global credentials and parameters to simplify
Packit Service 4684c1
 * the example. */
Packit Service 4684c1
static gnutls_certificate_credentials_t x509_cred;
Packit Service 4684c1
static gnutls_priority_t priority_cache;
Packit Service 4684c1
Packit Service 4684c1
int main(void)
Packit Service 4684c1
{
Packit Service 4684c1
        int listen_sd;
Packit Service 4684c1
        int sock, ret;
Packit Service 4684c1
        struct sockaddr_in sa_serv;
Packit Service 4684c1
        struct sockaddr_in cli_addr;
Packit Service 4684c1
        socklen_t cli_addr_size;
Packit Service 4684c1
        gnutls_session_t session;
Packit Service 4684c1
        char buffer[MAX_BUFFER];
Packit Service 4684c1
        priv_data_st priv;
Packit Service 4684c1
        gnutls_datum_t cookie_key;
Packit Service 4684c1
        gnutls_dtls_prestate_st prestate;
Packit Service 4684c1
        int mtu = 1400;
Packit Service 4684c1
        unsigned char sequence[8];
Packit Service 4684c1
Packit Service 4684c1
        /* this must be called once in the program
Packit Service 4684c1
         */
Packit Service 4684c1
        gnutls_global_init();
Packit Service 4684c1
Packit Service 4684c1
        gnutls_certificate_allocate_credentials(&x509_cred);
Packit Service 4684c1
        gnutls_certificate_set_x509_trust_file(x509_cred, CAFILE,
Packit Service 4684c1
                                               GNUTLS_X509_FMT_PEM);
Packit Service 4684c1
Packit Service 4684c1
        gnutls_certificate_set_x509_crl_file(x509_cred, CRLFILE,
Packit Service 4684c1
                                             GNUTLS_X509_FMT_PEM);
Packit Service 4684c1
Packit Service 4684c1
        ret =
Packit Service 4684c1
            gnutls_certificate_set_x509_key_file(x509_cred, CERTFILE,
Packit Service 4684c1
                                                 KEYFILE,
Packit Service 4684c1
                                                 GNUTLS_X509_FMT_PEM);
Packit Service 4684c1
        if (ret < 0) {
Packit Service 4684c1
                printf("No certificate or key were found\n");
Packit Service 4684c1
                exit(1);
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
        gnutls_certificate_set_known_dh_params(x509_cred, GNUTLS_SEC_PARAM_MEDIUM);
Packit Service 4684c1
Packit Service 4684c1
        /* pre-3.6.3 equivalent:
Packit Service 4684c1
         * gnutls_priority_init(&priority_cache,
Packit Service 4684c1
         *                      "NORMAL:-VERS-TLS-ALL:+VERS-DTLS1.0:%SERVER_PRECEDENCE",
Packit Service 4684c1
         *                      NULL);
Packit Service 4684c1
         */
Packit Service 4684c1
        gnutls_priority_init2(&priority_cache,
Packit Service 4684c1
                              "%SERVER_PRECEDENCE",
Packit Service 4684c1
                              NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
Packit Service 4684c1
Packit Service 4684c1
        gnutls_key_generate(&cookie_key, GNUTLS_COOKIE_KEY_SIZE);
Packit Service 4684c1
Packit Service 4684c1
        /* Socket operations
Packit Service 4684c1
         */
Packit Service 4684c1
        listen_sd = socket(AF_INET, SOCK_DGRAM, 0);
Packit Service 4684c1
Packit Service 4684c1
        memset(&sa_serv, '\0', sizeof(sa_serv));
Packit Service 4684c1
        sa_serv.sin_family = AF_INET;
Packit Service 4684c1
        sa_serv.sin_addr.s_addr = INADDR_ANY;
Packit Service 4684c1
        sa_serv.sin_port = htons(PORT);
Packit Service 4684c1
Packit Service 4684c1
        {                       /* DTLS requires the IP don't fragment (DF) bit to be set */
Packit Service 4684c1
#if defined(IP_DONTFRAG)
Packit Service 4684c1
                int optval = 1;
Packit Service 4684c1
                setsockopt(listen_sd, IPPROTO_IP, IP_DONTFRAG,
Packit Service 4684c1
                           (const void *) &optval, sizeof(optval));
Packit Service 4684c1
#elif defined(IP_MTU_DISCOVER)
Packit Service 4684c1
                int optval = IP_PMTUDISC_DO;
Packit Service 4684c1
                setsockopt(listen_sd, IPPROTO_IP, IP_MTU_DISCOVER,
Packit Service 4684c1
                           (const void *) &optval, sizeof(optval));
Packit Service 4684c1
#endif
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
        bind(listen_sd, (struct sockaddr *) &sa_serv, sizeof(sa_serv));
Packit Service 4684c1
Packit Service 4684c1
        printf("UDP server ready. Listening to port '%d'.\n\n", PORT);
Packit Service 4684c1
Packit Service 4684c1
        for (;;) {
Packit Service 4684c1
                printf("Waiting for connection...\n");
Packit Service 4684c1
                sock = wait_for_connection(listen_sd);
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 = recvfrom(sock, buffer, sizeof(buffer), MSG_PEEK,
Packit Service 4684c1
                               (struct sockaddr *) &cli_addr,
Packit Service 4684c1
                               &cli_addr_size);
Packit Service 4684c1
                if (ret > 0) {
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
                                                      sizeof(cli_addr),
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 = sizeof(cli_addr);
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
                                                sizeof(cli_addr), buffer,
Packit Service 4684c1
                                                sizeof(buffer)));
Packit Service 4684c1
Packit Service 4684c1
                                gnutls_dtls_cookie_send(&cookie_key,
Packit Service 4684c1
                                                        &cli_addr,
Packit Service 4684c1
                                                        sizeof(cli_addr),
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), 0,
Packit Service 4684c1
                                         (struct sockaddr *) &cli_addr,
Packit Service 4684c1
                                         &cli_addr_size);
Packit Service 4684c1
                                usleep(100);
Packit Service 4684c1
                                continue;
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)));
Packit Service 4684c1
                } else
Packit Service 4684c1
                        continue;
Packit Service 4684c1
Packit Service 4684c1
                gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
Packit Service 4684c1
                gnutls_priority_set(session, priority_cache);
Packit Service 4684c1
                gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
Packit Service 4684c1
                                       x509_cred);
Packit Service 4684c1
Packit Service 4684c1
                gnutls_dtls_prestate_set(session, &prestate);
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 = (struct sockaddr *) &cli_addr;
Packit Service 4684c1
                priv.cli_addr_size = sizeof(cli_addr);
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
                LOOP_CHECK(ret, gnutls_handshake(session));
Packit Service 4684c1
                /* Note that DTLS may also receive GNUTLS_E_LARGE_PACKET.
Packit Service 4684c1
                 * In that case the MTU should be adjusted.
Packit Service 4684c1
                 */
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
                printf("- Handshake was completed\n");
Packit Service 4684c1
Packit Service 4684c1
                for (;;) {
Packit Service 4684c1
                        LOOP_CHECK(ret,
Packit Service 4684c1
                                    gnutls_record_recv_seq(session, buffer,
Packit Service 4684c1
                                                           MAX_BUFFER,
Packit Service 4684c1
                                                           sequence));
Packit Service 4684c1
Packit Service 4684c1
                        if (ret < 0 && gnutls_error_is_fatal(ret) == 0) {
Packit Service 4684c1
                                fprintf(stderr, "*** Warning: %s\n",
Packit Service 4684c1
                                        gnutls_strerror(ret));
Packit Service 4684c1
                                continue;
Packit Service 4684c1
                        } else 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
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
                        /* reply back */
Packit Service 4684c1
                        LOOP_CHECK(ret, gnutls_record_send(session, buffer, ret));
Packit Service 4684c1
                        if (ret < 0) {
Packit Service 4684c1
                                fprintf(stderr, "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
                LOOP_CHECK(ret, gnutls_bye(session, GNUTLS_SHUT_WR));
Packit Service 4684c1
                gnutls_deinit(session);
Packit Service 4684c1
Packit Service 4684c1
        }
Packit Service 4684c1
        close(listen_sd);
Packit Service 4684c1
Packit Service 4684c1
        gnutls_certificate_free_credentials(x509_cred);
Packit Service 4684c1
        gnutls_priority_deinit(priority_cache);
Packit Service 4684c1
Packit Service 4684c1
        gnutls_global_deinit();
Packit Service 4684c1
Packit Service 4684c1
        return 0;
Packit Service 4684c1
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int wait_for_connection(int fd)
Packit Service 4684c1
{
Packit Service 4684c1
        fd_set rd, wr;
Packit Service 4684c1
        int n;
Packit Service 4684c1
Packit Service 4684c1
        FD_ZERO(&rd);
Packit Service 4684c1
        FD_ZERO(&wr);
Packit Service 4684c1
Packit Service 4684c1
        FD_SET(fd, &rd);
Packit Service 4684c1
Packit Service 4684c1
        /* waiting part */
Packit Service 4684c1
        n = select(fd + 1, &rd, &wr, NULL, NULL);
Packit Service 4684c1
        if (n == -1 && errno == EINTR)
Packit Service 4684c1
                return -1;
Packit Service 4684c1
        if (n < 0) {
Packit Service 4684c1
                perror("select()");
Packit Service 4684c1
                exit(1);
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
        return fd;
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
Packit Service 4684c1
push_func(gnutls_transport_ptr_t p, const void *data, 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, 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
}
Packit Service 4684c1
Packit Service 4684c1
static const char *human_addr(const struct sockaddr *sa, socklen_t salen,
Packit Service 4684c1
                              char *buf, size_t buflen)
Packit Service 4684c1
{
Packit Service 4684c1
        const char *save_buf = buf;
Packit Service 4684c1
        size_t l;
Packit Service 4684c1
Packit Service 4684c1
        if (!buf || !buflen)
Packit Service 4684c1
                return NULL;
Packit Service 4684c1
Packit Service 4684c1
        *buf = '\0';
Packit Service 4684c1
Packit Service 4684c1
        switch (sa->sa_family) {
Packit Service 4684c1
#if HAVE_IPV6
Packit Service 4684c1
        case AF_INET6:
Packit Service 4684c1
                snprintf(buf, buflen, "IPv6 ");
Packit Service 4684c1
                break;
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
        case AF_INET:
Packit Service 4684c1
                snprintf(buf, buflen, "IPv4 ");
Packit Service 4684c1
                break;
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
        l = strlen(buf);
Packit Service 4684c1
        buf += l;
Packit Service 4684c1
        buflen -= l;
Packit Service 4684c1
Packit Service 4684c1
        if (getnameinfo(sa, salen, buf, buflen, NULL, 0, NI_NUMERICHOST) !=
Packit Service 4684c1
            0)
Packit Service 4684c1
                return NULL;
Packit Service 4684c1
Packit Service 4684c1
        l = strlen(buf);
Packit Service 4684c1
        buf += l;
Packit Service 4684c1
        buflen -= l;
Packit Service 4684c1
Packit Service 4684c1
        strncat(buf, " port ", buflen);
Packit Service 4684c1
Packit Service 4684c1
        l = strlen(buf);
Packit Service 4684c1
        buf += l;
Packit Service 4684c1
        buflen -= l;
Packit Service 4684c1
Packit Service 4684c1
        if (getnameinfo(sa, salen, NULL, 0, buf, buflen, NI_NUMERICSERV) !=
Packit Service 4684c1
            0)
Packit Service 4684c1
                return NULL;
Packit Service 4684c1
Packit Service 4684c1
        return save_buf;
Packit Service 4684c1
}
Packit Service 4684c1