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 <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 
    107 void
    108 usage(char *prog_name);
    109 
    110 /*
    111  * leave_group(...) de-registers from a multicast group
    112  */
    113 
    114 void
    115 leave_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 
    122 typedef enum { sender, receiver, unknown } program_type;
    123 
    124 int
    125 main (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 
    493 void
    494 usage(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 
    512 void
    513 leave_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