merge master code to build iotivity
[platform/upstream/iotivity.git] / extlibs / tinydtls / tests / dtls-client.c
1 #include "tinydtls.h" 
2
3 /* This is needed for apple */
4 #define __APPLE_USE_RFC_3542
5
6 #include <stdio.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <ctype.h>
11 #include <netinet/in.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/time.h>
15 #include <arpa/inet.h>
16 #include <netdb.h>
17 #include <signal.h>
18
19 #include "global.h" 
20 #include "debug.h" 
21 #include "dtls.h" 
22
23 #define DEFAULT_PORT 20220
24
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:"
29
30 #ifdef __GNUC__
31 #define UNUSED_PARAM __attribute__((unused))
32 #else
33 #define UNUSED_PARAM
34 #endif /* __GNUC__ */
35
36 static char buf[200];
37 static size_t len = 0;
38
39 typedef struct {
40   size_t length;               /* length of string */
41   unsigned char *s;            /* string data */
42 } dtls_str;
43
44 static dtls_str output_file = { 0, NULL }; /* output file name */
45
46 static dtls_context_t *dtls_context = NULL;
47 static dtls_context_t *orig_dtls_context = NULL;
48
49
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};
55
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};
61
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};
67
68 #ifdef DTLS_PSK
69 ssize_t
70 read_from_file(char *arg, unsigned char *buf, size_t max_buf_len) {
71   FILE *f;
72   ssize_t result = 0;
73
74   f = fopen(arg, "r");
75   if (f == NULL)
76     return -1;
77
78   while (!feof(f)) {
79     size_t bytes_read;
80     bytes_read = fread(buf, 1, max_buf_len, f);
81     if (ferror(f)) {
82       result = -1;
83       break;
84     }
85
86     buf += bytes_read;
87     result += bytes_read;
88     max_buf_len -= bytes_read;
89   }
90
91   fclose(f);
92   return result;
93 }
94
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;
104
105 /* This function is the "key store" for tinyDTLS. It is called to
106  * retrieve a key for the given identity within this particular
107  * session. */
108 static int
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) {
114
115   switch (type) {
116   case DTLS_PSK_IDENTITY:
117     if (id_len) {
118       dtls_debug("got psk_identity_hint: '%.*s'\n", id_len, id);
119     }
120
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);
124     }
125
126     memcpy(result, psk_client_id, psk_client_id_length);
127     return psk_client_id_length;
128   case DTLS_PSK_KEY:
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);
135     }
136
137     memcpy(result, psk_key, psk_key_length);
138     return psk_key_length;
139   default:
140     dtls_warn("unsupported request type: %d\n", type);
141   }
142
143   return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
144 }
145 #endif /* DTLS_PSK */
146
147 #ifdef DTLS_ECC
148 static int
149 get_ecdsa_key(struct dtls_context_t *ctx,
150               const session_t *session,
151               const dtls_ecc_key_t **result) {
152   static const dtls_ecc_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
157   };
158
159   *result = &ecdsa_key;
160   return 0;
161 }
162
163 static int
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,
168                  size_t key_size) {
169   return 0;
170 }
171 #endif /* DTLS_ECC */
172
173 static void
174 try_send(struct dtls_context_t *ctx, session_t *dst) {
175   int res;
176   res = dtls_write(ctx, dst, (uint8 *)buf, len);
177   if (res >= 0) {
178     memmove(buf, buf + res, len - res);
179     len -= res;
180   }
181 }
182
183 static void
184 handle_stdin() {
185   if (fgets(buf + len, sizeof(buf) - len, stdin))
186     len += strlen(buf + len);
187 }
188
189 static int
190 read_from_peer(struct dtls_context_t *ctx, 
191                session_t *session, uint8 *data, size_t len) {
192   size_t i;
193   for (i = 0; i < len; i++)
194     printf("%c", data[i]);
195   return 0;
196 }
197
198 static int
199 send_to_peer(struct dtls_context_t *ctx, 
200              session_t *session, uint8 *data, size_t len) {
201
202   int fd = *(int *)dtls_get_app_data(ctx);
203   return sendto(fd, data, len, MSG_DONTWAIT,
204                 &session->addr.sa, session->size);
205 }
206
207 static int
208 dtls_handle_read(struct dtls_context_t *ctx) {
209   int fd;
210   session_t session;
211 #define MAX_READ_BUF 2000
212   static uint8 buf[MAX_READ_BUF];
213   int len;
214
215   fd = *(int *)dtls_get_app_data(ctx);
216   
217   if (!fd)
218     return -1;
219
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);
224   
225   if (len < 0) {
226     perror("recvfrom");
227     return -1;
228   } else {
229     dtls_dsrv_log_addr(DTLS_LOG_DEBUG, "peer", &session);
230     dtls_debug_dump("bytes from peer", buf, len);
231   }
232
233   return dtls_handle_message(ctx, &session, buf, len);
234 }    
235
236 static void dtls_handle_signal(int sig)
237 {
238   dtls_free_context(dtls_context);
239   dtls_free_context(orig_dtls_context);
240   signal(sig, SIG_DFL);
241   kill(getpid(), sig);
242 }
243
244 /* stolen from libcoap: */
245 static int
246 resolve_address(const char *server, struct sockaddr *dst) {
247   
248   struct addrinfo *res, *ainfo;
249   struct addrinfo hints;
250   static char addrstr[256];
251   int error;
252
253   memset(addrstr, 0, sizeof(addrstr));
254   if (server && strlen(server) > 0)
255     memcpy(addrstr, server, strlen(server));
256   else
257     memcpy(addrstr, "localhost", 9);
258
259   memset ((char *)&hints, 0, sizeof(hints));
260   hints.ai_socktype = SOCK_DGRAM;
261   hints.ai_family = AF_UNSPEC;
262
263   error = getaddrinfo(addrstr, "", &hints, &res);
264
265   if (error != 0) {
266     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
267     return error;
268   }
269
270   for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
271
272     switch (ainfo->ai_family) {
273     case AF_INET6:
274     case AF_INET:
275
276       memcpy(dst, ainfo->ai_addr, ainfo->ai_addrlen);
277       return ainfo->ai_addrlen;
278     default:
279       ;
280     }
281   }
282
283   freeaddrinfo(res);
284   return -1;
285 }
286
287 /*---------------------------------------------------------------------------*/
288 static void
289 usage( const char *program, const char *version) {
290   const char *p;
291
292   p = strrchr( program, '/' );
293   if ( p )
294     program = ++p;
295
296   fprintf(stderr, "%s v%s -- DTLS client implementation\n"
297           "(c) 2011-2014 Olaf Bergmann <bergmann@tzi.org>\n\n"
298 #ifdef DTLS_PSK
299           "usage: %s [-i file] [-s file] [-k file] [-o file] [-p port] [-v num] [-c num] addr [port]\n"
300 #else /*  DTLS_PSK */
301           "usage: %s [-o file] [-p port] [-v num] [-c num] addr [port]\n"
302 #endif /* DTLS_PSK */
303 #ifdef 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           "\t-c num\t\tcipher suite (default: 1)\n"
312           "\t\t\t1: TLS_ECDH_anon_WITH_AES_128_CBC_SHA_256 \n"
313           "\t\t\t2: TLS_PSK_WITH_AES_128_CCM_8\n"
314           "\t\t\t3: TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8\n"
315           "\t\t\t4: TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA_256\n",
316            program, version, program, DEFAULT_PORT);
317 }
318
319 static dtls_handler_t cb = {
320   .write = send_to_peer,
321   .read  = read_from_peer,
322   .event = NULL,
323 #ifdef DTLS_PSK
324   .get_psk_info = get_psk_info,
325 #endif /* DTLS_PSK */
326 #ifdef DTLS_ECC
327   .get_ecdsa_key = get_ecdsa_key,
328   .verify_ecdsa_key = verify_ecdsa_key
329 #endif /* DTLS_ECC */
330 };
331
332 #define DTLS_CLIENT_CMD_CLOSE "client:close"
333 #define DTLS_CLIENT_CMD_RENEGOTIATE "client:renegotiate"
334
335 /* As per RFC 6347 section 4.2.8, DTLS Server should support requests
336  * from clients who have silently abandoned the existing association
337  * and initiated a new handshake request by sending a ClientHello.
338  * Below command tests this feature.
339  */
340 #define DTLS_CLIENT_CMD_REHANDSHAKE "client:rehandshake"
341
342 int 
343 main(int argc, char **argv) {
344   fd_set rfds, wfds;
345   struct timeval timeout;
346   unsigned short port = DEFAULT_PORT;
347   char port_str[NI_MAXSERV] = "0";
348   log_t log_level = DTLS_LOG_WARN;
349   int fd, result;
350   int on = 1;
351   dtls_cipher_t selected_cipher = TLS_NULL_WITH_NULL_NULL;
352   dtls_cipher_enable_t ecdh_anon_enalbe = DTLS_CIPHER_ENABLE;
353   int opt, res;
354   session_t dst;
355
356   dtls_init();
357   snprintf(port_str, sizeof(port_str), "%d", port);
358
359 #ifdef DTLS_PSK
360   psk_client_id_length = strlen(PSK_CLIENT_IDENTITY);
361   psk_server_id_length = strlen(PSK_SERVER_IDENTITY);
362   psk_key_length = strlen(PSK_DEFAULT_KEY);
363   memcpy(psk_client_id, PSK_CLIENT_IDENTITY, psk_client_id_length);
364   memcpy(psk_server_id, PSK_SERVER_IDENTITY, psk_server_id_length);
365   memcpy(psk_key, PSK_DEFAULT_KEY, psk_key_length);
366 #endif /* DTLS_PSK */
367
368   while ((opt = getopt(argc, argv, "p:o:v:c:" PSK_OPTIONS)) != -1) {
369     switch (opt) {
370 #ifdef DTLS_PSK
371     case 'i' : {
372       ssize_t result = read_from_file(optarg, psk_client_id, PSK_ID_MAXLEN);
373       if (result < 0) {
374         dtls_warn("cannot read Client PSK identity\n");
375       } else {
376         psk_client_id_length = result;
377       }
378       break;
379     }
380     case 's' : {
381       ssize_t result = read_from_file(optarg, psk_server_id, PSK_ID_MAXLEN);
382       if (result < 0) {
383         dtls_warn("cannot read Server PSK identity\n");
384       } else {
385         psk_server_id_length = result;
386       }
387       break;
388     }
389     case 'k' : {
390       ssize_t result = read_from_file(optarg, psk_key, PSK_MAXLEN);
391       if (result < 0) {
392         dtls_warn("cannot read PSK\n");
393       } else {
394         psk_key_length = result;
395       }
396       break;
397     }
398 #endif /* DTLS_PSK */
399     case 'p' :
400       strncpy(port_str, optarg, NI_MAXSERV-1);
401       port_str[NI_MAXSERV - 1] = '\0';
402       break;
403     case 'o' :
404       output_file.length = strlen(optarg);
405       output_file.s = (unsigned char *)malloc(output_file.length + 1);
406       
407       if (!output_file.s) {
408         dtls_crit("cannot set output file: insufficient memory\n");
409         exit(-1);
410       } else {
411         /* copy filename including trailing zero */
412         memcpy(output_file.s, optarg, output_file.length + 1);
413       }
414       break;
415     case 'v' :
416       log_level = strtol(optarg, NULL, 10);
417       break;
418     case 'c':
419       if( strcmp(optarg, "1") == 0)
420       {
421           selected_cipher = TLS_ECDH_anon_WITH_AES_128_CBC_SHA_256;
422           ecdh_anon_enalbe = DTLS_CIPHER_ENABLE;
423       }
424       else if( strcmp(optarg, "2") == 0)
425       {
426           selected_cipher = TLS_PSK_WITH_AES_128_CCM_8 ;
427           ecdh_anon_enalbe = DTLS_CIPHER_DISABLE;
428       }
429       else if( strcmp(optarg, "3") == 0)
430       {
431           selected_cipher = TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ;
432           ecdh_anon_enalbe = DTLS_CIPHER_DISABLE;
433       }
434       else if( strcmp(optarg, "4") == 0)
435       {
436           selected_cipher = TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA_256;
437           ecdh_anon_enalbe = DTLS_CIPHER_DISABLE;
438       }
439       break;
440     default:
441       usage(argv[0], dtls_package_version());
442       exit(1);
443     }
444   }
445
446   dtls_set_log_level(log_level);
447   
448   if (argc <= optind) {
449     usage(argv[0], dtls_package_version());
450     exit(1);
451   }
452   
453   memset(&dst, 0, sizeof(session_t));
454   /* resolve destination address where server should be sent */
455   res = resolve_address(argv[optind++], &dst.addr.sa);
456   if (res < 0) {
457     dtls_emerg("failed to resolve address\n");
458     exit(-1);
459   }
460   dst.size = res;
461
462   /* use port number from command line when specified or the listen
463      port, otherwise */
464   dst.addr.sin.sin_port = htons(atoi(optind < argc ? argv[optind++] : port_str));
465
466   
467   /* init socket and set it to non-blocking */
468   fd = socket(dst.addr.sa.sa_family, SOCK_DGRAM, 0);
469
470   if (fd < 0) {
471     dtls_alert("socket: %s\n", strerror(errno));
472     return 0;
473   }
474
475   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) {
476     dtls_alert("setsockopt SO_REUSEADDR: %s\n", strerror(errno));
477   }
478 #if 0
479   flags = fcntl(fd, F_GETFL, 0);
480   if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
481     dtls_alert("fcntl: %s\n", strerror(errno));
482     goto error;
483   }
484 #endif
485   on = 1;
486 #ifdef IPV6_RECVPKTINFO
487   if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on) ) < 0) {
488 #else /* IPV6_RECVPKTINFO */
489   if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on) ) < 0) {
490 #endif /* IPV6_RECVPKTINFO */
491     dtls_alert("setsockopt IPV6_PKTINFO: %s\n", strerror(errno));
492   }
493
494   if (signal(SIGINT, dtls_handle_signal) == SIG_ERR) {
495     dtls_alert("An error occurred while setting a signal handler.\n");
496     return EXIT_FAILURE;
497   }
498
499   dtls_context = dtls_new_context(&fd);
500   if (!dtls_context) {
501     dtls_emerg("cannot create context\n");
502     exit(-1);
503   }
504
505
506   /* select cipher suite */
507   dtls_select_cipher(dtls_context, selected_cipher);
508
509   /* enable/disable tls_ecdh_anon_with_aes_128_cbc_sha_256 */
510   dtls_enables_anon_ecdh(dtls_context, ecdh_anon_enalbe);
511
512   dtls_set_handler(dtls_context, &cb);
513
514   dtls_connect(dtls_context, &dst);
515
516   while (1) {
517     FD_ZERO(&rfds);
518     FD_ZERO(&wfds);
519
520     FD_SET(fileno(stdin), &rfds);
521     FD_SET(fd, &rfds);
522     /* FD_SET(fd, &wfds); */
523     
524     timeout.tv_sec = 5;
525     timeout.tv_usec = 0;
526     
527     result = select(fd+1, &rfds, &wfds, 0, &timeout);
528     
529     if (result < 0) {           /* error */
530       if (errno != EINTR)
531         perror("select");
532     } else if (result == 0) {   /* timeout */
533     } else {                    /* ok */
534       if (FD_ISSET(fd, &wfds))
535         /* FIXME */;
536       else if (FD_ISSET(fd, &rfds))
537         dtls_handle_read(dtls_context);
538       else if (FD_ISSET(fileno(stdin), &rfds))
539         handle_stdin();
540     }
541
542     if (len) {
543       if (len >= strlen(DTLS_CLIENT_CMD_CLOSE) &&
544           !memcmp(buf, DTLS_CLIENT_CMD_CLOSE, strlen(DTLS_CLIENT_CMD_CLOSE))) {
545         printf("client: closing connection\n");
546         dtls_close(dtls_context, &dst);
547         len = 0;
548       } else if (len >= strlen(DTLS_CLIENT_CMD_RENEGOTIATE) &&
549                  !memcmp(buf, DTLS_CLIENT_CMD_RENEGOTIATE, strlen(DTLS_CLIENT_CMD_RENEGOTIATE))) {
550         printf("client: renegotiate connection\n");
551         dtls_renegotiate(dtls_context, &dst);
552         len = 0;
553       } else if (len >= strlen(DTLS_CLIENT_CMD_REHANDSHAKE) &&
554                  !memcmp(buf, DTLS_CLIENT_CMD_REHANDSHAKE, strlen(DTLS_CLIENT_CMD_REHANDSHAKE))) {
555         printf("client: rehandshake connection\n");
556         if (orig_dtls_context == NULL) {
557           /* Cache the current context. We cannot free the current context as it will notify 
558            * the Server to close the connection (which we do not want).
559            */
560           orig_dtls_context = dtls_context;
561           /* Now, Create a new context and attempt to initiate a handshake. */
562           dtls_context = dtls_new_context(&fd);
563           if (!dtls_context) {
564             dtls_emerg("cannot create context\n");
565             exit(-1);
566           }
567           dtls_set_handler(dtls_context, &cb);
568           dtls_connect(dtls_context, &dst);
569         }
570         len = 0;
571       } else {
572         try_send(dtls_context, &dst);
573       }
574     }
575   }
576   
577   dtls_free_context(dtls_context);
578   dtls_free_context(orig_dtls_context);
579   exit(0);
580 }
581