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
111void
112usage(char *prog_name);
113
114/*
115 * leave_group(...) de-registers from a multicast group
116 */
117
118void
119leave_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 */
126int setup_signal_handler(char* name);
127
128/*
129 * handle_signal(...) handles interrupt signal to trigger cleanups
130 */
131
132volatile int interrupted = 0;
133
134/*
135 * program_type distinguishes the [s]rtp sender and receiver cases
136 */
137
138typedef enum { sender, receiver, unknown } program_type;
139
140int
141main (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
537void
538usage(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
556void
557leave_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
568void 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
576int 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