3 /* This is needed for apple */
4 #define __APPLE_USE_RFC_3542
11 #include <netinet/in.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
15 #include <arpa/inet.h>
23 #define DEFAULT_PORT 20220
25 #define PSK_CLIENT_IDENTITY "Client_identity"
26 #define PSK_SERVER_IDENTITY "Server_identity"
27 #define PSK_DEFAULT_KEY "secretPSK"
28 #define PSK_OPTIONS "i:s:k:"
31 #define UNUSED_PARAM __attribute__((unused))
37 static size_t len = 0;
40 size_t length; /* length of string */
41 unsigned char *s; /* string data */
44 static dtls_str output_file = { 0, NULL }; /* output file name */
46 static dtls_context_t *dtls_context = NULL;
47 static dtls_context_t *orig_dtls_context = NULL;
50 static const unsigned char ecdsa_priv_key[] = {
51 0x41, 0xC1, 0xCB, 0x6B, 0x51, 0x24, 0x7A, 0x14,
52 0x43, 0x21, 0x43, 0x5B, 0x7A, 0x80, 0xE7, 0x14,
53 0x89, 0x6A, 0x33, 0xBB, 0xAD, 0x72, 0x94, 0xCA,
54 0x40, 0x14, 0x55, 0xA1, 0x94, 0xA9, 0x49, 0xFA};
56 static const unsigned char ecdsa_pub_key_x[] = {
57 0x36, 0xDF, 0xE2, 0xC6, 0xF9, 0xF2, 0xED, 0x29,
58 0xDA, 0x0A, 0x9A, 0x8F, 0x62, 0x68, 0x4E, 0x91,
59 0x63, 0x75, 0xBA, 0x10, 0x30, 0x0C, 0x28, 0xC5,
60 0xE4, 0x7C, 0xFB, 0xF2, 0x5F, 0xA5, 0x8F, 0x52};
62 static const unsigned char ecdsa_pub_key_y[] = {
63 0x71, 0xA0, 0xD4, 0xFC, 0xDE, 0x1A, 0xB8, 0x78,
64 0x5A, 0x3C, 0x78, 0x69, 0x35, 0xA7, 0xCF, 0xAB,
65 0xE9, 0x3F, 0x98, 0x72, 0x09, 0xDA, 0xED, 0x0B,
66 0x4F, 0xAB, 0xC3, 0x6F, 0xC7, 0x72, 0xF8, 0x29};
70 read_from_file(char *arg, unsigned char *buf, size_t max_buf_len) {
80 bytes_read = fread(buf, 1, max_buf_len, f);
88 max_buf_len -= bytes_read;
95 /* The PSK information for DTLS */
96 #define PSK_ID_MAXLEN 256
97 #define PSK_MAXLEN 256
98 static unsigned char psk_client_id[PSK_ID_MAXLEN];
99 static size_t psk_client_id_length = 0;
100 static unsigned char psk_server_id[PSK_ID_MAXLEN];
101 static size_t psk_server_id_length = 0;
102 static unsigned char psk_key[PSK_MAXLEN];
103 static size_t psk_key_length = 0;
105 /* This function is the "key store" for tinyDTLS. It is called to
106 * retrieve a key for the given identity within this particular
109 get_psk_info(struct dtls_context_t *ctx UNUSED_PARAM,
110 const session_t *session UNUSED_PARAM,
111 dtls_credentials_type_t type,
112 const unsigned char *id, size_t id_len,
113 unsigned char *result, size_t result_length) {
116 case DTLS_PSK_IDENTITY:
118 dtls_debug("got psk_identity_hint: '%.*s'\n", id_len, id);
121 if (result_length < psk_client_id_length) {
122 dtls_warn("cannot set psk_identity -- buffer too small\n");
123 return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
126 memcpy(result, psk_client_id, psk_client_id_length);
127 return psk_client_id_length;
129 if (id_len != psk_server_id_length || memcmp(psk_server_id, id, id_len) != 0) {
130 dtls_warn("PSK for unknown id requested, exiting\n");
131 return dtls_alert_fatal_create(DTLS_ALERT_ILLEGAL_PARAMETER);
132 } else if (result_length < psk_key_length) {
133 dtls_warn("cannot set psk -- buffer too small\n");
134 return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
137 memcpy(result, psk_key, psk_key_length);
138 return psk_key_length;
140 dtls_warn("unsupported request type: %d\n", type);
143 return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
145 #endif /* DTLS_PSK */
149 get_ecdsa_key(struct dtls_context_t *ctx,
150 const session_t *session,
151 const dtls_ecdsa_key_t **result) {
152 static const dtls_ecdsa_key_t ecdsa_key = {
153 .curve = DTLS_ECDH_CURVE_SECP256R1,
154 .priv_key = ecdsa_priv_key,
155 .pub_key_x = ecdsa_pub_key_x,
156 .pub_key_y = ecdsa_pub_key_y
159 *result = &ecdsa_key;
164 verify_ecdsa_key(struct dtls_context_t *ctx,
165 const session_t *session,
166 const unsigned char *other_pub_x,
167 const unsigned char *other_pub_y,
171 #endif /* DTLS_ECC */
174 try_send(struct dtls_context_t *ctx, session_t *dst) {
176 res = dtls_write(ctx, dst, (uint8 *)buf, len);
178 memmove(buf, buf + res, len - res);
185 if (fgets(buf + len, sizeof(buf) - len, stdin))
186 len += strlen(buf + len);
190 read_from_peer(struct dtls_context_t *ctx,
191 session_t *session, uint8 *data, size_t len) {
193 for (i = 0; i < len; i++)
194 printf("%c", data[i]);
199 send_to_peer(struct dtls_context_t *ctx,
200 session_t *session, uint8 *data, size_t len) {
202 int fd = *(int *)dtls_get_app_data(ctx);
203 return sendto(fd, data, len, MSG_DONTWAIT,
204 &session->addr.sa, session->size);
208 dtls_handle_read(struct dtls_context_t *ctx) {
211 #define MAX_READ_BUF 2000
212 static uint8 buf[MAX_READ_BUF];
215 fd = *(int *)dtls_get_app_data(ctx);
220 memset(&session, 0, sizeof(session_t));
221 session.size = sizeof(session.addr);
222 len = recvfrom(fd, buf, MAX_READ_BUF, 0,
223 &session.addr.sa, &session.size);
229 dtls_dsrv_log_addr(DTLS_LOG_DEBUG, "peer", &session);
230 dtls_debug_dump("bytes from peer", buf, len);
233 return dtls_handle_message(ctx, &session, buf, len);
236 static void dtls_handle_signal(int sig)
238 dtls_free_context(dtls_context);
239 dtls_free_context(orig_dtls_context);
240 signal(sig, SIG_DFL);
244 /* stolen from libcoap: */
246 resolve_address(const char *server, struct sockaddr *dst) {
248 struct addrinfo *res, *ainfo;
249 struct addrinfo hints;
250 static char addrstr[256];
253 memset(addrstr, 0, sizeof(addrstr));
254 if (server && strlen(server) > 0)
255 memcpy(addrstr, server, strlen(server));
257 memcpy(addrstr, "localhost", 9);
259 memset ((char *)&hints, 0, sizeof(hints));
260 hints.ai_socktype = SOCK_DGRAM;
261 hints.ai_family = AF_UNSPEC;
263 error = getaddrinfo(addrstr, "", &hints, &res);
266 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
270 for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
272 switch (ainfo->ai_family) {
276 memcpy(dst, ainfo->ai_addr, ainfo->ai_addrlen);
277 return ainfo->ai_addrlen;
287 /*---------------------------------------------------------------------------*/
289 usage( const char *program, const char *version) {
292 p = strrchr( program, '/' );
296 fprintf(stderr, "%s v%s -- DTLS client implementation\n"
297 "(c) 2011-2014 Olaf Bergmann <bergmann@tzi.org>\n\n"
299 "usage: %s [-i file] [-s file] [-k file] [-o file] [-p port] [-v num] addr [port]\n"
301 "usage: %s [-o file] [-p port] [-v num] addr [port]\n"
302 #endif /* DTLS_PSK */
304 "\t-i file\t\tread PSK Client identity from file\n"
305 "\t-s file\t\tread PSK Server identity from file\n"
306 "\t-k file\t\tread pre-shared key from file\n"
307 #endif /* DTLS_PSK */
308 "\t-o file\t\toutput received data to this file (use '-' for STDOUT)\n"
309 "\t-p port\t\tlisten on specified port (default is %d)\n"
310 "\t-v num\t\tverbosity level (default: 3)\n",
311 program, version, program, DEFAULT_PORT);
314 static dtls_handler_t cb = {
315 .write = send_to_peer,
316 .read = read_from_peer,
319 .get_psk_info = get_psk_info,
320 #endif /* DTLS_PSK */
322 .get_ecdsa_key = get_ecdsa_key,
323 .verify_ecdsa_key = verify_ecdsa_key
324 #endif /* DTLS_ECC */
327 #define DTLS_CLIENT_CMD_CLOSE "client:close"
328 #define DTLS_CLIENT_CMD_RENEGOTIATE "client:renegotiate"
330 /* As per RFC 6347 section 4.2.8, DTLS Server should support requests
331 * from clients who have silently abandoned the existing association
332 * and initiated a new handshake request by sending a ClientHello.
333 * Below command tests this feature.
335 #define DTLS_CLIENT_CMD_REHANDSHAKE "client:rehandshake"
337 main(int argc, char **argv) {
339 struct timeval timeout;
340 unsigned short port = DEFAULT_PORT;
341 char port_str[NI_MAXSERV] = "0";
342 log_t log_level = DTLS_LOG_WARN;
349 snprintf(port_str, sizeof(port_str), "%d", port);
352 psk_client_id_length = strlen(PSK_CLIENT_IDENTITY);
353 psk_server_id_length = strlen(PSK_SERVER_IDENTITY);
354 psk_key_length = strlen(PSK_DEFAULT_KEY);
355 memcpy(psk_client_id, PSK_CLIENT_IDENTITY, psk_client_id_length);
356 memcpy(psk_server_id, PSK_SERVER_IDENTITY, psk_server_id_length);
357 memcpy(psk_key, PSK_DEFAULT_KEY, psk_key_length);
358 #endif /* DTLS_PSK */
360 while ((opt = getopt(argc, argv, "p:o:v:" PSK_OPTIONS)) != -1) {
364 ssize_t result = read_from_file(optarg, psk_client_id, PSK_ID_MAXLEN);
366 dtls_warn("cannot read Client PSK identity\n");
368 psk_client_id_length = result;
373 ssize_t result = read_from_file(optarg, psk_server_id, PSK_ID_MAXLEN);
375 dtls_warn("cannot read Server PSK identity\n");
377 psk_server_id_length = result;
382 ssize_t result = read_from_file(optarg, psk_key, PSK_MAXLEN);
384 dtls_warn("cannot read PSK\n");
386 psk_key_length = result;
390 #endif /* DTLS_PSK */
392 strncpy(port_str, optarg, NI_MAXSERV-1);
393 port_str[NI_MAXSERV - 1] = '\0';
396 output_file.length = strlen(optarg);
397 output_file.s = (unsigned char *)malloc(output_file.length + 1);
399 if (!output_file.s) {
400 dtls_crit("cannot set output file: insufficient memory\n");
403 /* copy filename including trailing zero */
404 memcpy(output_file.s, optarg, output_file.length + 1);
408 log_level = strtol(optarg, NULL, 10);
411 usage(argv[0], dtls_package_version());
416 dtls_set_log_level(log_level);
418 if (argc <= optind) {
419 usage(argv[0], dtls_package_version());
423 memset(&dst, 0, sizeof(session_t));
424 /* resolve destination address where server should be sent */
425 res = resolve_address(argv[optind++], &dst.addr.sa);
427 dtls_emerg("failed to resolve address\n");
432 /* use port number from command line when specified or the listen
434 dst.addr.sin.sin_port = htons(atoi(optind < argc ? argv[optind++] : port_str));
437 /* init socket and set it to non-blocking */
438 fd = socket(dst.addr.sa.sa_family, SOCK_DGRAM, 0);
441 dtls_alert("socket: %s\n", strerror(errno));
445 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) {
446 dtls_alert("setsockopt SO_REUSEADDR: %s\n", strerror(errno));
449 flags = fcntl(fd, F_GETFL, 0);
450 if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
451 dtls_alert("fcntl: %s\n", strerror(errno));
456 #ifdef IPV6_RECVPKTINFO
457 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on) ) < 0) {
458 #else /* IPV6_RECVPKTINFO */
459 if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on) ) < 0) {
460 #endif /* IPV6_RECVPKTINFO */
461 dtls_alert("setsockopt IPV6_PKTINFO: %s\n", strerror(errno));
464 if (signal(SIGINT, dtls_handle_signal) == SIG_ERR) {
465 dtls_alert("An error occurred while setting a signal handler.\n");
469 dtls_context = dtls_new_context(&fd);
471 dtls_emerg("cannot create context\n");
475 dtls_set_handler(dtls_context, &cb);
477 dtls_connect(dtls_context, &dst);
483 FD_SET(fileno(stdin), &rfds);
485 /* FD_SET(fd, &wfds); */
490 result = select(fd+1, &rfds, &wfds, 0, &timeout);
492 if (result < 0) { /* error */
495 } else if (result == 0) { /* timeout */
497 if (FD_ISSET(fd, &wfds))
499 else if (FD_ISSET(fd, &rfds))
500 dtls_handle_read(dtls_context);
501 else if (FD_ISSET(fileno(stdin), &rfds))
506 if (len >= strlen(DTLS_CLIENT_CMD_CLOSE) &&
507 !memcmp(buf, DTLS_CLIENT_CMD_CLOSE, strlen(DTLS_CLIENT_CMD_CLOSE))) {
508 printf("client: closing connection\n");
509 dtls_close(dtls_context, &dst);
511 } else if (len >= strlen(DTLS_CLIENT_CMD_RENEGOTIATE) &&
512 !memcmp(buf, DTLS_CLIENT_CMD_RENEGOTIATE, strlen(DTLS_CLIENT_CMD_RENEGOTIATE))) {
513 printf("client: renegotiate connection\n");
514 dtls_renegotiate(dtls_context, &dst);
516 } else if (len >= strlen(DTLS_CLIENT_CMD_REHANDSHAKE) &&
517 !memcmp(buf, DTLS_CLIENT_CMD_REHANDSHAKE, strlen(DTLS_CLIENT_CMD_REHANDSHAKE))) {
518 printf("client: rehandshake connection\n");
519 if (orig_dtls_context == NULL) {
520 /* Cache the current context. We cannot free the current context as it will notify
521 * the Server to close the connection (which we do not want).
523 orig_dtls_context = dtls_context;
524 /* Now, Create a new context and attempt to initiate a handshake. */
525 dtls_context = dtls_new_context(&fd);
527 dtls_emerg("cannot create context\n");
530 dtls_set_handler(dtls_context, &cb);
531 dtls_connect(dtls_context, &dst);
535 try_send(dtls_context, &dst);
540 dtls_free_context(dtls_context);
541 dtls_free_context(orig_dtls_context);