From 7c53e9f95bf910576953fe7b03aa35886c1c9f20 Mon Sep 17 00:00:00 2001 From: Dylan Mikus Date: Tue, 1 Dec 2015 18:51:30 +0000 Subject: [PATCH] Moved file system management and random string code to appropriate files. File system code is now in fs.{h|c}. This includes: - spool directory setup - metadata file management - pcap file creation and writing Random hex string generation is now in str.h. --- daemon/Makefile | 2 +- daemon/call.c | 135 +------------------------- daemon/fs.c | 214 ++++++++++++++++++++++++++++++++++++++++++ daemon/fs.h | 53 +++++++++++ daemon/main.c | 58 +----------- daemon/media_socket.c | 53 +---------- daemon/str.c | 45 +++++++++ daemon/str.h | 7 ++ 8 files changed, 325 insertions(+), 242 deletions(-) create mode 100644 daemon/fs.c create mode 100644 daemon/fs.h diff --git a/daemon/Makefile b/daemon/Makefile index ac353440f..6bec78fc1 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -69,7 +69,7 @@ endif SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \ bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \ crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \ - media_socket.c rtcp_xr.c homer.c + media_socket.c rtcp_xr.c homer.c fs.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/call.c b/daemon/call.c index eb4fabb8f..4de0125c6 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -37,6 +37,7 @@ #include "ice.h" #include "rtpengine_config.h" #include "log_funcs.h" +#include "fs.h" @@ -132,13 +133,6 @@ const char * get_tag_type_text(enum tag_type t) { static void __monologue_destroy(struct call_monologue *monologue); static int monologue_destroy(struct call_monologue *ml); -/* Generate a random PCAP filepath to write recorded RTP stream. */ -str *setup_recording_file(struct call *call, struct call_monologue *monologue); -/* Generates a random string sandwiched between affixes. */ -char *rand_affixed_str(int num_bytes, char *prefix, char *suffix); -/* Generates a hex string representing n random bytes. len(rand_str) = 2*num_bytes + 1 */ -char *rand_hex_str(char *rand_str, int num_bytes); - /* called with call->master_lock held in R */ static int call_timer_delete_monologues(struct call *c) { GList *i; @@ -2234,46 +2228,6 @@ void call_destroy(struct call *c) { rwlock_unlock_w(&c->master_lock); } -/** - * Writes metadata to metafile, closes file, and renames it to finished location. - * Returns non-zero for failure. - */ -int meta_file_finish(struct call *call) { - int return_code = 0; - - if (call->meta_fp != NULL) { - fprintf(call->meta_fp, "\n%s\n", call->metadata->s); - fclose(call->meta_fp); - - // Get the filename (in between its directory and the file extension) - // and move it to the finished file location. - // Rename extension to ".txt". - int fn_len; - char *meta_filename = strrchr(call->meta_filepath->s, '/'); - char *meta_ext = NULL; - if (meta_filename == NULL) - meta_filename = call->meta_filepath->s; - else - meta_filename = meta_filename + 1; - // We can always expect a file extension - meta_ext = strrchr(meta_filename, '.'); - fn_len = meta_ext - meta_filename; - int prefix_len = 30; // for "/var/spool/rtpengine/metadata/" - int ext_len = 4; // for ".txt" - char new_metapath[prefix_len + fn_len + ext_len + 1]; - snprintf(new_metapath, prefix_len+fn_len+1, "/var/spool/rtpengine/metadata/%s", meta_filename); - snprintf(new_metapath + prefix_len+fn_len, ext_len+1, ".txt"); - return_code = return_code | rename(call->meta_filepath->s, new_metapath); - } - if (call->meta_filepath != NULL) { - free(call->meta_filepath->s); - free(call->meta_filepath); - } - - return return_code; -} - - /* XXX move these */ int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format, @@ -2377,20 +2331,7 @@ static struct call *call_create(const str *callid, struct callmaster *m) { c->dtls_cert = dtls_cert(); c->tos = m->conf.default_tos; - int rand_bytes = 16; - str *meta_filepath = malloc(sizeof(str)); - // Initially file extension is ".tmp". When call is over, it changes to ".txt". - char *path_chars = rand_affixed_str(rand_bytes, "/tmp/rtpengine-meta-", ".tmp"); - meta_filepath = str_init(meta_filepath, path_chars); - c->meta_filepath = meta_filepath; - FILE *mfp = fopen(meta_filepath->s, "w"); - if (mfp == NULL) { - ilog(LOG_ERROR, "Could not open metadata file: %s", meta_filepath->s); - free(c->meta_filepath->s); - free(c->meta_filepath); - c->meta_filepath = NULL; - } - c->meta_fp = mfp; + setup_meta_file(c); return c; } @@ -2920,75 +2861,3 @@ const struct transport_protocol *transport_protocol(const str *s) { out: return NULL; } - - -/** - * Generate a random PCAP filepath to write recorded RTP stream. - */ -str *setup_recording_file(struct call *call, struct call_monologue *monologue) { - str *recording_path = NULL; - if (call->record_call - && monologue->recording_pd == NULL && monologue->recording_pdumper == NULL) { - int rand_bytes = 16; - recording_path = malloc(sizeof(str)); - char *path_chars = rand_affixed_str(rand_bytes, "/var/spool/rtpengine/recordings/", ".pcap"); - - recording_path = str_init(recording_path, path_chars); - monologue->recording_path = recording_path; - - call->recording_pcaps = g_slist_prepend(call->recording_pcaps, g_strdup(path_chars)); - /* monologue->recording_file */ - monologue->recording_pd = pcap_open_dead(DLT_RAW, 65535); - monologue->recording_pdumper = pcap_dump_open(monologue->recording_pd, recording_path); - } else { - monologue->recording_path = NULL; - monologue->recording_pd = NULL; - monologue->recording_pdumper = NULL; - } - - return recording_path; -} - -/** - * Generates a random string sandwiched between affixes. - * Will create the char string for you. Don't forget to clean up! - */ -char *rand_affixed_str(int num_bytes, char *prefix, char *suffix) { - int rand_len = num_bytes*2 + 1; - char rand_prefix[rand_len]; - int prefix_len = strlen(prefix); - int suffix_len = strlen(suffix); - char *full_path = calloc(rand_len + prefix_len + suffix_len, sizeof(char)); - - rand_hex_str(rand_prefix, num_bytes); - snprintf(full_path, rand_len+prefix_len, "%s%s", prefix, rand_prefix); - snprintf(full_path + rand_len+prefix_len-1, suffix_len+1, "%s", suffix); - return full_path; -} - -/** - * Generates a random hexadecimal string representing n random bytes. - * rand_str length must be 2*num_bytes + 1. - */ -char *rand_hex_str(char *rand_str, int num_bytes) { - char rand_tmp[3]; - u_int8_t rand_byte; - int i, n; - // We might convert an int to a hex string shorter than 2 digits. - // This causes those strings to have leading '0' characters. - for (i=0; i +#include +#include +#include +#include +#include +#include "call.h" + + + +int maybe_create_spool_dir(char *dirpath); + + + +/** + * Initialize RTP Engine filesystem settings and structure. + * Check for or create the RTP Engine spool directory. + */ +void fs_init(char *spoolpath) { + // TODO should we change the umask at all? + if (!maybe_create_spool_dir(spoolpath)) { + fprintf(stderr, "Error while setting up spool directory \"%s\".\n", spoolpath); + exit(-1); + } +} + +/** + * Sets up the spool directory for RTP Engine. + * If the directory does not exist, return FALSE. + * If the directory exists, but "$dirpath/metadata" or "$dirpath/recordings" + * exist as non-directory files, return FALSE. + * Otherwise, return TRUE. + * + * Create the "metadata" and "recordings" directories if they are not there. + */ +int maybe_create_spool_dir(char *dirpath) { + struct stat info; + int spool_good = TRUE; + + if (stat(dirpath, &info) != 0) { + fprintf(stderr, "Spool directory \"%s\" does not exist.\n", dirpath); + spool_good = FALSE; + } else if (!S_ISDIR(info.st_mode)) { + fprintf(stderr, "Spool file exists, but \"%s\" is not a directory.\n", dirpath); + spool_good = FALSE; + } else { + // Spool directory exists. Make sure it has inner directories. + int path_len = strlen(dirpath); + char meta_path[path_len + 10]; + char rec_path[path_len + 12]; + snprintf(meta_path, path_len + 10, "%s/metadata", dirpath); + snprintf(rec_path, path_len + 12, "%s/recordings", dirpath); + + if (stat(meta_path, &info) != 0) { + fprintf(stdout, "Creating metadata directory \"%s\".\n", meta_path); + mkdir(meta_path, 0660); + } else if(!S_ISDIR(info.st_mode)) { + fprintf(stderr, "Metadata file exists, but \"%s\" is not a directory.\n", meta_path); + spool_good = FALSE; + } + + if (stat(rec_path, &info) != 0) { + fprintf(stdout, "Creating recordings directory \"%s\".\n", rec_path); + mkdir(rec_path, 0660); + } else if(!S_ISDIR(info.st_mode)) { + fprintf(stderr, "Recordings file exists, but \"%s\" is not a directory.\n", rec_path); + spool_good = FALSE; + } + } + + return spool_good; +} + +/** + * Create a call metadata file in a temporary location. + * Attaches the filepath and the file pointer to the call struct. + */ +str *setup_meta_file(struct call *call) { + int rand_bytes = 16; + str *meta_filepath = malloc(sizeof(str)); + // Initially file extension is ".tmp". When call is over, it changes to ".txt". + char *path_chars = rand_affixed_str(rand_bytes, "/tmp/rtpengine-meta-", ".tmp"); + meta_filepath = str_init(meta_filepath, path_chars); + call->meta_filepath = meta_filepath; + FILE *mfp = fopen(meta_filepath->s, "w"); + if (mfp == NULL) { + ilog(LOG_ERROR, "Could not open metadata file: %s", meta_filepath->s); + free(call->meta_filepath->s); + free(call->meta_filepath); + call->meta_filepath = NULL; + } + call->meta_fp = mfp; + return meta_filepath; +} + +/** + * Writes metadata to metafile, closes file, and renames it to finished location. + * Returns non-zero for failure. + */ +int meta_file_finish(struct call *call) { + int return_code = 0; + + if (call->meta_fp != NULL) { + fprintf(call->meta_fp, "\n%s\n", call->metadata->s); + fclose(call->meta_fp); + + // Get the filename (in between its directory and the file extension) + // and move it to the finished file location. + // Rename extension to ".txt". + int fn_len; + char *meta_filename = strrchr(call->meta_filepath->s, '/'); + char *meta_ext = NULL; + if (meta_filename == NULL) + meta_filename = call->meta_filepath->s; + else + meta_filename = meta_filename + 1; + // We can always expect a file extension + meta_ext = strrchr(meta_filename, '.'); + fn_len = meta_ext - meta_filename; + int prefix_len = 30; // for "/var/spool/rtpengine/metadata/" + int ext_len = 4; // for ".txt" + char new_metapath[prefix_len + fn_len + ext_len + 1]; + snprintf(new_metapath, prefix_len+fn_len+1, "/var/spool/rtpengine/metadata/%s", meta_filename); + snprintf(new_metapath + prefix_len+fn_len, ext_len+1, ".txt"); + return_code = return_code | rename(call->meta_filepath->s, new_metapath); + } + if (call->meta_filepath != NULL) { + free(call->meta_filepath->s); + free(call->meta_filepath); + } + + return return_code; +} + +/** + * Generate a random PCAP filepath to write recorded RTP stream. + */ +str *setup_recording_file(struct call *call, struct call_monologue *monologue) { + str *recording_path = NULL; + if (call->record_call + && monologue->recording_pd == NULL && monologue->recording_pdumper == NULL) { + int rand_bytes = 16; + recording_path = malloc(sizeof(str)); + char *path_chars = rand_affixed_str(rand_bytes, "/var/spool/rtpengine/recordings/", ".pcap"); + + recording_path = str_init(recording_path, path_chars); + monologue->recording_path = recording_path; + + call->recording_pcaps = g_slist_prepend(call->recording_pcaps, g_strdup(path_chars)); + /* monologue->recording_file */ + monologue->recording_pd = pcap_open_dead(DLT_RAW, 65535); + monologue->recording_pdumper = pcap_dump_open(monologue->recording_pd, path_chars); + } else { + monologue->recording_path = NULL; + monologue->recording_pd = NULL; + monologue->recording_pdumper = NULL; + } + + return recording_path; +} + +/** + * Write out a PCAP packet with payload string. + * A fair amount extraneous of packet data is spoofed. + */ +void stream_pcap_dump(pcap_dumper_t *pdumper, str *s) { + // Wrap RTP in fake UDP packet header + // Right now, we spoof it all + u_int16_t udp_len = ((u_int16_t)s->len) + 8; + u_int16_t udp_header[4]; + udp_header[0] = htons(5028); // source port + udp_header[1] = htons(50116); // destination port + udp_header[2] = htons(udp_len); // packet length + udp_header[3] = 0; // checksum + + // Wrap RTP in fake IP packet header + u_int8_t ip_header[20]; + u_int16_t *ip_total_length = (u_int16_t*)(ip_header + 2); + u_int32_t *ip_src_addr = (u_int32_t*)(ip_header + 12); + u_int32_t *ip_dst_addr = (u_int32_t*)(ip_header + 16); + memset(ip_header, 0, 20); + ip_header[0] = 4 << 4; // IP version - 4 bits + ip_header[0] = ip_header[0] | 5; // Internet Header Length (IHL) - 4 bits + ip_header[1] = 0; // DSCP - 6 bits + ip_header[1] = 0; // ECN - 2 bits + *ip_total_length = htons(udp_len + 20); // Total Length (entire packet size) - 2 bytes + ip_header[4] = 0; ip_header[5] = 0 ; // Identification - 2 bytes + ip_header[6] = 0; // Flags - 3 bits + ip_header[7] = 0; // Fragment Offset - 13 bits + ip_header[8] = 64; // TTL - 1 byte + ip_header[9] = 17; // Protocol (defines protocol in data portion) - 1 byte + ip_header[10] = 0; ip_header[11] = 0; // Header Checksum - 2 bytes + *ip_src_addr = htonl(2130706433); // Source IP (set to localhost) - 4 bytes + *ip_dst_addr = htonl(2130706433); // Destination IP (set to localhost) - 4 bytes + + // Set up PCAP packet header + struct pcap_pkthdr header; + ZERO(header); + header.ts = g_now; + header.caplen = s->len + 28; + // This must be the same value we use in `pcap_open_dead` + header.len = s->len + 28; + + // Copy all the headers and payload into a new string + unsigned char pkt_s[*ip_total_length]; + memcpy(pkt_s, ip_header, 20); + memcpy(pkt_s + 20, udp_header, 8); + memcpy(pkt_s + 28, s->s, s->len); + + // Write the packet to the PCAP file + // Casting quiets compiler warning. + pcap_dump((unsigned char *)pdumper, &header, (unsigned char *)pkt_s); +} diff --git a/daemon/fs.h b/daemon/fs.h new file mode 100644 index 000000000..3b6d627f4 --- /dev/null +++ b/daemon/fs.h @@ -0,0 +1,53 @@ +#ifndef __FS_H__ +#define __FS_H__ + +#include +#include +#include +#include "call.h" + +/** + * Initialize RTP Engine filesystem settings and structure. + * Check for or create the RTP Engine spool directory. + */ +void fs_init(char *spooldir); + +/** + * Create a call metadata file in a temporary location. + * Attaches the filepath and the file pointer to the call struct. + * Returns path to created file. + * + * Metadata file format is (with trailing newline): + * + * /path/to/rec-pcap01.pcap + * /path/to/rec-pcap02.pcap + * ... + * /path/to/rec-pcap0n.pcap + * + * start timestamp (YYYY-MM-DDThh:mm:ss) + * end timestamp (YYYY-MM-DDThh:mm:ss) + * + * metadata + * + */ +str *setup_meta_file(struct call *call); + +/** + * Writes metadata to metafile, closes file, and renames it to finished location. + * Returns non-zero for failure. + */ +int meta_file_finish(struct call *call); + +/** + * Generate a random PCAP filepath to write recorded RTP stream. + * Returns path to created file. + */ +str *setup_recording_file(struct call *call, struct call_monologue *monologue); + +/** + * Write out a PCAP packet with payload string. + * A fair amount extraneous of packet data is spoofed. + */ +void stream_pcap_dump(pcap_dumper_t *pdumper, str *s); + +#endif diff --git a/daemon/main.c b/daemon/main.c index caddbbe3c..076de3ad3 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -11,8 +11,6 @@ #include #include #include -#include -#include #include "poller.h" #include "control_tcp.h" @@ -32,6 +30,7 @@ #include "socket.h" #include "media_socket.h" #include "homer.h" +#include "fs.h" @@ -520,6 +519,7 @@ static void init_everything() { socket_init(); log_init(); + fs_init("/var/spool/rtpengine"); clock_gettime(CLOCK_REALTIME, &ts); srandom(ts.tv_sec ^ ts.tv_nsec); SSL_library_init(); @@ -551,13 +551,6 @@ static void create_everything(struct main_context *ctx) { struct timeval redis_start, redis_stop; double redis_diff = 0; - // TODO this should be a configurable path - char spoolpath[] = "/var/spool/rtpengine"; - if (!maybe_create_spool_dir(spoolpath)) { - fprintf(stderr, "Error while setting up spool directory \"%s\".\n", spoolpath); - exit(-1); - } - if (table < 0) goto no_kernel; if (kernel_create_table(table)) { @@ -696,53 +689,6 @@ no_kernel: } -/** - * Sets up the spool directory for RTP Engine. - * If the directory does not exist, return FALSE. - * If the directory exists, but "$dirpath/metadata" or "$dirpath/recordings" - * exist as non-directory files, return FALSE. - * Otherwise, return TRUE. - * - * Create the "metadata" and "recordings" directories if they are not there. - */ -int maybe_create_spool_dir(char *dirpath) { - struct stat info; - int spool_good = TRUE; - - if (stat(dirpath, &info) != 0) { - fprintf(stderr, "Spool directory \"%s\" does not exist.\n", dirpath); - spool_good = FALSE; - } else if (!S_ISDIR(info.st_mode)) { - fprintf(stderr, "Spool file exists, but \"%s\" is not a directory.\n", dirpath); - spool_good = FALSE; - } else { - // Spool directory exists. Make sure it has inner directories. - int path_len = strlen(dirpath); - char meta_path[path_len + 10]; - char rec_path[path_len + 12]; - snprintf(meta_path, path_len + 10, "%s/metadata", dirpath); - snprintf(rec_path, path_len + 12, "%s/recordings", dirpath); - - if (stat(meta_path, &info) != 0) { - fprintf(stdout, "Creating metadata directory \"%s\".\n", meta_path); - mkdir(meta_path, 0700); - } else if(!S_ISDIR(info.st_mode)) { - fprintf(stderr, "Metadata file exists, but \"%s\" is not a directory.\n", meta_path); - spool_good = FALSE; - } - - if (stat(rec_path, &info) != 0) { - fprintf(stdout, "Creating recordings directory \"%s\".\n", rec_path); - mkdir(rec_path, 0700); - } else if(!S_ISDIR(info.st_mode)) { - fprintf(stderr, "Recordings file exists, but \"%s\" is not a directory.\n", rec_path); - spool_good = FALSE; - } - } - - return spool_good; -} - int main(int argc, char **argv) { struct main_context ctx; int idx=0; diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 412bcd141..9e9ef52bc 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -18,6 +18,7 @@ #include "aux.h" #include "log_funcs.h" #include "poller.h" +#include "fs.h" @@ -48,8 +49,6 @@ struct streamhandler { static void determine_handler(struct packet_stream *in, const struct packet_stream *out); -static void stream_pcap_dump(pcap_dumper_t *pdumper, str *s); - static int __k_null(struct rtpengine_srtp *s, struct packet_stream *); static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *); static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *); @@ -1369,56 +1368,6 @@ unlock_out: } -static void stream_pcap_dump(pcap_dumper_t *pdumper, str *s) { - // Wrap RTP in fake UDP packet header - // Right now, we spoof it all - u_int16_t udp_len = ((u_int16_t)s->len) + 8; - u_int16_t udp_header[4]; - udp_header[0] = htons(5028); // source port - udp_header[1] = htons(50116); // destination port - udp_header[2] = htons(udp_len); // packet length - udp_header[3] = 0; // checksum - - // Wrap RTP in fake IP packet header - u_int8_t ip_header[20]; - u_int16_t *ip_total_length = (u_int16_t*)(ip_header + 2); - u_int32_t *ip_src_addr = (u_int32_t*)(ip_header + 12); - u_int32_t *ip_dst_addr = (u_int32_t*)(ip_header + 16); - memset(ip_header, 0, 20); - ip_header[0] = 4 << 4; // IP version - 4 bits - ip_header[0] = ip_header[0] | 5; // Internet Header Length (IHL) - 4 bits - ip_header[1] = 0; // DSCP - 6 bits - ip_header[1] = 0; // ECN - 2 bits - *ip_total_length = htons(udp_len + 20); // Total Length (entire packet size) - 2 bytes - ip_header[4] = 0; ip_header[5] = 0 ; // Identification - 2 bytes - ip_header[6] = 0; // Flags - 3 bits - ip_header[7] = 0; // Fragment Offset - 13 bits - ip_header[8] = 64; // TTL - 1 byte - ip_header[9] = 17; // Protocol (defines protocol in data portion) - 1 byte - ip_header[10] = 0; ip_header[11] = 0; // Header Checksum - 2 bytes - *ip_src_addr = htonl(2130706433); // Source IP (set to localhost) - 4 bytes - *ip_dst_addr = htonl(2130706433); // Destination IP (set to localhost) - 4 bytes - - // Set up PCAP packet header - struct pcap_pkthdr header; - ZERO(header); - header.ts = g_now; - header.caplen = s->len + 28; - // This must be the same value we use in `pcap_open_dead` - header.len = s->len + 28; - - // Copy all the headers and payload into a new string - unsigned char pkt_s[*ip_total_length]; - memcpy(pkt_s, ip_header, 20); - memcpy(pkt_s + 20, udp_header, 8); - memcpy(pkt_s + 28, s->s, s->len); - - // Write the packet to the PCAP file - // Casting quiets compiler warning. - pcap_dump((unsigned char *)pdumper, &header, (unsigned char *)pkt_s); -} - - static void stream_fd_readable(int fd, void *p, uintptr_t u) { struct stream_fd *sfd = p; char buf[RTP_BUFFER_SIZE]; diff --git a/daemon/str.c b/daemon/str.c index ceada3781..150812a96 100644 --- a/daemon/str.c +++ b/daemon/str.c @@ -33,3 +33,48 @@ str *__str_sprintf(const char *fmt, ...) { void str_slice_free(void *p) { g_slice_free1(sizeof(str), p); } + + +/** + * Generates a random string sandwiched between affixes. + * Will create the char string for you. Don't forget to clean up! + */ +char *rand_affixed_str(int num_bytes, char *prefix, char *suffix) { + int rand_len = num_bytes*2 + 1; + char rand_prefix[rand_len]; + int prefix_len = strlen(prefix); + int suffix_len = strlen(suffix); + char *full_path = calloc(rand_len + prefix_len + suffix_len, sizeof(char)); + + rand_hex_str(rand_prefix, num_bytes); + snprintf(full_path, rand_len+prefix_len, "%s%s", prefix, rand_prefix); + snprintf(full_path + rand_len+prefix_len-1, suffix_len+1, "%s", suffix); + return full_path; +} + +/** + * Generates a random hexadecimal string representing n random bytes. + * rand_str length must be 2*num_bytes + 1. + */ +char *rand_hex_str(char *rand_str, int num_bytes) { + char rand_tmp[3]; + u_int8_t rand_byte; + int i, n; + // We might convert an int to a hex string shorter than 2 digits. + // This causes those strings to have leading '0' characters. + for (i=0; i