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 <unistd.h>         /* for close()         */
61
62#include <string.h>         /* for strncpy()       */
63#include <time.h>	    /* for usleep()        */
64#ifdef HAVE_SYS_SOCKET_H
65# include <sys/socket.h>
66#endif
67#ifdef HAVE_NETINET_IN_H
68# include <netinet/in.h>
69#elif defined HAVE_WINSOCK2_H
70# include <winsock2.h>
71# include <ws2tcpip.h>
72# define RTPW_USE_WINSOCK2	1
73#endif
74#ifdef HAVE_ARPA_INET_H
75# include <arpa/inet.h>
76#endif
77
78#include "srtp.h"
79#include "rtp.h"
80
81#ifdef RTPW_USE_WINSOCK2
82# define DICT_FILE        "words.txt"
83#else
84# define DICT_FILE        "/usr/share/dict/words"
85#endif
86#define USEC_RATE        (5e5)
87#define MAX_WORD_LEN     128
88#define ADDR_IS_MULTICAST(a) IN_MULTICAST(htonl(a))
89#define MAX_KEY_LEN      64
90#define MASTER_KEY_LEN   30
91
92
93#ifndef HAVE_USLEEP
94# ifdef HAVE_WINDOWS_H
95#  define usleep(us)	Sleep((us)/1000)
96# else
97#  define usleep(us)	sleep((us)/1000000)
98# endif
99#endif
100
101
102/*
103 * the function usage() prints an error message describing how this
104 * program should be called, then calls exit()
105 */
106
107void
108usage(char *prog_name);
109
110/*
111 * leave_group(...) de-registers from a multicast group
112 */
113
114void
115leave_group(int sock, struct ip_mreq mreq, char *name);
116
117
118/*
119 * program_type distinguishes the [s]rtp sender and receiver cases
120 */
121
122typedef enum { sender, receiver, unknown } program_type;
123
124int
125main (int argc, char *argv[]) {
126  char *dictfile = DICT_FILE;
127  FILE *dict;
128  char word[MAX_WORD_LEN];
129  int sock, ret;
130  struct in_addr rcvr_addr;
131  struct sockaddr_in name;
132  struct ip_mreq mreq;
133#if BEW
134  struct sockaddr_in local;
135#endif
136  program_type prog_type = unknown;
137  sec_serv_t sec_servs = sec_serv_none;
138  unsigned char ttl = 5;
139  int c;
140  char *input_key = NULL;
141  char *address = NULL;
142  char key[MAX_KEY_LEN];
143  unsigned short port = 0;
144  rtp_sender_t snd;
145  srtp_policy_t policy;
146  err_status_t status;
147  int len;
148  int do_list_mods = 0;
149  uint32_t ssrc = 0xdeadbeef; /* ssrc value hardcoded for now */
150#ifdef RTPW_USE_WINSOCK2
151  WORD wVersionRequested = MAKEWORD(2, 0);
152  WSADATA wsaData;
153
154  ret = WSAStartup(wVersionRequested, &wsaData);
155  if (ret != 0) {
156    fprintf(stderr, "error: WSAStartup() failed: %d\n", ret);
157    exit(1);
158  }
159#endif
160
161  /* initialize srtp library */
162  status = srtp_init();
163  if (status) {
164    printf("error: srtp initialization failed with error code %d\n", status);
165    exit(1);
166  }
167
168  /* check args */
169  while (1) {
170    c = getopt_s(argc, argv, "k:rsaeld:");
171    if (c == -1) {
172      break;
173    }
174    switch (c) {
175    case 'k':
176      input_key = optarg_s;
177      break;
178    case 'e':
179      sec_servs |= sec_serv_conf;
180      break;
181    case 'a':
182      sec_servs |= sec_serv_auth;
183      break;
184    case 'r':
185      prog_type = receiver;
186      break;
187    case 's':
188      prog_type = sender;
189      break;
190    case 'd':
191      status = crypto_kernel_set_debug_module(optarg_s, 1);
192      if (status) {
193        printf("error: set debug module (%s) failed\n", optarg_s);
194        exit(1);
195      }
196      break;
197    case 'l':
198      do_list_mods = 1;
199      break;
200    default:
201      usage(argv[0]);
202    }
203  }
204
205  if (prog_type == unknown) {
206    if (do_list_mods) {
207      status = crypto_kernel_list_debug_modules();
208      if (status) {
209	printf("error: list of debug modules failed\n");
210	exit(1);
211      }
212      return 0;
213    } else {
214      printf("error: neither sender [-s] nor receiver [-r] specified\n");
215      usage(argv[0]);
216    }
217  }
218
219  if ((sec_servs && !input_key) || (!sec_servs && input_key)) {
220    /*
221     * a key must be provided if and only if security services have
222     * been requested
223     */
224    usage(argv[0]);
225  }
226
227  if (argc != optind_s + 2) {
228    /* wrong number of arguments */
229    usage(argv[0]);
230  }
231
232  /* get address from arg */
233  address = argv[optind_s++];
234
235  /* get port from arg */
236  port = atoi(argv[optind_s++]);
237
238  /* set address */
239#ifdef HAVE_INET_ATON
240  if (0 == inet_aton(address, &rcvr_addr)) {
241    fprintf(stderr, "%s: cannot parse IP v4 address %s\n", argv[0], address);
242    exit(1);
243  }
244  if (rcvr_addr.s_addr == INADDR_NONE) {
245    fprintf(stderr, "%s: address error", argv[0]);
246    exit(1);
247  }
248#else
249  rcvr_addr.s_addr = inet_addr(address);
250  if (0xffffffff == rcvr_addr.s_addr) {
251    fprintf(stderr, "%s: cannot parse IP v4 address %s\n", argv[0], address);
252    exit(1);
253  }
254#endif
255
256  /* open socket */
257  sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
258  if (sock < 0) {
259    int err;
260#ifdef RTPW_USE_WINSOCK2
261    err = WSAGetLastError();
262#else
263    err = errno;
264#endif
265    fprintf(stderr, "%s: couldn't open socket: %d\n", argv[0], err);
266    exit(1);
267  }
268
269  name.sin_addr   = rcvr_addr;
270  name.sin_family = PF_INET;
271  name.sin_port   = htons(port);
272
273  if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
274    if (prog_type == sender) {
275      ret = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
276  	               sizeof(ttl));
277      if (ret < 0) {
278	fprintf(stderr, "%s: Failed to set TTL for multicast group", argv[0]);
279	perror("");
280	exit(1);
281      }
282    }
283
284    mreq.imr_multiaddr.s_addr = rcvr_addr.s_addr;
285    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
286    ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&mreq,
287		     sizeof(mreq));
288    if (ret < 0) {
289      fprintf(stderr, "%s: Failed to join multicast group", argv[0]);
290      perror("");
291      exit(1);
292    }
293  }
294
295  /* report security services selected on the command line */
296  printf("security services: ");
297  if (sec_servs & sec_serv_conf)
298    printf("confidentiality ");
299  if (sec_servs & sec_serv_auth)
300    printf("message authentication");
301  if (sec_servs == sec_serv_none)
302    printf("none");
303  printf("\n");
304
305  /* set up the srtp policy and master key */
306  if (sec_servs) {
307    /*
308     * create policy structure, using the default mechanisms but
309     * with only the security services requested on the command line,
310     * using the right SSRC value
311     */
312    switch (sec_servs) {
313    case sec_serv_conf_and_auth:
314      crypto_policy_set_rtp_default(&policy.rtp);
315      crypto_policy_set_rtcp_default(&policy.rtcp);
316      break;
317    case sec_serv_conf:
318      crypto_policy_set_aes_cm_128_null_auth(&policy.rtp);
319      crypto_policy_set_rtcp_default(&policy.rtcp);
320      break;
321    case sec_serv_auth:
322      crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtp);
323      crypto_policy_set_rtcp_default(&policy.rtcp);
324      break;
325    default:
326      printf("error: unknown security service requested\n");
327      return -1;
328    }
329    policy.ssrc.type  = ssrc_specific;
330    policy.ssrc.value = ssrc;
331    policy.key  = (uint8_t *) key;
332    policy.next = NULL;
333    policy.window_size = 128;
334    policy.allow_repeat_tx = 0;
335    policy.rtp.sec_serv = sec_servs;
336    policy.rtcp.sec_serv = sec_serv_none;  /* we don't do RTCP anyway */
337
338    /*
339     * read key from hexadecimal on command line into an octet string
340     */
341    len = hex_string_to_octet_string(key, input_key, MASTER_KEY_LEN*2);
342
343    /* check that hex string is the right length */
344    if (len < MASTER_KEY_LEN*2) {
345      fprintf(stderr,
346	      "error: too few digits in key/salt "
347	      "(should be %d hexadecimal digits, found %d)\n",
348	      MASTER_KEY_LEN*2, len);
349      exit(1);
350    }
351    if (strlen(input_key) > MASTER_KEY_LEN*2) {
352      fprintf(stderr,
353	      "error: too many digits in key/salt "
354	      "(should be %d hexadecimal digits, found %u)\n",
355	      MASTER_KEY_LEN*2, (unsigned)strlen(input_key));
356      exit(1);
357    }
358
359    printf("set master key/salt to %s/", octet_string_hex_string(key, 16));
360    printf("%s\n", octet_string_hex_string(key+16, 14));
361
362  } else {
363    /*
364     * we're not providing security services, so set the policy to the
365     * null policy
366     *
367     * Note that this policy does not conform to the SRTP
368     * specification, since RTCP authentication is required.  However,
369     * the effect of this policy is to turn off SRTP, so that this
370     * application is now a vanilla-flavored RTP application.
371     */
372    policy.key                 = (uint8_t *)key;
373    policy.ssrc.type           = ssrc_specific;
374    policy.ssrc.value          = ssrc;
375    policy.rtp.cipher_type     = NULL_CIPHER;
376    policy.rtp.cipher_key_len  = 0;
377    policy.rtp.auth_type       = NULL_AUTH;
378    policy.rtp.auth_key_len    = 0;
379    policy.rtp.auth_tag_len    = 0;
380    policy.rtp.sec_serv        = sec_serv_none;
381    policy.rtcp.cipher_type    = NULL_CIPHER;
382    policy.rtcp.cipher_key_len = 0;
383    policy.rtcp.auth_type      = NULL_AUTH;
384    policy.rtcp.auth_key_len   = 0;
385    policy.rtcp.auth_tag_len   = 0;
386    policy.rtcp.sec_serv       = sec_serv_none;
387    policy.window_size         = 0;
388    policy.allow_repeat_tx     = 0;
389    policy.next                = NULL;
390  }
391
392  if (prog_type == sender) {
393
394#if BEW
395    /* bind to local socket (to match crypto policy, if need be) */
396    memset(&local, 0, sizeof(struct sockaddr_in));
397    local.sin_addr.s_addr = htonl(INADDR_ANY);
398    local.sin_port = htons(port);
399    ret = bind(sock, (struct sockaddr *) &local, sizeof(struct sockaddr_in));
400    if (ret < 0) {
401      fprintf(stderr, "%s: bind failed\n", argv[0]);
402      perror("");
403      exit(1);
404    }
405#endif /* BEW */
406
407    /* initialize sender's rtp and srtp contexts */
408    snd = rtp_sender_alloc();
409    if (snd == NULL) {
410      fprintf(stderr, "error: malloc() failed\n");
411      exit(1);
412    }
413    rtp_sender_init(snd, sock, name, ssrc);
414    status = rtp_sender_init_srtp(snd, &policy);
415    if (status) {
416      fprintf(stderr,
417	      "error: srtp_create() failed with code %d\n",
418	      status);
419      exit(1);
420    }
421
422    /* open dictionary */
423    dict = fopen (dictfile, "r");
424    if (dict == NULL) {
425      fprintf(stderr, "%s: couldn't open file %s\n", argv[0], dictfile);
426      if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
427  	leave_group(sock, mreq, argv[0]);
428      }
429      exit(1);
430    }
431
432    /* read words from dictionary, then send them off */
433    while (fgets(word, MAX_WORD_LEN, dict) != NULL) {
434      len = strlen(word) + 1;  /* plus one for null */
435
436      if (len > MAX_WORD_LEN)
437	printf("error: word %s too large to send\n", word);
438      else {
439	rtp_sendto(snd, word, len);
440        printf("sending word: %s", word);
441      }
442      usleep(USEC_RATE);
443    }
444
445  } else  { /* prog_type == receiver */
446    rtp_receiver_t rcvr;
447
448    if (bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0) {
449      close(sock);
450      fprintf(stderr, "%s: socket bind error\n", argv[0]);
451      perror(NULL);
452      if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
453    	leave_group(sock, mreq, argv[0]);
454      }
455      exit(1);
456    }
457
458    rcvr = rtp_receiver_alloc();
459    if (rcvr == NULL) {
460      fprintf(stderr, "error: malloc() failed\n");
461      exit(1);
462    }
463    rtp_receiver_init(rcvr, sock, name, ssrc);
464    status = rtp_receiver_init_srtp(rcvr, &policy);
465    if (status) {
466      fprintf(stderr,
467	      "error: srtp_create() failed with code %d\n",
468	      status);
469      exit(1);
470    }
471
472    /* get next word and loop */
473    while (1) {
474      len = MAX_WORD_LEN;
475      if (rtp_recvfrom(rcvr, word, &len) > -1)
476	printf("\tword: %s", word);
477    }
478
479  }
480
481  if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
482    leave_group(sock, mreq, argv[0]);
483  }
484
485#ifdef RTPW_USE_WINSOCK2
486  WSACleanup();
487#endif
488
489  return 0;
490}
491
492
493void
494usage(char *string) {
495
496  printf("usage: %s [-d <debug>]* [-k <key> [-a][-e]] "
497	 "[-s | -r] dest_ip dest_port\n"
498	 "or     %s -l\n"
499	 "where  -a use message authentication\n"
500	 "       -e use encryption\n"
501	 "       -k <key>  sets the srtp master key\n"
502	 "       -s act as rtp sender\n"
503	 "       -r act as rtp receiver\n"
504	 "       -l list debug modules\n"
505	 "       -d <debug> turn on debugging for module <debug>\n",
506	 string, string);
507  exit(1);
508
509}
510
511
512void
513leave_group(int sock, struct ip_mreq mreq, char *name) {
514  int ret;
515
516  ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*)&mreq,
517		   sizeof(mreq));
518  if (ret < 0) {
519	fprintf(stderr, "%s: Failed to leave multicast group", name);
520	perror("");
521  }
522}
523
524