diff options
author | Daniel Borkmann <dborkman@redhat.com> | 2013-05-13 13:53:27 +0200 |
---|---|---|
committer | Daniel Borkmann <dborkman@redhat.com> | 2013-05-13 15:10:16 +0200 |
commit | d0009856814c13d13770db5aadd7b2fabf947776 (patch) | |
tree | 6d18a94439f27f3c2685f05c57435116673f40cc /staging/tools.c | |
parent | 2b100f7515dbd01032967c2d1b81d2f8d63bf9b5 (diff) |
staging: add mausezahn staging directory
After some back and forth, we decided that it is easier to maintain
mausezahn in a staging directory until it is fully reworked and
cleaned up to be ready to be fully integrated. This way, it is better
than having it in a separate branch, and we can also accept patches
from outside more easily. Also, while at it, fix up some function
mismatches with libcli.
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Diffstat (limited to 'staging/tools.c')
-rw-r--r-- | staging/tools.c | 1344 |
1 files changed, 1344 insertions, 0 deletions
diff --git a/staging/tools.c b/staging/tools.c new file mode 100644 index 0000000..96f0bb7 --- /dev/null +++ b/staging/tools.c @@ -0,0 +1,1344 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Contains various tools to ease development of new modules: +// +// getarg ............. scans string for arguments and returns assigned value +// str2int ............. Converts a string into unsigned long int in a safe way +// str2lint ............ Same as str2int but returns an unsigned long long int +// xstr2int ............ Same as str2int but expects hex digits +// xstr2lint ........... Same as above but returns an unsigned long long int +// get_ip_range_dst .... Parses string for an IP range and sets start/stop addresses +// get_ip_range_src .... Same for source addresses +// check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately +// get_port_range ...... Parses string for a dst|src-port range and sets start/stop values +// get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control +// get_mpls_params ..... Parses string for MPLS parameters (label, exp, BOS, TTL) +// exists .............. Parses a string for a single character and returns "1" if found +// mz_strisbinary ...... Checks whether string consists only of 0 and 1, returns how many digits total +// str2bin8 ............ Converts a string containing max 8 binary digits into a number +// str2bin16 ........... Converts a string containing max 16 binary digits into a number +// char2bits ........... Converts a char into a string containing ones and zeros +// getfullpath_cfg ..... Creates a full filename with path to the desired config directory +// getfullpath_log ..... Creates a full filename with path to the desired logging directory +// mz_strncpy .......... A safer implementation of strncpy +// number_of_args ...... Returns number of arguments of the Mausezahn argument string +// mz_strisnum ......... Returns 1 if string only consists of decimal digits +// mz_strishex ......... Returns 1 if string only consists of hexadecimal digits +// mz_strcmp ........... Matches a string or a prefix of it with given min-length +// Example usage: User CLI input +// mz_tok .............. Decomposes a string into tokens and maps them to args +// Example usage: IPv6-addresses, user input for MPLS-tags +// delay_parse ......... Parses one or two strings for a delay specification and sets a struct timespec +// +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "mz.h" + + + +// Scan 'str' for an argument 'arg_name' and returns its value in arg_value +// Return value: number of occurences of arg_name +// Note that if arg_name occurs multiple times, the last found value is returned. +// If last argument (arg_value) is set to NULL it will be ignored. +// Example: +// int i; +// char ip[64]; +// i = getarg ("request, da=10.1.1.2, SYN", "da", ip); +// ...will assign "10.1.1.2" to ip and the occurence i is set to 1. +int getarg(char *str, char *arg_name, char *arg_value) +{ + char tmp[MAX_PAYLOAD_SIZE]; + char *str1, *str2, *token, *subtoken; + char *saveptr1, *saveptr2; + int j, occurence=0; + + strncpy(tmp,str,MAX_PAYLOAD_SIZE); // only operate on local strings + + for (j = 1, str1 = tmp; ; j++, str1 = NULL) + { + + token = strtok_r(str1, ",", &saveptr1); + if (token == NULL) + break; + + str2 = token; + if ( (subtoken = strtok_r(str2, " =", &saveptr2))!=NULL) + { + if (strcasecmp(subtoken,arg_name)==0) + { + occurence+=1; + //printf("found %s\n",arg_name); + if ( (subtoken = strtok_r(NULL, " =", &saveptr2))!=NULL) + { + // argument has a value! + //printf("%s has value: [%s]\n",arg_name, subtoken); + if (arg_value!=NULL) + { + strcpy(arg_value,subtoken); + } + } + } + } + else + break; + } + return occurence; +} + + +// Convert str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int str2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoul"); + } + + return i; +} + + + +// Convert str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int str2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + +// Convert hex-str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int xstr2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + + perror("strtoul"); + } + + return i; +} + + +// Convert hex-str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int xstr2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_dst_start ... contains start value +// (u_int32_t) tx.ip_dst_stop ... contains stop value +// (u_int32_t) tx.ip_dst ... initialized with start value +// int tx.ip_dst_isrange ... set to 1 if above values valid +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_dst (char *arg) +{ + + int + i, len, + found_slash=0, found_dash=0; + + unsigned int q; + u_int32_t mask, invmask; + + char *start_str, *stop_str; + + len = strnlen(arg, 32); + + if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i<len; i++) + { + if (arg[i]=='/') found_slash=1; + if (arg[i]=='-') found_dash=1; + } + + if ((found_slash) && (found_dash)) + exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!! + + if (found_dash) + { + start_str = strtok (arg, "-"); + stop_str = strtok (NULL, "-"); + + // These are the start and stop IP addresses of the range: + tx.ip_dst_start = str2ip32 (start_str); + tx.ip_dst_stop = str2ip32 (stop_str); + tx.ip_dst_h = tx.ip_dst_start; + tx.ip_dst = str2ip32_rev (start_str); + + if (tx.ip_dst_start < tx.ip_dst_stop) + { + // Set range flag: + tx.ip_dst_isrange = 1; + return 0; + } + else + { + tx.ip_dst_isrange = 0; + return 1; // ERROR: stop value must be greater than start value !!! + } + } + else if (found_slash) + { + start_str = strtok (arg, "/"); + stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24" + + q = (unsigned int) str2int (stop_str); + + mask = 0xffffffff; + mask <<= (32-q); + invmask = 0xffffffff - mask; + + tx.ip_dst_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + tx.ip_dst_stop = tx.ip_dst_start | invmask; + tx.ip_dst_h = tx.ip_dst_start; + tx.ip_dst = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id + tx.ip_dst_isrange = 1; + return 0; + } + + + return 1; // ERROR: The specified argument string is not a range! + +} + + + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_src_start ... contains start value +// (u_int32_t) tx.ip_src_stop ... contains stop value +// (u_int32_t) tx.ip_src ... initialized with start value +// int tx.ip_src_isrange ... set to 1 if above values valid +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_src (char *arg) +{ + + int + i, len, + found_slash=0, found_dash=0; + + unsigned int q; + u_int32_t mask, invmask; + + char *start_str, *stop_str; + + + len = strnlen(arg,32); + + if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i<len; i++) + { + if (arg[i]=='/') found_slash=1; + if (arg[i]=='-') found_dash=1; + } + + if ((found_slash) && (found_dash)) + exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!! + + if (found_dash) + { + start_str = strtok (arg, "-"); + stop_str = strtok (NULL, "-"); + + // These are the start and stop IP addresses of the range: + tx.ip_src_start = str2ip32 (start_str); + tx.ip_src_stop = str2ip32 (stop_str); + tx.ip_src_h = tx.ip_src_start; + tx.ip_src = str2ip32_rev (start_str); + + if (tx.ip_src_start < tx.ip_src_stop) + { + // Set range flag: + tx.ip_src_isrange = 1; + return 0; + } + else + { + tx.ip_src_isrange = 0; + return 1; // ERROR: stop value must be greater than start value !!! + } + } + else if (found_slash) + { + start_str = strtok (arg, "/"); + stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24" + + q = (unsigned int) str2int (stop_str); + + mask = 0xffffffff; + mask <<= (32-q); + invmask = 0xffffffff - mask; + + tx.ip_src_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + tx.ip_src_stop = tx.ip_src_start | invmask; + tx.ip_src_h = tx.ip_src_start; + tx.ip_src = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id + tx.ip_src_isrange = 1; + return 0; + } + + return 1; // ERROR: The specified argument string is not a range! + +} + + +// Scans tx.eth_dst_txt or tx.eth_src_txt and sets the corresponding +// MAC addresses (tx.eth_dst or tx.eth_src) accordingly. +// Argument: What string should be checked, ETH_SRC or ETH_DST. +// +// Return value: +// 0 when a MAC address has been set or +// 1 when not set (or wrongly set) +// +// Currently eth_src|dst_txt can be: +// 'rand', 'own', 'bc'|'bcast', 'stp', 'pvst', 'cisco', +// or a real mac address. +// +// TODO: implement other important MAC addresses +int check_eth_mac_txt(int src_or_dst) +{ + char *eth_mac_txt; + u_int8_t *eth_mac; + int *eth_rand; + int i; + + // Check argument + if (src_or_dst == ETH_SRC) + { + eth_mac_txt = tx.eth_src_txt; + eth_mac = tx.eth_src; + eth_rand = &tx.eth_src_rand; + } + else if (src_or_dst == ETH_DST) + { + eth_mac_txt = tx.eth_dst_txt; + eth_mac = tx.eth_dst; + eth_rand = &tx.eth_dst_rand; + } + else + { + return 1; // wrong argument + } + + + // Did the user really specify a dst-address? + if (strnlen(eth_mac_txt, 18)==0) + { + return 1; // No. + } + + + // Okay, lets check the commandline argument: + // + // Do you want a random MAC? + // TODO: Consider enforcement of unicast addresses + if (strncmp(eth_mac_txt, "rand", 4)==0) + { + eth_mac[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + *eth_rand = 1; + } + // Do you want your own interface MAC? + else if (strncmp(eth_mac_txt, "own", 3)==0) + { + for (i=0; i<6; i++) + { + eth_mac[i] = tx.eth_mac_own[i]; + } + } + // Do you want a broadcast MAC? + else if (strncmp(eth_mac_txt, "bc", 2)==0) // NOTE that this also fetches "bcast" + { + str2hex_mac("FF:FF:FF:FF:FF:FF", eth_mac); + } + // Do you want the IEEE address 'all bridges' used for STP? + else if (strncmp(eth_mac_txt, "stp", 3)==0) // + { + str2hex_mac("01:80:C2:00:00:00", eth_mac); // IEEE for all bridges + } + // Do you want the Cisco address e. g. for CDP, VTP? + else if (strncmp(eth_mac_txt, "cisco", 5)==0) + { + str2hex_mac("01:00:0C:CC:CC:CC", eth_mac); + } + // Do you want the Cisco address e. g. for CDP, VTP? + else if (strncmp(eth_mac_txt, "pvst", 5)==0) + { + str2hex_mac("01:00:0C:CC:CC:CD", eth_mac); + } + // The string MUST contain a mac address + // TODO: CHECK whether the string has correct format for a mac address! + else + { + str2hex_mac(eth_mac_txt, eth_mac); + } + + return 0; +} + + + + + +// Scans argument for a port number or range and sets +// the corresponding values in the tx struct: +// +// a) tx.sp_start, tx.sp_stop, tx.sp = tx.sp_start +// +// ** OR ** +// +// b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start +// +// Arguments: +// +// - 'sp_or_dp' is either SRC_PORT or DST_PORT +// - 'arg' contains the port range as string such as 1-1024 +// +// Return value: 0 on success, 1 upon failure +// +int get_port_range (int sp_or_dp, char *arg) +{ + + int i, len, found_dash=0; + + u_int32_t tmp1, tmp2; + + u_int16_t + *port, + *start, + *stop; + int + *isrange; + + char *start_str, *stop_str; + + + // Check which port to manage + if (sp_or_dp == DST_PORT) + { + port = &tx.dp; + start = &tx.dp_start; + stop = &tx.dp_stop; + isrange = &tx.dp_isrange; + } + else if (sp_or_dp == SRC_PORT) + { + port = &tx.sp; + start = &tx.sp_start; + stop = &tx.sp_stop; + isrange = &tx.sp_isrange; + } + else + { + return 1; // error + } + + + len = strnlen(arg,12); + if (len==0) return 1; // error + + // Find "-" + for (i=0; i<len; i++) + { + if (arg[i]=='-') found_dash=1; + } + + if (found_dash) // range specified + { + start_str = strtok (arg, "-"); + stop_str = strtok (NULL, "-"); + + tmp1 = str2int (start_str); + if ( (tmp1<0)||(tmp1>65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *start = tmp1; + + tmp2 = str2int (stop_str); + if ( (tmp2<0)||(tmp2>65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *stop = tmp2; + + if (tmp1>tmp2) // swap start/stop values! + { + *start = tmp2; + *stop = tmp1; + } + + *port = *start; + *isrange = 1; + + return 0; + } + else // single port number + { + tmp1 = str2int (arg); + if ( (tmp1<0)||(tmp1>65535)) tmp1=0; + *port = tmp1; + *isrange = 0; + return 0; + } + + return 1; // error +} + + + + +// Scans argument for TCP flags and sets +// tx.tcp_control accordingly. +// +// Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr +// Valid delimiters are: | or + or - +// Return value: 0 on success, 1 upon failure +// +int get_tcp_flags (char* flags) +{ + char *f; + + // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr + // ecn...ECN-Echo, cwr...Congestion Window Reduced + + if (strnlen(flags,40)==0) return 1; // error + + + f = strtok (flags, "|+-"); + do + { + if (strncmp(f,"fin",3)==0) + { + tx.tcp_control = tx.tcp_control | 1; + } + else if (strncmp(f,"syn",3)==0) + { + tx.tcp_control = tx.tcp_control | 2; + } + else if (strncmp(f,"rst",3)==0) + { + tx.tcp_control = tx.tcp_control | 4; + } + else if (strncmp(f,"psh",3)==0) + { + tx.tcp_control = tx.tcp_control | 8; + } + else if (strncmp(f,"ack",3)==0) + { + tx.tcp_control = tx.tcp_control | 16; + } + else if (strncmp(f,"urg",3)==0) + { + tx.tcp_control = tx.tcp_control | 32; + } + else if (strncmp(f,"ecn",3)==0) + { + tx.tcp_control = tx.tcp_control | 64; + } + else if (strncmp(f,"cwr",3)==0) + { + tx.tcp_control = tx.tcp_control | 128; + } + + } while ( (f=strtok(NULL, "|+-")) != NULL); + + return 0; +} + + + +// Scans string 'params' for MPLS parameters +// and sets tx.mpls_* accordingly. +// +// CLI Syntax Examples: +// +// -M help .... shows syntax +// +// -M 800 .... label=800 +// -M 800:S .... label=800 and BOS flag set +// -M 800:S:64 .... label=800, BOS, TTL=64 +// -M 800:64:S .... same +// -M 64:77 .... label=64, TTL=77 +// -M 64:800 .... INVALID +// -M 800:64 .... label=800, TTL=64 +// -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!) +// +// Note: S = BOS(1), s = NOT-BOS(0) +// +// Valid delimiters: :-.,+ +// Return value: 0 on success, 1 upon failure +int get_mpls_params(char *p) +{ + + char *f1, *f2, *f3, *f4; + char params[256]; + + tx.mpls_exp = 0; + tx.mpls_ttl = 255; + + strncpy(params, p, 256); + + if (strncmp(params,"help",4)==0) + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| MPLS header Syntax: -M label[,label[,label[,...]]]\n" + "| where each header may consist of the following parameters:\n" + "|\n" + "| label ... the MPLS label (mandatory, 0..1048575)\n" + "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n" + "| TTL ..... Time To Live (default: 255)\n" + "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n" + "| will have BOS=1. If desired you can set this flag for any header\n" + "| inbetween but this will lead to an invalid packet. Simply use\n" + "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n" + "| the LAST argument.\n" + "|\n" + "| Examples:\n" + "|\n" + "| -M 800 .... label=800\n" + "| -M 800:6 .... label=800 and CoS=6\n" + "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n" + "| -M 800:S .... label=800 and BOS=1\n" + "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n" + "|\n" + "| multiple headers:\n" + "|\n" + "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n" + "| middle label=20 with CoS=7,\n" + "| inner label=30 (this one is closest to L3).\n" + "|\n" + "| Valid delimiters inside a header: : - . +\n" + "|\n" + "\n"); + exit (0); + } + else + { + + if ( (f1 = strtok (params, ":-.+")) == NULL ) + { + return 1; // error! + } + + tx.mpls_label = (u_int32_t) str2int (f1); + if (tx.mpls_label>1048575) + { + tx.mpls_label = 1048575; // 2^20 + fprintf(stderr," Warning: MPLS label too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f2 = strtok (NULL, ":-.+")) != NULL ) // 2nd param set + { + if (strncmp(f2,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f2,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + tx.mpls_exp = (u_int8_t) str2int (f2); + if (tx.mpls_exp > 7) + { + tx.mpls_exp = 7; + fprintf(stderr," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f3 = strtok (NULL, ":-.+")) != NULL ) // 3rd param set + { + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + if ((u_int16_t) str2int (f3)>255) + { + fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n"); + tx.mpls_ttl = 255; + } + else + { + tx.mpls_ttl = (u_int8_t) str2int (f3); + } + } + + if ( (f4 = strtok (NULL, ":-.+")) != NULL ) // 4th param set + { + + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + } + + } + + } + } + + + return 0; +} + + +// Parses str for occurence of character or sequence ch. +// Returns number of occurences +int exists(char* str, char* ch) +{ + int i,match; + + size_t len_str, len_ch; + + len_str = strlen(str); + len_ch = strlen(ch); + match=0; + + for (i=0; i<len_str; i++) + { + if (strcmp(str++,ch)==0) match++; + } + + return match; +} + + + +// Checks if str consists only of 0 and 1 +// +// RETURN VALUE: +// +// 0 if invalid chars found or str empty +// n if str consists exactly of n binary digits +int mz_strisbinary(char *str) +{ + int i, len, ret=0; + + len = strlen(str); + if (len==0) return 0; + + for (i=0; i<len; i++) { + if ((str[i]=='0') || (str[i]=='1')) { + ret++; + } else { + return 0; + } + } + return ret; +} + + + + + +// Converts a string containing (max 8) binary digits into a number +// RETURN VALUE: +// +// Either the number on success +// Or -1 upon failure +// +int str2bin8 (char *str) +{ + int i, n, ret=0; + + n=mz_strisbinary(str); + + if ((!n) || (n>8)) return -1; + + for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) ); + return ret; +} + + + + +// Converts a string containing (max 16) binary digits into a number +// RETURN VALUE: +// +// Either the number on success +// Or -1 upon failure +// +long int str2bin16 (char *str) +{ + int i, n; + long int ret=0; + + n=mz_strisbinary(str); + + if ((!n) || (n>16)) return -1; + + for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) ); // C is great ;-) + return ret; +} + + + +// Converts a char into a string containing ones and zeros +// +// EXAMPLE: +// +// char c = 0x81; char str[16]; +// char2bits(c, str); +// printf("%s\n",str); => "1 0 0 0 0 0 0 1" +// +int char2bits (char c, char *str) +{ + int i,j=1; + char tmp[]="0 0 0 0 0 0 0 0"; + + for (i=0; i<8; i++) + { + if (c&j) tmp[14-i*2]='1'; + j=j*2; + } + + strncpy(str, tmp, 15); + return 0; +} + + +// Takes filename and prepends valid configuration directory +// +// 1) prefer configurable mz_default_config_path[] +// 2) otherwise use MZ_DEFAULT_CONFIG_PATH +// +// NOTE: 'filename' finally holds the full path +// and must therefore be big enough +// +// +// RETURN VALUE: +// 0 upon success +// 1 upon failure +// +int getfullpath_cfg (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_config_path,255); + + if (lenp) { + if (strncmp(mz_default_config_path+lenp-1, "/",1)) + strncat(mz_default_config_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_config_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_CONFIG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + + + +// Takes filename and prepends valid logging directory +// +// 1) prefer configurable mz_default_log_path[] +// 2) otherwise use MZ_DEFAULT_LOG_PATH +// +// NOTE: filename is overwritten and must be big enough to hold full path! +// +int getfullpath_log (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_log_path,255); + if (lenp) { + if (strncmp(mz_default_log_path+lenp-1, "/",1)) + strncat(mz_default_log_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_log_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_LOG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_LOG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + +// Behaves much like strncpy but additionally ensures +// that dest is always \0-terminated. +// +// USAGE NOTE: If you know exactly the length n of your string, +// then you must provide n+1 to support the termination character. +// +// EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n) +// would result in dest={H,e,l,l,\0}. +// Therefore the correct usage is: +// mz_strncpy(dest, src, strlen(src)+1); +// ============= +// +// RETURN VALUE: pointer to dest +char * mz_strncpy(char *dest, const char *src, size_t n) +{ + char *tmp; + tmp = strncpy(dest, src, n); + dest[n-1]='\0'; + return tmp; +} + + + + +// Helper function to count the number of arguments +// in the Mausezahn argument string (comma separated args) +// +// RETURN VALUE: Number of arguments +// +// TODO: Improve it. Use strtok. +// +int number_of_args (char *str) +{ + int len=0, i=0, commas=1; + if ((len=strnlen(str,MAX_PAYLOAD_SIZE))<2) return 0; // no valid argument + for (i=0; i<len; i++) if (str[i]==',') commas++; + if (str[len-1]==',') commas--; // comma at the end! + return commas; +} + + + +// Checks if str consists only of digits 0..9 +// +// RETURN VALUE: +// +// 0 if invalid chars found or str empty +// n if str consists exactly of n digits +int mz_strisnum(char *str) +{ + int i, len, ret=0; + + len = strlen(str); + if (len==0) return 0; + + for (i=0; i<len; i++) { + if (isdigit(str[i])) { + ret++; + } else { + return 0; + } + } + return ret; +} + + +// Checks if str consists only of hex digits 0..9 and a..f +// +// RETURN VALUE: +// +// 0 if invalid chars found or str empty +// n if str consists exactly of n digits +int mz_strishex(char *str) +{ + int i, len, ret=0; + + len = strlen(str); + if (len==0) return 0; + + for (i=0; i<len; i++) { + if (isxdigit(str[i])) { + ret++; + } else { + return 0; + } + } + return ret; +} + + +// Returns an 4-byte random number +// +u_int32_t mz_rand32 (void) +{ + static unsigned int r=0; + srand(r); + r=rand(); + return (r<<16 | r); +} + + + + + + +// Compares user-provided string with a specified string. +// +// Return value: +// +// 0 if at least min characters match +// 1 if at least one character of usr does NOT match the corresponding character in str. +// +// Note: Case-insensitive! +// Goal: Should be more practical and secure than strcmp (and related) +int mz_strcmp(char* usr_orig, char* str_orig, int min) +{ + int i, same=0, usrlen, max; + char usr[80], str[80]; + + usrlen = strlen(usr_orig); + max = strlen(str_orig); + + strncpy(usr, usr_orig, 80); + strncpy(str, str_orig, 80); + + // User provided not enough or too many chars + if ((usrlen<min) || (usrlen>max)) return 1; + + // now check how many bytes really match + for (i=0; i<usrlen; i++) { + if (strncasecmp(&usr[i], &str[i], 1)==0) { + same++; + } + } + + if (same<usrlen) return 1; + + return 0; +} + + + + + +// PURPOSE: +// +// Maps an arbitrary number of tokens from 'str' which are separated by +// a character 'delim' into provided arguments. +// +// USAGE EXAMPLE: +// +// char str[]="Am:Dam:Des"; +// char t1[64], t2[64], t3[64], t4[64]; +// +// mz_tok (str, ":", 4, t1, t2, t3, t4) +// +// => t1="Am", t2="Dam", t3="Des", t4=NULL +// +// NOTE: +// +// 1. If the delimiter symbol occurs twice without gap, it is interpreted +// as 'fill-up' command. To avoid ambiguities this may only occur once. +// See the IPv6 address format shortcuts as similar example. +// +// 2. If there are less tokens than allowed, the arguments are filled up +// in order, while the remaining are casted to NULL: +// +// 3. str must be smaller than 4096 bytes! +// +// 4. To mitigate buffer overflow problems, the maximum token size is +// currently limited to 64 bytes. Therefore it is recommended to +// allocate 64 bytes for each argument. +// +// RETURN VALUE: Number of returned tokens or -1 upon error + +int mz_tok(char * str, char * delim, int anz, ...) +{ + + va_list ap; + int i=0, n=0, len, llen, rlen, ltok=0, rtok=0; + char *d, *l, *r, *token, *saveptr, *arg; + char str2[4096], delim2[4]="", delim3[4]="";; + + if (strlen(delim)!=1) return -1; // delim must contain a single character! + strncpy(str2, str, 4095); // protect the original str from strtok => operate on a copy only + len = strlen(str2); + + // Check if some tokens are omitted (::) + strncpy(delim2, delim, 1); strncat(delim2, delim, 1); // create the double-delim + strncpy(delim3, delim2, 2); strncat(delim3, delim, 1); // create the double-delim + if (strstr(str2, delim3)!=NULL) return -1; // Error: ':::' occured! + + if ( (d=strstr(str2, delim2))!=NULL ) { // delim2 ('::') found + // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::" + if (strlen(d)>2) { // '::' is not at the end of str2 + r=d+2; // r points to beginning of right string + if (strstr(r, delim2)!=NULL) return -1; // Error: '::' occurs more than once! + rtok++; // there is at least one token in the right string + rlen = strlen(r); + for(i=0;i<rlen;i++) if (strncmp(r++,delim,1)==0) rtok++; + } + else + rlen = 0; + + if (rlen<(len-2)) { // '::' is not at the beginning of str2 + l=d-1; // l points to end of left string + ltok++; + llen = len - rlen - 2; + for(i=0;i<llen;i++) if (strncmp(l--,delim,1)==0) ltok++; + } + //printf("ltok=%i, rtok=%i\n",ltok,rtok); + if ((ltok+rtok)>anz) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping) + } + else + ltok=len+1; // makes subsequent algorithm to ignore exception handling + + + + rtok=anz-rtok; + va_start(ap, anz); + + token = strtok_r(str2, delim, &saveptr); + if (token==NULL) { va_end(ap); return n; } + + for(i=0; i<anz; i++) { + arg = va_arg(ap, char *); + if ( (token==NULL) || // less tokens than arguments => assign NULL to the remaining arguments! + ((i>=ltok) && (i<rtok))) { + arg[0] = 0x00; + } + else { // we still have tokens... + n++; + strncpy(arg, token, 64); + token = strtok_r(NULL, delim, &saveptr); + } + } + + va_end(ap); + return n; +} + + + + + + +// +// PURPOSE: Simplify reading of user delay specifications. +// Parse 'a' and 'b' and configure a struct timespec, i. e. seconds and nanoseconds. +// +// Typically 'a' contains only the value and 'b' the unit. +// But if b==NULL then 'a' may also contain the unit such as "100msec" +// +// Allowed units are: nsec, usec, sec, min, hour +// +// NOTE: If no unit is given then assume msec as default unit +// +// RETURN VALUE: 0 upon success, 1 upon error (bad arguments) +// +int delay_parse (struct timespec *t, char *a, char *b) +{ + int i; + unsigned int sfactor=0, nfactor=1000000; // assume msec as default unit + unsigned long long delay, sdelay, ndelay; + + if (b==NULL) { // only one argument, but may contain an unit (such as '314sec') + if (strstr(a, "msec")) { + nfactor=1000000; + } + else if (strstr(a, "usec")) { + nfactor=1000; + } + else if (strstr(a, "nsec")) { + nfactor=1; + } + else if (strstr(a, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings + sfactor=1; + nfactor=0; + } + else if (strstr(a, "min")) { + sfactor=60; + nfactor=0; + } + else if (strstr(a, "hour")) { + sfactor=3600; + nfactor=0; + } + else { // Unit not found; check for non-digits! + // NOTE: we do not currently catch wrong strings that contain sec, usec, or msec. + // + for (i=0; i<strlen(a); i++) { + if (!isdigit(a[i])) return 1; // invalid unit + } + nfactor=1000000; // no unit given => assume msec + } + } else { // caller specified two arguments + if (mz_strcmp(b,"nsec", 1)==0) + nfactor=1; + else if (mz_strcmp(b,"usec", 1)==0) + nfactor=1000; + else if (mz_strcmp(b,"msec", 1)==0) + nfactor=1000000; + else if (mz_strcmp(b,"sec", 1)==0) { + sfactor=1; + nfactor=0; + } + else if (mz_strcmp(b,"min", 1)==0) { + sfactor=60; + nfactor=0; + } + else if (mz_strcmp(b,"hour", 1)==0) { + sfactor=3600; + nfactor=0; + } + else return 1; // Invalid unit + } + + // Get user-defined actual value: + delay = strtoull(a, (char **)NULL, 10); + if ((errno==ERANGE) || (delay>999999999L)) { // see man 2 nanosleep + return 2; // Value too large! Supported range is from 0 to 999999999 + } + + sdelay = delay * sfactor; + ndelay = delay * nfactor; + + if (ndelay>999999999L) { + sdelay = ndelay/1000000000L; + ndelay = ndelay - (sdelay*1000000000L); + } + + t->tv_sec = sdelay; + t->tv_nsec = ndelay; + return 0; +} + |