Home | History | Annotate | Download | only in test
      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
    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
    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
    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 
    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 
    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