123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- #include "getopt_s.h"
- #include <assert.h> /* for assert() */
- #include <pcap.h>
- #include "rtp_decoder.h"
- #include "util.h"
- #ifndef timersub
- #define timersub(a, b, result) \
- do { \
- (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
- (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
- if ((result)->tv_usec < 0) { \
- --(result)->tv_sec; \
- (result)->tv_usec += 1000000; \
- } \
- } while (0)
- #endif
- #define MAX_KEY_LEN 96
- #define MAX_FILTER 256
- #define MAX_FILE 255
- struct srtp_crypto_suite {
- const char *can_name;
- int gcm_on;
- int key_size;
- int tag_size;
- };
- static struct srtp_crypto_suite srtp_crypto_suites[] = {
- #if 0
- {.can_name = "F8_128_HMAC_SHA1_32", .gcm_on = 0, .key_size = 128, .tag_size = 4},
- #endif
- {.can_name = "AES_CM_128_HMAC_SHA1_32",
- .gcm_on = 0,
- .key_size = 128,
- .tag_size = 4 },
- {.can_name = "AES_CM_128_HMAC_SHA1_80",
- .gcm_on = 0,
- .key_size = 128,
- .tag_size = 10 },
- {.can_name = "AES_192_CM_HMAC_SHA1_32",
- .gcm_on = 0,
- .key_size = 192,
- .tag_size = 4 },
- {.can_name = "AES_192_CM_HMAC_SHA1_80",
- .gcm_on = 0,
- .key_size = 192,
- .tag_size = 10 },
- {.can_name = "AES_256_CM_HMAC_SHA1_32",
- .gcm_on = 0,
- .key_size = 256,
- .tag_size = 4 },
- {.can_name = "AES_256_CM_HMAC_SHA1_80",
- .gcm_on = 0,
- .key_size = 256,
- .tag_size = 10 },
- {.can_name = "AEAD_AES_128_GCM",
- .gcm_on = 1,
- .key_size = 128,
- .tag_size = 16 },
- {.can_name = "AEAD_AES_256_GCM",
- .gcm_on = 1,
- .key_size = 256,
- .tag_size = 16 },
- {.can_name = NULL }
- };
- void rtp_decoder_srtp_log_handler(srtp_log_level_t level,
- const char *msg,
- void *data)
- {
- char level_char = '?';
- switch (level) {
- case srtp_log_level_error:
- level_char = 'e';
- break;
- case srtp_log_level_warning:
- level_char = 'w';
- break;
- case srtp_log_level_info:
- level_char = 'i';
- break;
- case srtp_log_level_debug:
- level_char = 'd';
- break;
- }
- fprintf(stderr, "SRTP-LOG [%c]: %s\n", level_char, msg);
- }
- int main(int argc, char *argv[])
- {
- char errbuf[PCAP_ERRBUF_SIZE];
- bpf_u_int32 pcap_net = 0;
- pcap_t *pcap_handle;
- #if BEW
- struct sockaddr_in local;
- #endif
- srtp_sec_serv_t sec_servs = sec_serv_none;
- int c;
- struct srtp_crypto_suite scs, *i_scsp;
- scs.key_size = 128;
- scs.tag_size = 0;
- int gcm_on = 0;
- char *input_key = NULL;
- int b64_input = 0;
- char key[MAX_KEY_LEN];
- struct bpf_program fp;
- char filter_exp[MAX_FILTER] = "";
- char pcap_file[MAX_FILE] = "-";
- int rtp_packet_offset = DEFAULT_RTP_OFFSET;
- rtp_decoder_t dec;
- srtp_policy_t policy = { { 0 } };
- rtp_decoder_mode_t mode = mode_rtp;
- srtp_err_status_t status;
- int len;
- int expected_len;
- int do_list_mods = 0;
- fprintf(stderr, "Using %s [0x%x]\n", srtp_get_version_string(),
- srtp_get_version());
-
- status = srtp_init();
- if (status) {
- fprintf(stderr,
- "error: srtp initialization failed with error code %d\n",
- status);
- exit(1);
- }
- status = srtp_install_log_handler(rtp_decoder_srtp_log_handler, NULL);
- if (status) {
- fprintf(stderr, "error: install log handler failed\n");
- exit(1);
- }
-
- while (1) {
- c = getopt_s(argc, argv, "b:k:gt:ae:ld:f:s:m:p:o:");
- if (c == -1) {
- break;
- }
- switch (c) {
- case 'b':
- b64_input = 1;
-
- case 'k':
- input_key = optarg_s;
- break;
- case 'e':
- scs.key_size = atoi(optarg_s);
- if (scs.key_size != 128 && scs.key_size != 192 &&
- scs.key_size != 256) {
- fprintf(
- stderr,
- "error: encryption key size must be 128, 192 or 256 (%d)\n",
- scs.key_size);
- exit(1);
- }
- input_key = malloc(scs.key_size);
- sec_servs |= sec_serv_conf;
- break;
- case 't':
- scs.tag_size = atoi(optarg_s);
- break;
- case 'a':
- sec_servs |= sec_serv_auth;
- break;
- case 'g':
- gcm_on = 1;
- sec_servs |= sec_serv_auth;
- break;
- case 'd':
- status = srtp_set_debug_module(optarg_s, 1);
- if (status) {
- fprintf(stderr, "error: set debug module (%s) failed\n",
- optarg_s);
- exit(1);
- }
- break;
- case 'f':
- if (strlen(optarg_s) > MAX_FILTER) {
- fprintf(stderr, "error: filter bigger than %d characters\n",
- MAX_FILTER);
- exit(1);
- }
- fprintf(stderr, "Setting filter as %s\n", optarg_s);
- strcpy(filter_exp, optarg_s);
- break;
- case 'l':
- do_list_mods = 1;
- break;
- case 's':
- for (i_scsp = &srtp_crypto_suites[0]; i_scsp->can_name != NULL;
- i_scsp++) {
- if (strcasecmp(i_scsp->can_name, optarg_s) == 0) {
- break;
- }
- }
- if (i_scsp->can_name == NULL) {
- fprintf(stderr, "Unknown/unsupported crypto suite name %s\n",
- optarg_s);
- exit(1);
- }
- scs = *i_scsp;
- input_key = malloc(scs.key_size);
- sec_servs |= sec_serv_conf | sec_serv_auth;
- gcm_on = scs.gcm_on;
- break;
- case 'm':
- if (strcasecmp("rtp", optarg_s) == 0) {
- mode = mode_rtp;
- } else if (strcasecmp("rtcp", optarg_s) == 0) {
- mode = mode_rtcp;
- } else if (strcasecmp("rtcp-mux", optarg_s) == 0) {
- mode = mode_rtcp_mux;
- } else {
- fprintf(stderr, "Unknown/unsupported mode %s\n", optarg_s);
- exit(1);
- }
- break;
- case 'p':
- if (strlen(optarg_s) > MAX_FILE) {
- fprintf(stderr,
- "error: pcap file path bigger than %d characters\n",
- MAX_FILE);
- exit(1);
- }
- strcpy(pcap_file, optarg_s);
- break;
- case 'o':
- rtp_packet_offset = atoi(optarg_s);
- break;
- default:
- usage(argv[0]);
- }
- }
- if (scs.tag_size == 0) {
- if (gcm_on) {
- scs.tag_size = 16;
- } else {
- scs.tag_size = 10;
- }
- }
- if (gcm_on && scs.tag_size != 8 && scs.tag_size != 16) {
- fprintf(stderr, "error: GCM tag size must be 8 or 16 (%d)\n",
- scs.tag_size);
- exit(1);
- }
- if (!gcm_on && scs.tag_size != 4 && scs.tag_size != 10) {
- fprintf(stderr, "error: non GCM tag size must be 4 or 10 (%d)\n",
- scs.tag_size);
- exit(1);
- }
- if (do_list_mods) {
- status = srtp_list_debug_modules();
- if (status) {
- fprintf(stderr, "error: list of debug modules failed\n");
- exit(1);
- }
- return 0;
- }
- if ((sec_servs && !input_key) || (!sec_servs && input_key)) {
-
- if (input_key == NULL) {
- fprintf(stderr, "key not provided\n");
- }
- if (!sec_servs) {
- fprintf(stderr, "no secservs\n");
- }
- fprintf(stderr, "provided\n");
- usage(argv[0]);
- }
-
- fprintf(stderr, "security services: ");
- if (sec_servs & sec_serv_conf)
- fprintf(stderr, "confidentiality ");
- if (sec_servs & sec_serv_auth)
- fprintf(stderr, "message authentication");
- if (sec_servs == sec_serv_none)
- fprintf(stderr, "none");
- fprintf(stderr, "\n");
-
- if (sec_servs) {
-
- switch (sec_servs) {
- case sec_serv_conf_and_auth:
- if (gcm_on) {
- #ifdef OPENSSL
- switch (scs.key_size) {
- case 128:
- if (scs.tag_size == 16) {
- srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_gcm_128_16_auth(
- &policy.rtcp);
- } else {
- srtp_crypto_policy_set_aes_gcm_128_8_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_gcm_128_8_auth(&policy.rtcp);
- }
- break;
- case 256:
- if (scs.tag_size == 16) {
- srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_gcm_256_16_auth(
- &policy.rtcp);
- } else {
- srtp_crypto_policy_set_aes_gcm_256_8_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_gcm_256_8_auth(&policy.rtcp);
- }
- break;
- }
- #else
- fprintf(stderr, "error: GCM mode only supported when using the "
- "OpenSSL crypto engine.\n");
- return 0;
- #endif
- } else {
- switch (scs.key_size) {
- case 128:
- if (scs.tag_size == 4) {
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(
- &policy.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(
- &policy.rtcp);
- } else {
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(
- &policy.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(
- &policy.rtcp);
- }
- break;
- case 192:
- #ifdef OPENSSL
- if (scs.tag_size == 4) {
- srtp_crypto_policy_set_aes_cm_192_hmac_sha1_32(
- &policy.rtp);
- srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80(
- &policy.rtcp);
- } else {
- srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80(
- &policy.rtp);
- srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80(
- &policy.rtcp);
- }
- #else
- fprintf(stderr,
- "error: AES 192 mode only supported when using the "
- "OpenSSL crypto engine.\n");
- return 0;
- #endif
- break;
- case 256:
- if (scs.tag_size == 4) {
- srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32(
- &policy.rtp);
- srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(
- &policy.rtcp);
- } else {
- srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(
- &policy.rtp);
- srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(
- &policy.rtcp);
- }
- break;
- }
- }
- break;
- case sec_serv_conf:
- if (gcm_on) {
- fprintf(
- stderr,
- "error: GCM mode must always be used with auth enabled\n");
- return -1;
- } else {
- switch (scs.key_size) {
- case 128:
- srtp_crypto_policy_set_aes_cm_128_null_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(
- &policy.rtcp);
- break;
- case 192:
- #ifdef OPENSSL
- srtp_crypto_policy_set_aes_cm_192_null_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80(
- &policy.rtcp);
- #else
- fprintf(stderr,
- "error: AES 192 mode only supported when using the "
- "OpenSSL crypto engine.\n");
- return 0;
- #endif
- break;
- case 256:
- srtp_crypto_policy_set_aes_cm_256_null_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(
- &policy.rtcp);
- break;
- }
- }
- break;
- case sec_serv_auth:
- if (gcm_on) {
- #ifdef OPENSSL
- switch (scs.key_size) {
- case 128:
- srtp_crypto_policy_set_aes_gcm_128_8_only_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_gcm_128_8_only_auth(
- &policy.rtcp);
- break;
- case 256:
- srtp_crypto_policy_set_aes_gcm_256_8_only_auth(&policy.rtp);
- srtp_crypto_policy_set_aes_gcm_256_8_only_auth(
- &policy.rtcp);
- break;
- }
- #else
- printf("error: GCM mode only supported when using the OpenSSL "
- "crypto engine.\n");
- return 0;
- #endif
- } else {
- srtp_crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtp);
- srtp_crypto_policy_set_rtcp_default(&policy.rtcp);
- }
- break;
- default:
- fprintf(stderr, "error: unknown security service requested\n");
- return -1;
- }
- policy.key = (uint8_t *)key;
- policy.ekt = NULL;
- policy.next = NULL;
- policy.window_size = 128;
- policy.allow_repeat_tx = 0;
- policy.rtp.sec_serv = sec_servs;
- policy.rtcp.sec_serv =
- sec_servs;
- fprintf(stderr, "setting tag len %d\n", scs.tag_size);
- policy.rtp.auth_tag_len = scs.tag_size;
- if (gcm_on && scs.tag_size != 8) {
- fprintf(stderr, "set tag len %d\n", scs.tag_size);
- policy.rtp.auth_tag_len = scs.tag_size;
- }
-
- if (b64_input) {
- int pad;
- expected_len = policy.rtp.cipher_key_len * 4 / 3;
- len = base64_string_to_octet_string(key, &pad, input_key,
- strlen(input_key));
- } else {
- expected_len = policy.rtp.cipher_key_len * 2;
- len = hex_string_to_octet_string(key, input_key, expected_len);
- }
-
- if (len < expected_len) {
- fprintf(stderr, "error: too few digits in key/salt "
- "(should be %d digits, found %d)\n",
- expected_len, len);
- exit(1);
- }
- if (strlen(input_key) > policy.rtp.cipher_key_len * 2) {
- fprintf(stderr, "error: too many digits in key/salt "
- "(should be %d hexadecimal digits, found %u)\n",
- policy.rtp.cipher_key_len * 2, (unsigned)strlen(input_key));
- exit(1);
- }
- int key_octets = (scs.key_size / 8);
- int salt_octets = policy.rtp.cipher_key_len - key_octets;
- fprintf(stderr, "set master key/salt to %s/",
- octet_string_hex_string(key, key_octets));
- fprintf(stderr, "%s\n",
- octet_string_hex_string(key + key_octets, salt_octets));
- } else {
- fprintf(stderr,
- "error: neither encryption or authentication were selected\n");
- exit(1);
- }
- pcap_handle = pcap_open_offline(pcap_file, errbuf);
- if (!pcap_handle) {
- fprintf(stderr, "libpcap failed to open file '%s'\n", errbuf);
- exit(1);
- }
- assert(pcap_handle != NULL);
- if ((pcap_compile(pcap_handle, &fp, filter_exp, 1, pcap_net)) == -1) {
- fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp,
- pcap_geterr(pcap_handle));
- return (2);
- }
- if (pcap_setfilter(pcap_handle, &fp) == -1) {
- fprintf(stderr, "couldn't install filter %s: %s\n", filter_exp,
- pcap_geterr(pcap_handle));
- return (2);
- }
- dec = rtp_decoder_alloc();
- if (dec == NULL) {
- fprintf(stderr, "error: malloc() failed\n");
- exit(1);
- }
- fprintf(stderr, "Starting decoder\n");
- if (rtp_decoder_init(dec, policy, mode, rtp_packet_offset)) {
- fprintf(stderr, "error: init failed\n");
- exit(1);
- }
- pcap_loop(pcap_handle, 0, rtp_decoder_handle_pkt, (u_char *)dec);
- if (dec->mode == mode_rtp || dec->mode == mode_rtcp_mux) {
- fprintf(stderr, "RTP packets decoded: %d\n", dec->rtp_cnt);
- }
- if (dec->mode == mode_rtcp || dec->mode == mode_rtcp_mux) {
- fprintf(stderr, "RTCP packets decoded: %d\n", dec->rtcp_cnt);
- }
- fprintf(stderr, "Packet decode errors: %d\n", dec->error_cnt);
- rtp_decoder_deinit(dec);
- rtp_decoder_dealloc(dec);
- status = srtp_shutdown();
- if (status) {
- fprintf(stderr, "error: srtp shutdown failed with error code %d\n",
- status);
- exit(1);
- }
- return 0;
- }
- void usage(char *string)
- {
- fprintf(
- stderr,
- "usage: %s [-d <debug>]* [[-k][-b] <key>] [-a][-t][-e] [-s "
- "<srtp-crypto-suite>] [-m <mode>]\n"
- "or %s -l\n"
- "where -a use message authentication\n"
- " -e <key size> use encryption (use 128 or 256 for key size)\n"
- " -g Use AES-GCM mode (must be used with -e)\n"
- " -t <tag size> Tag size to use (in GCM mode use 8 or 16)\n"
- " -k <key> sets the srtp master key given in hexadecimal\n"
- " -b <key> sets the srtp master key given in base64\n"
- " -l list debug modules\n"
- " -f \"<pcap filter>\" to filter only the desired SRTP packets\n"
- " -d <debug> turn on debugging for module <debug>\n"
- " -s \"<srtp-crypto-suite>\" to set both key and tag size based\n"
- " on RFC4568-style crypto suite specification\n"
- " -m <mode> set the mode to be one of [rtp]|rtcp|rtcp-mux\n"
- " -p <pcap file> path to pcap file (defaults to stdin)\n"
- " -o byte offset of RTP packet in capture (defaults to 42)\n",
- string, string);
- exit(1);
- }
- rtp_decoder_t rtp_decoder_alloc(void)
- {
- return (rtp_decoder_t)malloc(sizeof(rtp_decoder_ctx_t));
- }
- void rtp_decoder_dealloc(rtp_decoder_t rtp_ctx)
- {
- free(rtp_ctx);
- }
- int rtp_decoder_deinit(rtp_decoder_t decoder)
- {
- if (decoder->srtp_ctx) {
- return srtp_dealloc(decoder->srtp_ctx);
- }
- return 0;
- }
- int rtp_decoder_init(rtp_decoder_t dcdr,
- srtp_policy_t policy,
- rtp_decoder_mode_t mode,
- int rtp_packet_offset)
- {
- dcdr->rtp_offset = rtp_packet_offset;
- dcdr->srtp_ctx = NULL;
- dcdr->start_tv.tv_usec = 0;
- dcdr->start_tv.tv_sec = 0;
- dcdr->frame_nr = -1;
- dcdr->error_cnt = 0;
- dcdr->rtp_cnt = 0;
- dcdr->rtcp_cnt = 0;
- dcdr->mode = mode;
- dcdr->policy = policy;
- dcdr->policy.ssrc.type = ssrc_any_inbound;
- if (srtp_create(&dcdr->srtp_ctx, &dcdr->policy)) {
- return 1;
- }
- return 0;
- }
- void hexdump(const void *ptr, size_t size)
- {
- int i, j;
- const unsigned char *cptr = ptr;
- for (i = 0; i < size; i += 16) {
- fprintf(stdout, "%04x ", i);
- for (j = 0; j < 16 && i + j < size; j++) {
- fprintf(stdout, "%02x ", cptr[i + j]);
- }
- fprintf(stdout, "\n");
- }
- }
- void rtp_decoder_handle_pkt(u_char *arg,
- const struct pcap_pkthdr *hdr,
- const u_char *bytes)
- {
- rtp_decoder_t dcdr = (rtp_decoder_t)arg;
- rtp_msg_t message;
- int rtp;
- int pktsize;
- struct timeval delta;
- int octets_recvd;
- srtp_err_status_t status;
- dcdr->frame_nr++;
- if ((dcdr->start_tv.tv_sec == 0) && (dcdr->start_tv.tv_usec == 0)) {
- dcdr->start_tv = hdr->ts;
- }
- if (hdr->caplen < dcdr->rtp_offset) {
- return;
- }
- const void *rtp_packet = bytes + dcdr->rtp_offset;
- memcpy((void *)&message, rtp_packet, hdr->caplen - dcdr->rtp_offset);
- pktsize = hdr->caplen - dcdr->rtp_offset;
- octets_recvd = pktsize;
- if (octets_recvd == -1) {
- return;
- }
- if (dcdr->mode == mode_rtp) {
- rtp = 1;
- } else if (dcdr->mode == mode_rtcp) {
- rtp = 0;
- } else {
- rtp = 1;
- if (octets_recvd >= 2) {
-
- u_char payload_type = *(bytes + dcdr->rtp_offset + 1) & 0x7f;
- rtp = payload_type < 64 || payload_type > 95;
- }
- }
- if (rtp) {
-
- if (message.header.version != 2) {
- return;
- }
- status = srtp_unprotect(dcdr->srtp_ctx, &message, &octets_recvd);
- if (status) {
- dcdr->error_cnt++;
- return;
- }
- dcdr->rtp_cnt++;
- } else {
- status = srtp_unprotect_rtcp(dcdr->srtp_ctx, &message, &octets_recvd);
- if (status) {
- dcdr->error_cnt++;
- return;
- }
- dcdr->rtcp_cnt++;
- }
- timersub(&hdr->ts, &dcdr->start_tv, &delta);
- fprintf(stdout, "%02ld:%02ld.%06ld\n", delta.tv_sec / 60, delta.tv_sec % 60,
- (long)delta.tv_usec);
- hexdump(&message, octets_recvd);
- }
- void rtp_print_error(srtp_err_status_t status, char *message)
- {
-
- fprintf(stderr,
- "error: %s %d%s\n", message, status,
- status == srtp_err_status_replay_fail ? " (replay check failed)" :
- status == srtp_err_status_bad_param ? " (bad param)" :
- status == srtp_err_status_no_ctx ? " (no context)" :
- status == srtp_err_status_cipher_fail ? " (cipher failed)" :
- status == srtp_err_status_key_expired ? " (key expired)" :
- status == srtp_err_status_auth_fail ? " (auth check failed)" : "");
-
- }
|