]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added DTLS server example.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Mon, 26 Dec 2011 15:08:20 +0000 (17:08 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 28 Dec 2011 14:08:55 +0000 (16:08 +0200)
doc/cha-gtls-app.texi
doc/cha-gtls-examples.texi
doc/examples/Makefile.am
doc/examples/ex-serv-dtls.c [new file with mode: 0644]
doc/examples/ex-serv1.c

index 715471c4df1a94bd43b30013f93bfb9ca6bc5d5d..be12188529e5b8f770a0d54dece605026381500a 100644 (file)
@@ -750,7 +750,7 @@ COMP-NULL, COMP-DEFLATE. Catch all is COMP-ALL.
 
 @item TLS versions @tab
 VERS-SSL3.0, VERS-TLS1.0, VERS-TLS1.1,
-VERS-TLS1.2. Catch all is VERS-TLS-ALL.
+VERS-TLS1.2, VERS-DTLS1.0. Catch all is VERS-TLS-ALL.
 
 @item Signature algorithms @tab
 SIGN-RSA-SHA1, SIGN-RSA-SHA224, 
index 7ce4ee13ed03e9d60de5d9520b071ffda366dcd7..23f76100d89c8e647840fccaf1ef12a096d3631a 100644 (file)
@@ -150,21 +150,22 @@ This section contains examples of @acronym{TLS} and @acronym{SSL}
 servers, using @acronym{GnuTLS}.
 
 @menu
-* Echo Server with X.509 authentication::
-* Echo Server with OpenPGP authentication::
-* Echo Server with SRP authentication::
-* Echo Server with anonymous authentication::
+* Echo server with X.509 authentication::
+* Echo server with OpenPGP authentication::
+* Echo server with SRP authentication::
+* Echo server with anonymous authentication::
+* Echo DTLS server with X.509 authentication::
 @end menu
 
-@node Echo Server with X.509 authentication
+@node Echo server with X.509 authentication
 @subsection Echo server with @acronym{X.509} authentication
 
 This example is a very simple echo server which supports
-@acronym{X.509} authentication, using the RSA ciphersuites.
+@acronym{X.509} authentication.
 
 @verbatiminclude examples/ex-serv1.c
 
-@node Echo Server with OpenPGP authentication
+@node Echo server with OpenPGP authentication
 @subsection Echo server with @acronym{OpenPGP} authentication
 @cindex OpenPGP server
 
@@ -176,7 +177,7 @@ them to keep these examples as simple as possible.
 
 @verbatiminclude examples/ex-serv-pgp.c
 
-@node Echo Server with SRP authentication
+@node Echo server with SRP authentication
 @subsection Echo server with @acronym{SRP} authentication
 
 This is a server which supports @acronym{SRP} authentication. It is
@@ -185,14 +186,22 @@ server. Here it is separate for simplicity.
 
 @verbatiminclude examples/ex-serv-srp.c
 
-@node Echo Server with anonymous authentication
-@subsection Echo Server with anonymous authentication
+@node Echo server with anonymous authentication
+@subsection Echo server with anonymous authentication
 
 This example server support anonymous authentication, and could be
 used to serve the example client for anonymous authentication.
 
 @verbatiminclude examples/ex-serv-anon.c
 
+@node Echo DTLS server with X.509 authentication
+@subsection Echo DTLS server with @acronym{X.509} authentication
+
+This example is a very simple echo server using Datagram TLS and 
+@acronym{X.509} authentication.
+
+@verbatiminclude examples/ex-serv-dtls.c
+
 @node Miscellaneous examples
 @section Miscellaneous examples
 
index 0ed1eae2db4d9789e5162c206266536f69dac3a2..38d25a5dd206243d175f0a29a3b5a6bd34715f4d 100644 (file)
@@ -45,7 +45,7 @@ noinst_PROGRAMS = ex-client-resume ex-client-udp
 noinst_PROGRAMS += ex-cert-select ex-rfc2818
 
 if ENABLE_PKI
-noinst_PROGRAMS += ex-crq ex-serv1
+noinst_PROGRAMS += ex-crq ex-serv1 ex-serv-dtls
 endif
 
 if ENABLE_CXX
diff --git a/doc/examples/ex-serv-dtls.c b/doc/examples/ex-serv-dtls.c
new file mode 100644 (file)
index 0000000..bdcbe3b
--- /dev/null
@@ -0,0 +1,408 @@
+/* This example code is placed in the public domain. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+
+#define KEYFILE "key.pem"
+#define CERTFILE "cert.pem"
+#define CAFILE "ca.pem"
+#define CRLFILE "crl.pem"
+
+/* This is a sample DTLS echo server, using X.509 authentication.
+ */
+
+#define SA struct sockaddr
+#define SOCKET_ERR(err,s) if(err==-1) {perror(s);return(1);}
+#define MAX_BUFFER 1024
+#define PORT 5556               /* listen to 5556 port */
+
+typedef struct {
+  gnutls_session_t session;
+  int fd;
+  struct sockaddr * cli_addr;
+  socklen_t cli_addr_size;
+} priv_data_st;
+
+static int pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms);
+static ssize_t push_func (gnutls_transport_ptr_t p, const void * data, size_t size);
+static ssize_t pull_func(gnutls_transport_ptr_t p, void * data, size_t size);
+static const char *
+human_addr (const struct sockaddr *sa, socklen_t salen,
+            char *buf, size_t buflen);
+static int wait_for_connection(int fd);
+static gnutls_session_t initialize_tls_session (void);
+static int generate_dh_params (void);
+
+/* These are global */
+static gnutls_certificate_credentials_t x509_cred;
+static gnutls_priority_t priority_cache;
+static gnutls_dh_params_t dh_params;
+
+int
+main (void)
+{
+  int err, listen_sd;
+  int sock, ret;
+  struct sockaddr_in sa_serv;
+  struct sockaddr_in cli_addr;
+  socklen_t cli_addr_size;
+  gnutls_session_t session;
+  char buffer[MAX_BUFFER];
+  priv_data_st priv;
+  gnutls_datum_t cookie_key;
+  gnutls_dtls_prestate_st prestate;
+  int mtu = 1400;
+  unsigned char sequence[8];
+
+  /* this must be called once in the program
+   */
+  gnutls_global_init ();
+
+  gnutls_certificate_allocate_credentials (&x509_cred);
+  gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE,
+                                          GNUTLS_X509_FMT_PEM);
+
+  gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE,
+                                        GNUTLS_X509_FMT_PEM);
+
+  gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE,
+                                        GNUTLS_X509_FMT_PEM);
+
+  generate_dh_params ();
+
+  gnutls_certificate_set_dh_params (x509_cred, dh_params);
+
+  ret = gnutls_priority_init (&priority_cache, "NORMAL:-VERS-TLS-ALL:+VERS-DTLS1.0", NULL);
+  if (ret < 0)
+    {
+      fprintf(stderr, "Error in the priority string\n");
+      exit(1);
+    }
+
+  ret = gnutls_key_generate(&cookie_key, GNUTLS_COOKIE_KEY_SIZE);
+  if (ret < 0)
+    {
+      fprintf(stderr, "Cannot generate key\n");
+      exit(1);
+    }
+
+  /* Socket operations
+   */
+  listen_sd = socket (AF_INET, SOCK_DGRAM, 0);
+  SOCKET_ERR (listen_sd, "socket");
+
+  memset (&sa_serv, '\0', sizeof (sa_serv));
+  sa_serv.sin_family = AF_INET;
+  sa_serv.sin_addr.s_addr = INADDR_ANY;
+  sa_serv.sin_port = htons (PORT);      /* Server Port number */
+
+#ifdef IP_DONTFRAG
+  {
+    int optval = 1;
+    if (setsockopt (s, IPPROTO_IP, IP_DONTFRAG,
+                    (const void *) &optval, sizeof (optval)) < 0)
+      {
+        perror ("setsockopt(IP_DF) failed");
+      }
+  }
+#endif
+  err = bind (listen_sd, (SA *) &sa_serv, sizeof (sa_serv));
+  SOCKET_ERR (err, "bind");
+
+  printf ("UDP server ready. Listening to port '%d'.\n\n", PORT);
+
+  for (;;)
+    {
+      printf("Waiting for connection...\n");
+      sock = wait_for_connection(listen_sd);
+      if (sock < 0)
+        continue;
+
+      cli_addr_size = sizeof(cli_addr);
+      ret = recvfrom(sock, buffer, sizeof(buffer), MSG_PEEK, (struct sockaddr*)&cli_addr, &cli_addr_size);
+      if (ret > 0)
+        {
+          memset(&prestate, 0, sizeof(prestate));
+          ret = gnutls_dtls_cookie_verify(&cookie_key, &cli_addr, sizeof(cli_addr), buffer, ret, &prestate);
+          if (ret < 0) /* cookie not valid */
+            {
+              priv_data_st s;
+              
+              memset(&s, 0, sizeof(s));
+              s.fd = sock;
+              s.cli_addr = (void*)&cli_addr;
+              s.cli_addr_size = sizeof(cli_addr);
+              
+              printf("Sending hello verify request to %s\n", human_addr ((struct sockaddr *)
+                  &cli_addr, sizeof(cli_addr), buffer, sizeof(buffer)));
+              gnutls_dtls_cookie_send(&cookie_key, &cli_addr, sizeof(cli_addr), &prestate, (gnutls_transport_ptr_t)&s, push_func);
+
+              /* discard peeked data*/
+              recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&cli_addr, &cli_addr_size);
+              usleep(100);
+              continue;
+            }
+          printf ("Accepted connection from %s\n",
+                  human_addr ((struct sockaddr *)
+                  &cli_addr, sizeof(cli_addr), buffer,
+                  sizeof (buffer)));
+        }
+      else
+        continue;
+
+      session = initialize_tls_session ();
+      gnutls_dtls_prestate_set(session, &prestate);
+      gnutls_dtls_set_mtu(session, mtu);
+
+      priv.session = session;
+      priv.fd = sock;
+      priv.cli_addr = (struct sockaddr *)&cli_addr;
+      priv.cli_addr_size = sizeof(cli_addr);
+
+      gnutls_transport_set_ptr (session, &priv);
+      gnutls_transport_set_push_function (session, push_func);
+      gnutls_transport_set_pull_function (session, pull_func);
+      gnutls_transport_set_pull_timeout_function (session, pull_timeout_func);
+
+      do
+        {
+          ret = gnutls_handshake(session);
+        }
+      while(gnutls_error_is_fatal(ret) == 0);
+
+      if (ret < 0)
+        {
+          fprintf(stderr, "Error in handshake(): %s\n", gnutls_strerror(ret));
+          gnutls_deinit(session);
+          continue;
+        }
+
+      printf ("- Handshake was completed\n");
+
+      for(;;)
+        {
+          do {
+            ret = gnutls_record_recv_seq(session, buffer, MAX_BUFFER, sequence);
+          } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+          if (ret < 0)
+            {
+              fprintf(stderr, "Error in recv(): %s\n", gnutls_strerror(ret));
+              break;
+            }
+          if (ret == 0)
+            {
+              printf("EOF\n\n");
+              break;
+            }
+          buffer[ret] = 0;
+          printf("received[%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x]: %s\n", sequence[0], 
+                 sequence[1], sequence[2], sequence[3], sequence[4], sequence[5], 
+                 sequence[6], sequence[7], buffer);
+
+          /* reply back */
+          ret = gnutls_record_send(session, buffer, ret);
+          if (ret < 0)
+            {
+              fprintf(stderr, "Error in send(): %s\n", gnutls_strerror(ret));
+              break;
+            }
+        }
+
+      gnutls_bye (session, GNUTLS_SHUT_WR);
+      gnutls_deinit (session);
+
+    }
+  close (listen_sd);
+
+  gnutls_certificate_free_credentials (x509_cred);
+  gnutls_priority_deinit (priority_cache);
+
+  gnutls_global_deinit ();
+
+  return 0;
+
+}
+
+static int wait_for_connection(int fd)
+{
+  fd_set rd, wr;
+  int n;
+
+  FD_ZERO (&rd);
+  FD_ZERO (&wr);
+
+  FD_SET (fd, &rd);
+
+  /* waiting part */
+  n = select (fd + 1, &rd, &wr, NULL, NULL);
+  if (n == -1 && errno == EINTR)
+    return -1;
+  if (n < 0)
+    {
+      perror ("select()");
+      exit (1);
+    }
+
+  return fd;
+}
+
+/* Wait for data to be received within a timeout period in milliseconds
+ */
+static int pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms)
+{
+fd_set rfds;
+struct timeval tv;
+priv_data_st *priv = ptr;
+struct sockaddr_in cli_addr;
+socklen_t cli_addr_size;
+int ret;
+char c;
+
+  FD_ZERO(&rfds);
+  FD_SET(priv->fd, &rfds);
+  
+  tv.tv_sec = 0;
+  tv.tv_usec = ms * 1000;
+  
+  ret = select(priv->fd+1, &rfds, NULL, NULL, &tv);
+
+  if (ret <= 0)
+    return ret;
+
+  /* only report ok if the next message is from the peer we expect
+   * from 
+   */
+  cli_addr_size = sizeof(cli_addr);
+  ret = recvfrom(priv->fd, &c, 1, MSG_PEEK, (struct sockaddr*)&cli_addr, &cli_addr_size);
+  if (ret > 0)
+    {
+      if (cli_addr_size == priv->cli_addr_size && memcmp(&cli_addr, priv->cli_addr, sizeof(cli_addr))==0)
+        return 1;
+    }
+
+  return 0;
+}
+
+static ssize_t push_func (gnutls_transport_ptr_t p, const void * data, size_t size)
+{
+priv_data_st *priv = p;
+
+  return sendto(priv->fd, data, size, 0, priv->cli_addr, priv->cli_addr_size);
+}
+
+static ssize_t pull_func(gnutls_transport_ptr_t p, void * data, size_t size)
+{
+priv_data_st *priv = p;
+struct sockaddr_in cli_addr;
+socklen_t cli_addr_size;
+char buffer[64];
+int ret;
+
+  cli_addr_size = sizeof(cli_addr);
+  ret = recvfrom(priv->fd, data, size, 0, (struct sockaddr*)&cli_addr, &cli_addr_size);
+  if (ret == -1)
+    return ret;
+
+  if (cli_addr_size == priv->cli_addr_size && memcmp(&cli_addr, priv->cli_addr, sizeof(cli_addr))==0)
+    return ret;
+
+  printf ("Denied connection from %s\n",
+                human_addr ((struct sockaddr *)
+                            &cli_addr, sizeof(cli_addr), buffer,
+                            sizeof (buffer)));
+  
+  gnutls_transport_set_errno(priv->session, EAGAIN);
+  return -1;
+}
+
+static const char *
+human_addr (const struct sockaddr *sa, socklen_t salen,
+            char *buf, size_t buflen)
+{
+  const char *save_buf = buf;
+  size_t l;
+
+  if (!buf || !buflen)
+    return NULL;
+
+  *buf = '\0';
+
+  switch (sa->sa_family)
+    {
+#if HAVE_IPV6
+    case AF_INET6:
+      snprintf (buf, buflen, "IPv6 ");
+      break;
+#endif
+
+    case AF_INET:
+      snprintf (buf, buflen, "IPv4 ");
+      break;
+    }
+
+  l = strlen (buf);
+  buf += l;
+  buflen -= l;
+
+  if (getnameinfo (sa, salen, buf, buflen, NULL, 0, NI_NUMERICHOST) != 0)
+    return NULL;
+
+  l = strlen (buf);
+  buf += l;
+  buflen -= l;
+
+  strncat (buf, " port ", buflen);
+
+  l = strlen (buf);
+  buf += l;
+  buflen -= l;
+
+  if (getnameinfo (sa, salen, NULL, 0, buf, buflen, NI_NUMERICSERV) != 0)
+    return NULL;
+
+  return save_buf;
+}
+
+static gnutls_session_t
+initialize_tls_session (void)
+{
+  gnutls_session_t session;
+
+  gnutls_init (&session, GNUTLS_SERVER|GNUTLS_DATAGRAM);
+
+  gnutls_priority_set (session, priority_cache);
+
+  gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+  return session;
+}
+
+static int generate_dh_params (void)
+{
+  int bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LOW);
+
+  /* Generate Diffie-Hellman parameters - for use with DHE
+   * kx algorithms. When short bit length is used, it might
+   * be wise to regenerate parameters often.
+   */
+  gnutls_dh_params_init (&dh_params);
+  gnutls_dh_params_generate2 (dh_params, bits);
+
+  return 0;
+}
index 7166657e9d637205e660fb1222deb36c838f5c40..97eba2f7298cea1bad430f8e54b7e85fc050729d 100644 (file)
@@ -28,7 +28,6 @@
 #define SOCKET_ERR(err,s) if(err==-1) {perror(s);return(1);}
 #define MAX_BUF 1024
 #define PORT 5556               /* listen to 5556 port */
-#define DH_BITS 1024
 
 /* These are global */
 gnutls_certificate_credentials_t x509_cred;
@@ -49,11 +48,6 @@ initialize_tls_session (void)
    */
   gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
 
-  /* Set maximum compatibility mode. This is only suggested on public webservers
-   * that need to trade security for compatibility
-   */
-  gnutls_session_enable_compatibility_mode (session);
-
   return session;
 }
 
@@ -62,16 +56,14 @@ static gnutls_dh_params_t dh_params;
 static int
 generate_dh_params (void)
 {
+  int bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LOW);
 
   /* Generate Diffie-Hellman parameters - for use with DHE
    * kx algorithms. When short bit length is used, it might
-   * be wise to regenerate parameters.
-   *
-   * Check the ex-serv-export.c example for using static
-   * parameters.
+   * be wise to regenerate parameters often.
    */
   gnutls_dh_params_init (&dh_params);
-  gnutls_dh_params_generate2 (dh_params, DH_BITS);
+  gnutls_dh_params_generate2 (dh_params, bits);
 
   return 0;
 }
@@ -105,7 +97,7 @@ main (void)
 
   generate_dh_params ();
 
-  gnutls_priority_init (&priority_cache, "NORMAL", NULL);
+  gnutls_priority_init (&priority_cache, "PERFORMANCE:%SERVER_PRECEDENCE", NULL);
 
 
   gnutls_certificate_set_dh_params (x509_cred, dh_params);