• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * rtpw.c
3  *
4  * rtp word sender/receiver
5  *
6  * David A. McGrew
7  * Cisco Systems, Inc.
8  *
9  * This app is a simple RTP application intended only for testing
10  * libsrtp.  It reads one word at a time from /usr/dict/words (or
11  * whatever file is specified as DICT_FILE), and sends one word out
12  * each USEC_RATE microseconds.  Secure RTP protections can be
13  * applied.  See the usage() function for more details.
14  *
15  */
16 
17 /*
18  *
19  * Copyright (c) 2001-2006, Cisco Systems, Inc.
20  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  *
26  *   Redistributions of source code must retain the above copyright
27  *   notice, this list of conditions and the following disclaimer.
28  *
29  *   Redistributions in binary form must reproduce the above
30  *   copyright notice, this list of conditions and the following
31  *   disclaimer in the documentation and/or other materials provided
32  *   with the distribution.
33  *
34  *   Neither the name of the Cisco Systems, Inc. nor the names of its
35  *   contributors may be used to endorse or promote products derived
36  *   from this software without specific prior written permission.
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
39  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
40  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
41  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
42  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
43  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
44  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
45  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
47  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
49  * OF THE POSSIBILITY OF SUCH DAMAGE.
50  *
51  */
52 
53 
54 #include "datatypes.h"
55 #include "getopt_s.h"       /* for local getopt()  */
56 
57 #include <stdio.h>          /* for printf, fprintf */
58 #include <stdlib.h>         /* for atoi()          */
59 #include <errno.h>
60 #include <signal.h>         /* for signal()        */
61 
62 #include <string.h>         /* for strncpy()       */
63 #include <time.h>	    /* for usleep()        */
64 
65 #ifdef HAVE_UNISTD_H
66 #include <unistd.h>         /* for close()         */
67 #endif
68 #ifdef HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 #endif
71 #ifdef HAVE_NETINET_IN_H
72 # include <netinet/in.h>
73 #elif defined HAVE_WINSOCK2_H
74 # include <winsock2.h>
75 # include <ws2tcpip.h>
76 # define RTPW_USE_WINSOCK2	1
77 #endif
78 #ifdef HAVE_ARPA_INET_H
79 # include <arpa/inet.h>
80 #endif
81 
82 #include "srtp.h"
83 #include "rtp.h"
84 
85 #ifdef RTPW_USE_WINSOCK2
86 # define DICT_FILE        "words.txt"
87 #else
88 # define DICT_FILE        "/usr/share/dict/words"
89 #endif
90 #define USEC_RATE        (5e5)
91 #define MAX_WORD_LEN     128
92 #define ADDR_IS_MULTICAST(a) IN_MULTICAST(htonl(a))
93 #define MAX_KEY_LEN      64
94 #define MASTER_KEY_LEN   30
95 
96 
97 #ifndef HAVE_USLEEP
98 # ifdef HAVE_WINDOWS_H
99 #  define usleep(us)	Sleep((us)/1000)
100 # else
101 #  define usleep(us)	sleep((us)/1000000)
102 # endif
103 #endif
104 
105 
106 /*
107  * the function usage() prints an error message describing how this
108  * program should be called, then calls exit()
109  */
110 
111 void
112 usage(char *prog_name);
113 
114 /*
115  * leave_group(...) de-registers from a multicast group
116  */
117 
118 void
119 leave_group(int sock, struct ip_mreq mreq, char *name);
120 
121 
122 /*
123  * setup_signal_handler() sets up a signal handler to trigger
124  * cleanups after an interrupt
125  */
126 int setup_signal_handler(char* name);
127 
128 /*
129  * handle_signal(...) handles interrupt signal to trigger cleanups
130  */
131 
132 volatile int interrupted = 0;
133 
134 /*
135  * program_type distinguishes the [s]rtp sender and receiver cases
136  */
137 
138 typedef enum { sender, receiver, unknown } program_type;
139 
140 int
main(int argc,char * argv[])141 main (int argc, char *argv[]) {
142   char *dictfile = DICT_FILE;
143   FILE *dict;
144   char word[MAX_WORD_LEN];
145   int sock, ret;
146   struct in_addr rcvr_addr;
147   struct sockaddr_in name;
148   struct ip_mreq mreq;
149 #if BEW
150   struct sockaddr_in local;
151 #endif
152   program_type prog_type = unknown;
153   sec_serv_t sec_servs = sec_serv_none;
154   unsigned char ttl = 5;
155   int c;
156   char *input_key = NULL;
157   char *address = NULL;
158   char key[MAX_KEY_LEN];
159   unsigned short port = 0;
160   rtp_sender_t snd;
161   srtp_policy_t policy;
162   err_status_t status;
163   int len;
164   int do_list_mods = 0;
165   uint32_t ssrc = 0xdeadbeef; /* ssrc value hardcoded for now */
166 #ifdef RTPW_USE_WINSOCK2
167   WORD wVersionRequested = MAKEWORD(2, 0);
168   WSADATA wsaData;
169 
170   ret = WSAStartup(wVersionRequested, &wsaData);
171   if (ret != 0) {
172     fprintf(stderr, "error: WSAStartup() failed: %d\n", ret);
173     exit(1);
174   }
175 #endif
176 
177   if (setup_signal_handler(argv[0]) != 0) {
178     exit(1);
179   }
180 
181   /* initialize srtp library */
182   status = srtp_init();
183   if (status) {
184     printf("error: srtp initialization failed with error code %d\n", status);
185     exit(1);
186   }
187 
188   /* check args */
189   while (1) {
190     c = getopt_s(argc, argv, "k:rsaeld:");
191     if (c == -1) {
192       break;
193     }
194     switch (c) {
195     case 'k':
196       input_key = optarg_s;
197       break;
198     case 'e':
199       sec_servs |= sec_serv_conf;
200       break;
201     case 'a':
202       sec_servs |= sec_serv_auth;
203       break;
204     case 'r':
205       prog_type = receiver;
206       break;
207     case 's':
208       prog_type = sender;
209       break;
210     case 'd':
211       status = crypto_kernel_set_debug_module(optarg_s, 1);
212       if (status) {
213         printf("error: set debug module (%s) failed\n", optarg_s);
214         exit(1);
215       }
216       break;
217     case 'l':
218       do_list_mods = 1;
219       break;
220     default:
221       usage(argv[0]);
222     }
223   }
224 
225   if (prog_type == unknown) {
226     if (do_list_mods) {
227       status = crypto_kernel_list_debug_modules();
228       if (status) {
229 	printf("error: list of debug modules failed\n");
230 	exit(1);
231       }
232       return 0;
233     } else {
234       printf("error: neither sender [-s] nor receiver [-r] specified\n");
235       usage(argv[0]);
236     }
237   }
238 
239   if ((sec_servs && !input_key) || (!sec_servs && input_key)) {
240     /*
241      * a key must be provided if and only if security services have
242      * been requested
243      */
244     usage(argv[0]);
245   }
246 
247   if (argc != optind_s + 2) {
248     /* wrong number of arguments */
249     usage(argv[0]);
250   }
251 
252   /* get address from arg */
253   address = argv[optind_s++];
254 
255   /* get port from arg */
256   port = atoi(argv[optind_s++]);
257 
258   /* set address */
259 #ifdef HAVE_INET_ATON
260   if (0 == inet_aton(address, &rcvr_addr)) {
261     fprintf(stderr, "%s: cannot parse IP v4 address %s\n", argv[0], address);
262     exit(1);
263   }
264   if (rcvr_addr.s_addr == INADDR_NONE) {
265     fprintf(stderr, "%s: address error", argv[0]);
266     exit(1);
267   }
268 #else
269   rcvr_addr.s_addr = inet_addr(address);
270   if (0xffffffff == rcvr_addr.s_addr) {
271     fprintf(stderr, "%s: cannot parse IP v4 address %s\n", argv[0], address);
272     exit(1);
273   }
274 #endif
275 
276   /* open socket */
277   sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
278   if (sock < 0) {
279     int err;
280 #ifdef RTPW_USE_WINSOCK2
281     err = WSAGetLastError();
282 #else
283     err = errno;
284 #endif
285     fprintf(stderr, "%s: couldn't open socket: %d\n", argv[0], err);
286    exit(1);
287   }
288 
289   name.sin_addr   = rcvr_addr;
290   name.sin_family = PF_INET;
291   name.sin_port   = htons(port);
292 
293   if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
294     if (prog_type == sender) {
295       ret = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
296   	               sizeof(ttl));
297       if (ret < 0) {
298 	fprintf(stderr, "%s: Failed to set TTL for multicast group", argv[0]);
299 	perror("");
300 	exit(1);
301       }
302     }
303 
304     mreq.imr_multiaddr.s_addr = rcvr_addr.s_addr;
305     mreq.imr_interface.s_addr = htonl(INADDR_ANY);
306     ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&mreq,
307 		     sizeof(mreq));
308     if (ret < 0) {
309       fprintf(stderr, "%s: Failed to join multicast group", argv[0]);
310       perror("");
311       exit(1);
312     }
313   }
314 
315   /* report security services selected on the command line */
316   printf("security services: ");
317   if (sec_servs & sec_serv_conf)
318     printf("confidentiality ");
319   if (sec_servs & sec_serv_auth)
320     printf("message authentication");
321   if (sec_servs == sec_serv_none)
322     printf("none");
323   printf("\n");
324 
325   /* set up the srtp policy and master key */
326   if (sec_servs) {
327     /*
328      * create policy structure, using the default mechanisms but
329      * with only the security services requested on the command line,
330      * using the right SSRC value
331      */
332     switch (sec_servs) {
333     case sec_serv_conf_and_auth:
334       crypto_policy_set_rtp_default(&policy.rtp);
335       crypto_policy_set_rtcp_default(&policy.rtcp);
336       break;
337     case sec_serv_conf:
338       crypto_policy_set_aes_cm_128_null_auth(&policy.rtp);
339       crypto_policy_set_rtcp_default(&policy.rtcp);
340       break;
341     case sec_serv_auth:
342       crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtp);
343       crypto_policy_set_rtcp_default(&policy.rtcp);
344       break;
345     default:
346       printf("error: unknown security service requested\n");
347       return -1;
348     }
349     policy.ssrc.type  = ssrc_specific;
350     policy.ssrc.value = ssrc;
351     policy.key  = (uint8_t *) key;
352     policy.ekt  = NULL;
353     policy.next = NULL;
354     policy.window_size = 128;
355     policy.allow_repeat_tx = 0;
356     policy.rtp.sec_serv = sec_servs;
357     policy.rtcp.sec_serv = sec_serv_none;  /* we don't do RTCP anyway */
358 
359     /*
360      * read key from hexadecimal on command line into an octet string
361      */
362     len = hex_string_to_octet_string(key, input_key, MASTER_KEY_LEN*2);
363 
364     /* check that hex string is the right length */
365     if (len < MASTER_KEY_LEN*2) {
366       fprintf(stderr,
367 	      "error: too few digits in key/salt "
368 	      "(should be %d hexadecimal digits, found %d)\n",
369 	      MASTER_KEY_LEN*2, len);
370       exit(1);
371     }
372     if (strlen(input_key) > MASTER_KEY_LEN*2) {
373       fprintf(stderr,
374 	      "error: too many digits in key/salt "
375 	      "(should be %d hexadecimal digits, found %u)\n",
376 	      MASTER_KEY_LEN*2, (unsigned)strlen(input_key));
377       exit(1);
378     }
379 
380     printf("set master key/salt to %s/", octet_string_hex_string(key, 16));
381     printf("%s\n", octet_string_hex_string(key+16, 14));
382 
383   } else {
384     /*
385      * we're not providing security services, so set the policy to the
386      * null policy
387      *
388      * Note that this policy does not conform to the SRTP
389      * specification, since RTCP authentication is required.  However,
390      * the effect of this policy is to turn off SRTP, so that this
391      * application is now a vanilla-flavored RTP application.
392      */
393     policy.key                 = (uint8_t *)key;
394     policy.ssrc.type           = ssrc_specific;
395     policy.ssrc.value          = ssrc;
396     policy.rtp.cipher_type     = NULL_CIPHER;
397     policy.rtp.cipher_key_len  = 0;
398     policy.rtp.auth_type       = NULL_AUTH;
399     policy.rtp.auth_key_len    = 0;
400     policy.rtp.auth_tag_len    = 0;
401     policy.rtp.sec_serv        = sec_serv_none;
402     policy.rtcp.cipher_type    = NULL_CIPHER;
403     policy.rtcp.cipher_key_len = 0;
404     policy.rtcp.auth_type      = NULL_AUTH;
405     policy.rtcp.auth_key_len   = 0;
406     policy.rtcp.auth_tag_len   = 0;
407     policy.rtcp.sec_serv       = sec_serv_none;
408     policy.window_size         = 0;
409     policy.allow_repeat_tx     = 0;
410     policy.ekt                 = NULL;
411     policy.next                = NULL;
412   }
413 
414   if (prog_type == sender) {
415 
416 #if BEW
417     /* bind to local socket (to match crypto policy, if need be) */
418     memset(&local, 0, sizeof(struct sockaddr_in));
419     local.sin_addr.s_addr = htonl(INADDR_ANY);
420     local.sin_port = htons(port);
421     ret = bind(sock, (struct sockaddr *) &local, sizeof(struct sockaddr_in));
422     if (ret < 0) {
423       fprintf(stderr, "%s: bind failed\n", argv[0]);
424       perror("");
425       exit(1);
426     }
427 #endif /* BEW */
428 
429     /* initialize sender's rtp and srtp contexts */
430     snd = rtp_sender_alloc();
431     if (snd == NULL) {
432       fprintf(stderr, "error: malloc() failed\n");
433       exit(1);
434     }
435     rtp_sender_init(snd, sock, name, ssrc);
436     status = rtp_sender_init_srtp(snd, &policy);
437     if (status) {
438       fprintf(stderr,
439 	      "error: srtp_create() failed with code %d\n",
440 	      status);
441       exit(1);
442     }
443 
444     /* open dictionary */
445     dict = fopen (dictfile, "r");
446     if (dict == NULL) {
447       fprintf(stderr, "%s: couldn't open file %s\n", argv[0], dictfile);
448       if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
449   	leave_group(sock, mreq, argv[0]);
450       }
451       exit(1);
452     }
453 
454     /* read words from dictionary, then send them off */
455     while (!interrupted && fgets(word, MAX_WORD_LEN, dict) != NULL) {
456       len = strlen(word) + 1;  /* plus one for null */
457 
458       if (len > MAX_WORD_LEN)
459 	printf("error: word %s too large to send\n", word);
460       else {
461 	rtp_sendto(snd, word, len);
462         printf("sending word: %s", word);
463       }
464       usleep(USEC_RATE);
465     }
466 
467     rtp_sender_deinit_srtp(snd);
468     rtp_sender_dealloc(snd);
469 
470     fclose(dict);
471   } else  { /* prog_type == receiver */
472     rtp_receiver_t rcvr;
473 
474     if (bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0) {
475       close(sock);
476       fprintf(stderr, "%s: socket bind error\n", argv[0]);
477       perror(NULL);
478       if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
479     	leave_group(sock, mreq, argv[0]);
480       }
481       exit(1);
482     }
483 
484     rcvr = rtp_receiver_alloc();
485     if (rcvr == NULL) {
486       fprintf(stderr, "error: malloc() failed\n");
487       exit(1);
488     }
489     rtp_receiver_init(rcvr, sock, name, ssrc);
490     status = rtp_receiver_init_srtp(rcvr, &policy);
491     if (status) {
492       fprintf(stderr,
493 	      "error: srtp_create() failed with code %d\n",
494 	      status);
495       exit(1);
496     }
497 
498     /* get next word and loop */
499     while (!interrupted) {
500       len = MAX_WORD_LEN;
501       if (rtp_recvfrom(rcvr, word, &len) > -1)
502 	printf("\tword: %s\n", word);
503     }
504 
505     rtp_receiver_deinit_srtp(rcvr);
506     rtp_receiver_dealloc(rcvr);
507   }
508 
509   if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
510     leave_group(sock, mreq, argv[0]);
511   }
512 
513 #ifdef RTPW_USE_WINSOCK2
514   ret = closesocket(sock);
515 #else
516   ret = close(sock);
517 #endif
518   if (ret < 0) {
519     fprintf(stderr, "%s: Failed to close socket", argv[0]);
520     perror("");
521   }
522 
523   status = srtp_shutdown();
524   if (status) {
525     printf("error: srtp shutdown failed with error code %d\n", status);
526     exit(1);
527   }
528 
529 #ifdef RTPW_USE_WINSOCK2
530   WSACleanup();
531 #endif
532 
533   return 0;
534 }
535 
536 
537 void
usage(char * string)538 usage(char *string) {
539 
540   printf("usage: %s [-d <debug>]* [-k <key> [-a][-e]] "
541 	 "[-s | -r] dest_ip dest_port\n"
542 	 "or     %s -l\n"
543 	 "where  -a use message authentication\n"
544 	 "       -e use encryption\n"
545 	 "       -k <key>  sets the srtp master key\n"
546 	 "       -s act as rtp sender\n"
547 	 "       -r act as rtp receiver\n"
548 	 "       -l list debug modules\n"
549 	 "       -d <debug> turn on debugging for module <debug>\n",
550 	 string, string);
551   exit(1);
552 
553 }
554 
555 
556 void
leave_group(int sock,struct ip_mreq mreq,char * name)557 leave_group(int sock, struct ip_mreq mreq, char *name) {
558   int ret;
559 
560   ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*)&mreq,
561 		   sizeof(mreq));
562   if (ret < 0) {
563 	fprintf(stderr, "%s: Failed to leave multicast group", name);
564 	perror("");
565   }
566 }
567 
handle_signal(int signum)568 void handle_signal(int signum)
569 {
570   interrupted = 1;
571   /* Reset handler explicitly, in case we don't have sigaction() (and signal()
572      has BSD semantics), or we don't have SA_RESETHAND */
573   signal(signum, SIG_DFL);
574 }
575 
setup_signal_handler(char * name)576 int setup_signal_handler(char* name)
577 {
578 #if HAVE_SIGACTION
579   struct sigaction act;
580   memset(&act, 0, sizeof(act));
581 
582   act.sa_handler = handle_signal;
583   sigemptyset(&act.sa_mask);
584 #if defined(SA_RESETHAND)
585   act.sa_flags = SA_RESETHAND;
586 #else
587   act.sa_flags = 0;
588 #endif
589   /* Note that we're not setting SA_RESTART; we want recvfrom to return
590    * EINTR when we signal the receiver. */
591 
592   if (sigaction(SIGTERM, &act, NULL) != 0) {
593     fprintf(stderr, "%s: error setting up signal handler", name);
594     perror("");
595     return -1;
596   }
597 #else
598   if (signal(SIGTERM, handle_signal) == SIG_ERR) {
599     fprintf(stderr, "%s: error setting up signal handler", name);
600     perror("");
601     return -1;
602   }
603 #endif
604   return 0;
605 }
606