Corrected allocation check
[gnutls.git] / doc / examples / ex-serv-dtls.c
blobadcc7eb389fe49b2036e57a8cd6b1cd2d5e668cc
1 /* This example code is placed in the public domain. */
3 #ifdef HAVE_CONFIG_H
4 #include <config.h>
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <arpa/inet.h>
13 #include <netinet/in.h>
14 #include <sys/select.h>
15 #include <netdb.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <gnutls/gnutls.h>
19 #include <gnutls/dtls.h>
21 #define KEYFILE "key.pem"
22 #define CERTFILE "cert.pem"
23 #define CAFILE "/etc/ssl/certs/ca-certificates.crt"
24 #define CRLFILE "crl.pem"
26 /* This is a sample DTLS echo server, using X.509 authentication.
27 * Note that error checking is minimal to simplify the example.
30 #define MAX_BUFFER 1024
31 #define PORT 5556
33 typedef struct
35 gnutls_session_t session;
36 int fd;
37 struct sockaddr *cli_addr;
38 socklen_t cli_addr_size;
39 } priv_data_st;
41 static int pull_timeout_func (gnutls_transport_ptr_t ptr, unsigned int ms);
42 static ssize_t push_func (gnutls_transport_ptr_t p, const void *data,
43 size_t size);
44 static ssize_t pull_func (gnutls_transport_ptr_t p, void *data, size_t size);
45 static const char *human_addr (const struct sockaddr *sa, socklen_t salen,
46 char *buf, size_t buflen);
47 static int wait_for_connection (int fd);
48 static gnutls_session_t initialize_tls_session (void);
49 static int generate_dh_params (void);
51 /* Use global credentials and parameters to simplify
52 * the example. */
53 static gnutls_certificate_credentials_t x509_cred;
54 static gnutls_priority_t priority_cache;
55 static gnutls_dh_params_t dh_params;
57 int
58 main (void)
60 int listen_sd;
61 int sock, ret;
62 struct sockaddr_in sa_serv;
63 struct sockaddr_in cli_addr;
64 socklen_t cli_addr_size;
65 gnutls_session_t session;
66 char buffer[MAX_BUFFER];
67 priv_data_st priv;
68 gnutls_datum_t cookie_key;
69 gnutls_dtls_prestate_st prestate;
70 int mtu = 1400;
71 unsigned char sequence[8];
73 /* this must be called once in the program
75 gnutls_global_init ();
77 gnutls_certificate_allocate_credentials (&x509_cred);
78 gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE,
79 GNUTLS_X509_FMT_PEM);
81 gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE,
82 GNUTLS_X509_FMT_PEM);
84 ret = gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE,
85 GNUTLS_X509_FMT_PEM);
86 if (ret < 0)
88 printf("No certificate or key were found\n");
89 exit(1);
92 generate_dh_params ();
94 gnutls_certificate_set_dh_params (x509_cred, dh_params);
96 gnutls_priority_init (&priority_cache,
97 "PERFORMANCE:-VERS-TLS-ALL:+VERS-DTLS1.0:%SERVER_PRECEDENCE",
98 NULL);
100 gnutls_key_generate (&cookie_key, GNUTLS_COOKIE_KEY_SIZE);
102 /* Socket operations
104 listen_sd = socket (AF_INET, SOCK_DGRAM, 0);
106 memset (&sa_serv, '\0', sizeof (sa_serv));
107 sa_serv.sin_family = AF_INET;
108 sa_serv.sin_addr.s_addr = INADDR_ANY;
109 sa_serv.sin_port = htons (PORT);
111 { /* DTLS requires the IP don't fragment (DF) bit to be set */
112 #if defined(IP_DONTFRAG)
113 int optval = 1;
114 setsockopt (listen_sd, IPPROTO_IP, IP_DONTFRAG,
115 (const void *) &optval, sizeof (optval));
116 #elif defined(IP_MTU_DISCOVER)
117 int optval = IP_PMTUDISC_DO;
118 setsockopt(listen_sd, IPPROTO_IP, IP_MTU_DISCOVER,
119 (const void*) &optval, sizeof (optval));
120 #endif
123 bind (listen_sd, (struct sockaddr *) &sa_serv, sizeof (sa_serv));
125 printf ("UDP server ready. Listening to port '%d'.\n\n", PORT);
127 for (;;)
129 printf ("Waiting for connection...\n");
130 sock = wait_for_connection (listen_sd);
131 if (sock < 0)
132 continue;
134 cli_addr_size = sizeof (cli_addr);
135 ret = recvfrom (sock, buffer, sizeof (buffer), MSG_PEEK,
136 (struct sockaddr *) &cli_addr, &cli_addr_size);
137 if (ret > 0)
139 memset (&prestate, 0, sizeof (prestate));
140 ret = gnutls_dtls_cookie_verify (&cookie_key, &cli_addr,
141 sizeof (cli_addr), buffer, ret,
142 &prestate);
143 if (ret < 0) /* cookie not valid */
145 priv_data_st s;
147 memset (&s, 0, sizeof (s));
148 s.fd = sock;
149 s.cli_addr = (void *) &cli_addr;
150 s.cli_addr_size = sizeof (cli_addr);
152 printf ("Sending hello verify request to %s\n",
153 human_addr ((struct sockaddr *) &cli_addr,
154 sizeof (cli_addr), buffer,
155 sizeof (buffer)));
157 gnutls_dtls_cookie_send (&cookie_key, &cli_addr,
158 sizeof (cli_addr), &prestate,
159 (gnutls_transport_ptr_t) & s,
160 push_func);
162 /* discard peeked data */
163 recvfrom (sock, buffer, sizeof (buffer), 0,
164 (struct sockaddr *) &cli_addr, &cli_addr_size);
165 usleep (100);
166 continue;
168 printf ("Accepted connection from %s\n",
169 human_addr ((struct sockaddr *)
170 &cli_addr, sizeof (cli_addr), buffer,
171 sizeof (buffer)));
173 else
174 continue;
176 session = initialize_tls_session ();
177 gnutls_dtls_prestate_set (session, &prestate);
178 gnutls_dtls_set_mtu (session, mtu);
180 priv.session = session;
181 priv.fd = sock;
182 priv.cli_addr = (struct sockaddr *) &cli_addr;
183 priv.cli_addr_size = sizeof (cli_addr);
185 gnutls_transport_set_ptr (session, &priv);
186 gnutls_transport_set_push_function (session, push_func);
187 gnutls_transport_set_pull_function (session, pull_func);
188 gnutls_transport_set_pull_timeout_function (session, pull_timeout_func);
192 ret = gnutls_handshake (session);
194 while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
196 if (ret < 0)
198 fprintf (stderr, "Error in handshake(): %s\n",
199 gnutls_strerror (ret));
200 gnutls_deinit (session);
201 continue;
204 printf ("- Handshake was completed\n");
206 for (;;)
210 ret = gnutls_record_recv_seq (session, buffer, MAX_BUFFER,
211 sequence);
213 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
215 if (ret < 0)
217 fprintf (stderr, "Error in recv(): %s\n",
218 gnutls_strerror (ret));
219 break;
221 if (ret == 0)
223 printf ("EOF\n\n");
224 break;
226 buffer[ret] = 0;
227 printf ("received[%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x]: %s\n",
228 sequence[0], sequence[1], sequence[2], sequence[3],
229 sequence[4], sequence[5], sequence[6], sequence[7], buffer);
231 /* reply back */
232 ret = gnutls_record_send (session, buffer, ret);
233 if (ret < 0)
235 fprintf (stderr, "Error in send(): %s\n",
236 gnutls_strerror (ret));
237 break;
241 gnutls_bye (session, GNUTLS_SHUT_WR);
242 gnutls_deinit (session);
245 close (listen_sd);
247 gnutls_certificate_free_credentials (x509_cred);
248 gnutls_priority_deinit (priority_cache);
250 gnutls_global_deinit ();
252 return 0;
256 static int
257 wait_for_connection (int fd)
259 fd_set rd, wr;
260 int n;
262 FD_ZERO (&rd);
263 FD_ZERO (&wr);
265 FD_SET (fd, &rd);
267 /* waiting part */
268 n = select (fd + 1, &rd, &wr, NULL, NULL);
269 if (n == -1 && errno == EINTR)
270 return -1;
271 if (n < 0)
273 perror ("select()");
274 exit (1);
277 return fd;
280 /* Wait for data to be received within a timeout period in milliseconds
282 static int
283 pull_timeout_func (gnutls_transport_ptr_t ptr, unsigned int ms)
285 fd_set rfds;
286 struct timeval tv;
287 priv_data_st *priv = ptr;
288 struct sockaddr_in cli_addr;
289 socklen_t cli_addr_size;
290 int ret;
291 char c;
293 FD_ZERO (&rfds);
294 FD_SET (priv->fd, &rfds);
296 tv.tv_sec = 0;
297 tv.tv_usec = ms * 1000;
299 ret = select (priv->fd + 1, &rfds, NULL, NULL, &tv);
301 if (ret <= 0)
302 return ret;
304 /* only report ok if the next message is from the peer we expect
305 * from
307 cli_addr_size = sizeof (cli_addr);
308 ret =
309 recvfrom (priv->fd, &c, 1, MSG_PEEK, (struct sockaddr *) &cli_addr,
310 &cli_addr_size);
311 if (ret > 0)
313 if (cli_addr_size == priv->cli_addr_size
314 && memcmp (&cli_addr, priv->cli_addr, sizeof (cli_addr)) == 0)
315 return 1;
318 return 0;
321 static ssize_t
322 push_func (gnutls_transport_ptr_t p, const void *data, size_t size)
324 priv_data_st *priv = p;
326 return sendto (priv->fd, data, size, 0, priv->cli_addr,
327 priv->cli_addr_size);
330 static ssize_t
331 pull_func (gnutls_transport_ptr_t p, void *data, size_t size)
333 priv_data_st *priv = p;
334 struct sockaddr_in cli_addr;
335 socklen_t cli_addr_size;
336 char buffer[64];
337 int ret;
339 cli_addr_size = sizeof (cli_addr);
340 ret =
341 recvfrom (priv->fd, data, size, 0, (struct sockaddr *) &cli_addr,
342 &cli_addr_size);
343 if (ret == -1)
344 return ret;
346 if (cli_addr_size == priv->cli_addr_size
347 && memcmp (&cli_addr, priv->cli_addr, sizeof (cli_addr)) == 0)
348 return ret;
350 printf ("Denied connection from %s\n",
351 human_addr ((struct sockaddr *)
352 &cli_addr, sizeof (cli_addr), buffer, sizeof (buffer)));
354 gnutls_transport_set_errno (priv->session, EAGAIN);
355 return -1;
358 static const char *
359 human_addr (const struct sockaddr *sa, socklen_t salen,
360 char *buf, size_t buflen)
362 const char *save_buf = buf;
363 size_t l;
365 if (!buf || !buflen)
366 return NULL;
368 *buf = '\0';
370 switch (sa->sa_family)
372 #if HAVE_IPV6
373 case AF_INET6:
374 snprintf (buf, buflen, "IPv6 ");
375 break;
376 #endif
378 case AF_INET:
379 snprintf (buf, buflen, "IPv4 ");
380 break;
383 l = strlen (buf);
384 buf += l;
385 buflen -= l;
387 if (getnameinfo (sa, salen, buf, buflen, NULL, 0, NI_NUMERICHOST) != 0)
388 return NULL;
390 l = strlen (buf);
391 buf += l;
392 buflen -= l;
394 strncat (buf, " port ", buflen);
396 l = strlen (buf);
397 buf += l;
398 buflen -= l;
400 if (getnameinfo (sa, salen, NULL, 0, buf, buflen, NI_NUMERICSERV) != 0)
401 return NULL;
403 return save_buf;
406 static gnutls_session_t
407 initialize_tls_session (void)
409 gnutls_session_t session;
411 gnutls_init (&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
413 gnutls_priority_set (session, priority_cache);
415 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
417 return session;
420 static int
421 generate_dh_params (void)
423 int bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LOW);
425 /* Generate Diffie-Hellman parameters - for use with DHE
426 * kx algorithms. When short bit length is used, it might
427 * be wise to regenerate parameters often.
429 gnutls_dh_params_init (&dh_params);
430 gnutls_dh_params_generate2 (dh_params, bits);
432 return 0;