dev: mark paths likely/unlikely
[netsniff-ng.git] / staging / tools.c
blob96f0bb71220efc8de9a1f7fce138c85fcd19a0d0
1 /*
2 * Mausezahn - A fast versatile traffic generator
3 * Copyright (C) 2008-2010 Herbert Haas
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License version 2 as published by the
7 * Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html
21 ////////////////////////////////////////////////////////////////////////////////////////////
23 // Contains various tools to ease development of new modules:
24 //
25 // getarg ............. scans string for arguments and returns assigned value
26 // str2int ............. Converts a string into unsigned long int in a safe way
27 // str2lint ............ Same as str2int but returns an unsigned long long int
28 // xstr2int ............ Same as str2int but expects hex digits
29 // xstr2lint ........... Same as above but returns an unsigned long long int
30 // get_ip_range_dst .... Parses string for an IP range and sets start/stop addresses
31 // get_ip_range_src .... Same for source addresses
32 // check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately
33 // get_port_range ...... Parses string for a dst|src-port range and sets start/stop values
34 // get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control
35 // get_mpls_params ..... Parses string for MPLS parameters (label, exp, BOS, TTL)
36 // exists .............. Parses a string for a single character and returns "1" if found
37 // mz_strisbinary ...... Checks whether string consists only of 0 and 1, returns how many digits total
38 // str2bin8 ............ Converts a string containing max 8 binary digits into a number
39 // str2bin16 ........... Converts a string containing max 16 binary digits into a number
40 // char2bits ........... Converts a char into a string containing ones and zeros
41 // getfullpath_cfg ..... Creates a full filename with path to the desired config directory
42 // getfullpath_log ..... Creates a full filename with path to the desired logging directory
43 // mz_strncpy .......... A safer implementation of strncpy
44 // number_of_args ...... Returns number of arguments of the Mausezahn argument string
45 // mz_strisnum ......... Returns 1 if string only consists of decimal digits
46 // mz_strishex ......... Returns 1 if string only consists of hexadecimal digits
47 // mz_strcmp ........... Matches a string or a prefix of it with given min-length
48 // Example usage: User CLI input
49 // mz_tok .............. Decomposes a string into tokens and maps them to args
50 // Example usage: IPv6-addresses, user input for MPLS-tags
51 // delay_parse ......... Parses one or two strings for a delay specification and sets a struct timespec
52 //
53 ////////////////////////////////////////////////////////////////////////////////////////////
55 #include "mz.h"
59 // Scan 'str' for an argument 'arg_name' and returns its value in arg_value
60 // Return value: number of occurences of arg_name
61 // Note that if arg_name occurs multiple times, the last found value is returned.
62 // If last argument (arg_value) is set to NULL it will be ignored.
63 // Example:
64 // int i;
65 // char ip[64];
66 // i = getarg ("request, da=10.1.1.2, SYN", "da", ip);
67 // ...will assign "10.1.1.2" to ip and the occurence i is set to 1.
68 int getarg(char *str, char *arg_name, char *arg_value)
70 char tmp[MAX_PAYLOAD_SIZE];
71 char *str1, *str2, *token, *subtoken;
72 char *saveptr1, *saveptr2;
73 int j, occurence=0;
75 strncpy(tmp,str,MAX_PAYLOAD_SIZE); // only operate on local strings
77 for (j = 1, str1 = tmp; ; j++, str1 = NULL)
80 token = strtok_r(str1, ",", &saveptr1);
81 if (token == NULL)
82 break;
84 str2 = token;
85 if ( (subtoken = strtok_r(str2, " =", &saveptr2))!=NULL)
87 if (strcasecmp(subtoken,arg_name)==0)
89 occurence+=1;
90 //printf("found %s\n",arg_name);
91 if ( (subtoken = strtok_r(NULL, " =", &saveptr2))!=NULL)
93 // argument has a value!
94 //printf("%s has value: [%s]\n",arg_name, subtoken);
95 if (arg_value!=NULL)
97 strcpy(arg_value,subtoken);
102 else
103 break;
105 return occurence;
109 // Convert str to (unsigned long) int
110 // Return value: the unsigned long int
111 unsigned long int str2int(char *str)
113 unsigned long int i;
115 errno=0;
117 i = strtoul(str, (char **)NULL, 10);
119 if ((errno == ERANGE && (i == ULONG_MAX))
120 || (errno != 0 && i == 0))
122 perror("strtoul");
125 return i;
130 // Convert str to (unsigned long long) int
131 // Return value: the unsigned long long int
132 unsigned long long int str2lint(char *str)
134 unsigned long long int i;
136 errno=0;
138 i = strtoull(str, (char **)NULL, 10);
140 if ((errno == ERANGE && (i == ULLONG_MAX))
141 || (errno != 0 && i == 0))
143 perror("strtoull");
146 return i;
150 // Convert hex-str to (unsigned long) int
151 // Return value: the unsigned long int
152 unsigned long int xstr2int(char *str)
154 unsigned long int i;
156 errno=0;
158 i = strtoul(str, (char **)NULL, 16);
160 if ((errno == ERANGE && (i == ULONG_MAX))
161 || (errno != 0 && i == 0))
164 perror("strtoul");
167 return i;
171 // Convert hex-str to (unsigned long long) int
172 // Return value: the unsigned long long int
173 unsigned long long int xstr2lint(char *str)
175 unsigned long long int i;
177 errno=0;
179 i = strtoull(str, (char **)NULL, 16);
181 if ((errno == ERANGE && (i == ULLONG_MAX))
182 || (errno != 0 && i == 0))
184 perror("strtoull");
187 return i;
193 // Parses string 'arg' for an IP range and finds start and stop IP addresses.
194 // Return value: 0 upon success, 1 upon failure.
196 // NOTE: The results are written in the following variables:
198 // (u_int32_t) tx.ip_dst_start ... contains start value
199 // (u_int32_t) tx.ip_dst_stop ... contains stop value
200 // (u_int32_t) tx.ip_dst ... initialized with start value
201 // int tx.ip_dst_isrange ... set to 1 if above values valid
203 // Possible range specifications:
205 // 1) 192.168.0.0-192.168.0.12
206 // 2) 10.2.11.0-10.55.13.2
207 // 3) 172.18.96.0/19
209 // That is:
211 // FIRST detect a range by scanning for the "-" OR "/" chars
212 // THEN determine start and stop value and store them as normal unsigned integers
214 int get_ip_range_dst (char *arg)
217 int
218 i, len,
219 found_slash=0, found_dash=0;
221 unsigned int q;
222 u_int32_t mask, invmask;
224 char *start_str, *stop_str;
226 len = strnlen(arg, 32);
228 if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars)
229 return 1; // ERROR: no range
231 // Find "-" or "/"
232 for (i=0; i<len; i++)
234 if (arg[i]=='/') found_slash=1;
235 if (arg[i]=='-') found_dash=1;
238 if ((found_slash) && (found_dash))
239 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
241 if (found_dash)
243 start_str = strtok (arg, "-");
244 stop_str = strtok (NULL, "-");
246 // These are the start and stop IP addresses of the range:
247 tx.ip_dst_start = str2ip32 (start_str);
248 tx.ip_dst_stop = str2ip32 (stop_str);
249 tx.ip_dst_h = tx.ip_dst_start;
250 tx.ip_dst = str2ip32_rev (start_str);
252 if (tx.ip_dst_start < tx.ip_dst_stop)
254 // Set range flag:
255 tx.ip_dst_isrange = 1;
256 return 0;
258 else
260 tx.ip_dst_isrange = 0;
261 return 1; // ERROR: stop value must be greater than start value !!!
264 else if (found_slash)
266 start_str = strtok (arg, "/");
267 stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24"
269 q = (unsigned int) str2int (stop_str);
271 mask = 0xffffffff;
272 mask <<= (32-q);
273 invmask = 0xffffffff - mask;
275 tx.ip_dst_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id
276 tx.ip_dst_stop = tx.ip_dst_start | invmask;
277 tx.ip_dst_h = tx.ip_dst_start;
278 tx.ip_dst = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id
279 tx.ip_dst_isrange = 1;
280 return 0;
284 return 1; // ERROR: The specified argument string is not a range!
291 // Parses string 'arg' for an IP range and finds start and stop IP addresses.
292 // Return value: 0 upon success, 1 upon failure.
294 // NOTE: The results are written in the following variables:
296 // (u_int32_t) tx.ip_src_start ... contains start value
297 // (u_int32_t) tx.ip_src_stop ... contains stop value
298 // (u_int32_t) tx.ip_src ... initialized with start value
299 // int tx.ip_src_isrange ... set to 1 if above values valid
301 // Possible range specifications:
303 // 1) 192.168.0.0-192.168.0.12
304 // 2) 10.2.11.0-10.55.13.2
305 // 3) 172.18.96.0/19
307 // That is:
309 // FIRST detect a range by scanning for the "-" OR "/" chars
310 // THEN determine start and stop value and store them as normal unsigned integers
312 int get_ip_range_src (char *arg)
315 int
316 i, len,
317 found_slash=0, found_dash=0;
319 unsigned int q;
320 u_int32_t mask, invmask;
322 char *start_str, *stop_str;
325 len = strnlen(arg,32);
327 if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars)
328 return 1; // ERROR: no range
330 // Find "-" or "/"
331 for (i=0; i<len; i++)
333 if (arg[i]=='/') found_slash=1;
334 if (arg[i]=='-') found_dash=1;
337 if ((found_slash) && (found_dash))
338 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
340 if (found_dash)
342 start_str = strtok (arg, "-");
343 stop_str = strtok (NULL, "-");
345 // These are the start and stop IP addresses of the range:
346 tx.ip_src_start = str2ip32 (start_str);
347 tx.ip_src_stop = str2ip32 (stop_str);
348 tx.ip_src_h = tx.ip_src_start;
349 tx.ip_src = str2ip32_rev (start_str);
351 if (tx.ip_src_start < tx.ip_src_stop)
353 // Set range flag:
354 tx.ip_src_isrange = 1;
355 return 0;
357 else
359 tx.ip_src_isrange = 0;
360 return 1; // ERROR: stop value must be greater than start value !!!
363 else if (found_slash)
365 start_str = strtok (arg, "/");
366 stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24"
368 q = (unsigned int) str2int (stop_str);
370 mask = 0xffffffff;
371 mask <<= (32-q);
372 invmask = 0xffffffff - mask;
374 tx.ip_src_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id
375 tx.ip_src_stop = tx.ip_src_start | invmask;
376 tx.ip_src_h = tx.ip_src_start;
377 tx.ip_src = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id
378 tx.ip_src_isrange = 1;
379 return 0;
382 return 1; // ERROR: The specified argument string is not a range!
387 // Scans tx.eth_dst_txt or tx.eth_src_txt and sets the corresponding
388 // MAC addresses (tx.eth_dst or tx.eth_src) accordingly.
389 // Argument: What string should be checked, ETH_SRC or ETH_DST.
391 // Return value:
392 // 0 when a MAC address has been set or
393 // 1 when not set (or wrongly set)
395 // Currently eth_src|dst_txt can be:
396 // 'rand', 'own', 'bc'|'bcast', 'stp', 'pvst', 'cisco',
397 // or a real mac address.
399 // TODO: implement other important MAC addresses
400 int check_eth_mac_txt(int src_or_dst)
402 char *eth_mac_txt;
403 u_int8_t *eth_mac;
404 int *eth_rand;
405 int i;
407 // Check argument
408 if (src_or_dst == ETH_SRC)
410 eth_mac_txt = tx.eth_src_txt;
411 eth_mac = tx.eth_src;
412 eth_rand = &tx.eth_src_rand;
414 else if (src_or_dst == ETH_DST)
416 eth_mac_txt = tx.eth_dst_txt;
417 eth_mac = tx.eth_dst;
418 eth_rand = &tx.eth_dst_rand;
420 else
422 return 1; // wrong argument
426 // Did the user really specify a dst-address?
427 if (strnlen(eth_mac_txt, 18)==0)
429 return 1; // No.
433 // Okay, lets check the commandline argument:
435 // Do you want a random MAC?
436 // TODO: Consider enforcement of unicast addresses
437 if (strncmp(eth_mac_txt, "rand", 4)==0)
439 eth_mac[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
440 eth_mac[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
441 eth_mac[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
442 eth_mac[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
443 eth_mac[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
444 eth_mac[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
445 *eth_rand = 1;
447 // Do you want your own interface MAC?
448 else if (strncmp(eth_mac_txt, "own", 3)==0)
450 for (i=0; i<6; i++)
452 eth_mac[i] = tx.eth_mac_own[i];
455 // Do you want a broadcast MAC?
456 else if (strncmp(eth_mac_txt, "bc", 2)==0) // NOTE that this also fetches "bcast"
458 str2hex_mac("FF:FF:FF:FF:FF:FF", eth_mac);
460 // Do you want the IEEE address 'all bridges' used for STP?
461 else if (strncmp(eth_mac_txt, "stp", 3)==0) //
463 str2hex_mac("01:80:C2:00:00:00", eth_mac); // IEEE for all bridges
465 // Do you want the Cisco address e. g. for CDP, VTP?
466 else if (strncmp(eth_mac_txt, "cisco", 5)==0)
468 str2hex_mac("01:00:0C:CC:CC:CC", eth_mac);
470 // Do you want the Cisco address e. g. for CDP, VTP?
471 else if (strncmp(eth_mac_txt, "pvst", 5)==0)
473 str2hex_mac("01:00:0C:CC:CC:CD", eth_mac);
475 // The string MUST contain a mac address
476 // TODO: CHECK whether the string has correct format for a mac address!
477 else
479 str2hex_mac(eth_mac_txt, eth_mac);
482 return 0;
489 // Scans argument for a port number or range and sets
490 // the corresponding values in the tx struct:
492 // a) tx.sp_start, tx.sp_stop, tx.sp = tx.sp_start
494 // ** OR **
496 // b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start
498 // Arguments:
500 // - 'sp_or_dp' is either SRC_PORT or DST_PORT
501 // - 'arg' contains the port range as string such as 1-1024
503 // Return value: 0 on success, 1 upon failure
505 int get_port_range (int sp_or_dp, char *arg)
508 int i, len, found_dash=0;
510 u_int32_t tmp1, tmp2;
512 u_int16_t
513 *port,
514 *start,
515 *stop;
516 int
517 *isrange;
519 char *start_str, *stop_str;
522 // Check which port to manage
523 if (sp_or_dp == DST_PORT)
525 port = &tx.dp;
526 start = &tx.dp_start;
527 stop = &tx.dp_stop;
528 isrange = &tx.dp_isrange;
530 else if (sp_or_dp == SRC_PORT)
532 port = &tx.sp;
533 start = &tx.sp_start;
534 stop = &tx.sp_stop;
535 isrange = &tx.sp_isrange;
537 else
539 return 1; // error
543 len = strnlen(arg,12);
544 if (len==0) return 1; // error
546 // Find "-"
547 for (i=0; i<len; i++)
549 if (arg[i]=='-') found_dash=1;
552 if (found_dash) // range specified
554 start_str = strtok (arg, "-");
555 stop_str = strtok (NULL, "-");
557 tmp1 = str2int (start_str);
558 if ( (tmp1<0)||(tmp1>65535))
560 fprintf(stderr," mz/get_port_range: Invalid port range!\n");
561 exit (-1);
563 *start = tmp1;
565 tmp2 = str2int (stop_str);
566 if ( (tmp2<0)||(tmp2>65535))
568 fprintf(stderr," mz/get_port_range: Invalid port range!\n");
569 exit (-1);
571 *stop = tmp2;
573 if (tmp1>tmp2) // swap start/stop values!
575 *start = tmp2;
576 *stop = tmp1;
579 *port = *start;
580 *isrange = 1;
582 return 0;
584 else // single port number
586 tmp1 = str2int (arg);
587 if ( (tmp1<0)||(tmp1>65535)) tmp1=0;
588 *port = tmp1;
589 *isrange = 0;
590 return 0;
593 return 1; // error
599 // Scans argument for TCP flags and sets
600 // tx.tcp_control accordingly.
602 // Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr
603 // Valid delimiters are: | or + or -
604 // Return value: 0 on success, 1 upon failure
606 int get_tcp_flags (char* flags)
608 char *f;
610 // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr
611 // ecn...ECN-Echo, cwr...Congestion Window Reduced
613 if (strnlen(flags,40)==0) return 1; // error
616 f = strtok (flags, "|+-");
619 if (strncmp(f,"fin",3)==0)
621 tx.tcp_control = tx.tcp_control | 1;
623 else if (strncmp(f,"syn",3)==0)
625 tx.tcp_control = tx.tcp_control | 2;
627 else if (strncmp(f,"rst",3)==0)
629 tx.tcp_control = tx.tcp_control | 4;
631 else if (strncmp(f,"psh",3)==0)
633 tx.tcp_control = tx.tcp_control | 8;
635 else if (strncmp(f,"ack",3)==0)
637 tx.tcp_control = tx.tcp_control | 16;
639 else if (strncmp(f,"urg",3)==0)
641 tx.tcp_control = tx.tcp_control | 32;
643 else if (strncmp(f,"ecn",3)==0)
645 tx.tcp_control = tx.tcp_control | 64;
647 else if (strncmp(f,"cwr",3)==0)
649 tx.tcp_control = tx.tcp_control | 128;
652 } while ( (f=strtok(NULL, "|+-")) != NULL);
654 return 0;
659 // Scans string 'params' for MPLS parameters
660 // and sets tx.mpls_* accordingly.
662 // CLI Syntax Examples:
664 // -M help .... shows syntax
666 // -M 800 .... label=800
667 // -M 800:S .... label=800 and BOS flag set
668 // -M 800:S:64 .... label=800, BOS, TTL=64
669 // -M 800:64:S .... same
670 // -M 64:77 .... label=64, TTL=77
671 // -M 64:800 .... INVALID
672 // -M 800:64 .... label=800, TTL=64
673 // -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!)
675 // Note: S = BOS(1), s = NOT-BOS(0)
677 // Valid delimiters: :-.,+
678 // Return value: 0 on success, 1 upon failure
679 int get_mpls_params(char *p)
682 char *f1, *f2, *f3, *f4;
683 char params[256];
685 tx.mpls_exp = 0;
686 tx.mpls_ttl = 255;
688 strncpy(params, p, 256);
690 if (strncmp(params,"help",4)==0)
692 fprintf(stderr,"\n"
693 MAUSEZAHN_VERSION
694 "\n"
695 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
696 "| where each header may consist of the following parameters:\n"
697 "|\n"
698 "| label ... the MPLS label (mandatory, 0..1048575)\n"
699 "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n"
700 "| TTL ..... Time To Live (default: 255)\n"
701 "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n"
702 "| will have BOS=1. If desired you can set this flag for any header\n"
703 "| inbetween but this will lead to an invalid packet. Simply use\n"
704 "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n"
705 "| the LAST argument.\n"
706 "|\n"
707 "| Examples:\n"
708 "|\n"
709 "| -M 800 .... label=800\n"
710 "| -M 800:6 .... label=800 and CoS=6\n"
711 "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n"
712 "| -M 800:S .... label=800 and BOS=1\n"
713 "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n"
714 "|\n"
715 "| multiple headers:\n"
716 "|\n"
717 "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n"
718 "| middle label=20 with CoS=7,\n"
719 "| inner label=30 (this one is closest to L3).\n"
720 "|\n"
721 "| Valid delimiters inside a header: : - . +\n"
722 "|\n"
723 "\n");
724 exit (0);
726 else
729 if ( (f1 = strtok (params, ":-.+")) == NULL )
731 return 1; // error!
734 tx.mpls_label = (u_int32_t) str2int (f1);
735 if (tx.mpls_label>1048575)
737 tx.mpls_label = 1048575; // 2^20
738 fprintf(stderr," Warning: MPLS label too big! Reduced to maximum allowed value.\n");
743 if ( (f2 = strtok (NULL, ":-.+")) != NULL ) // 2nd param set
745 if (strncmp(f2,"S",1)==0)
747 tx.mpls_bos = 1;
748 return 0;
750 else if (strncmp(f2,"s",1)==0)
752 tx.mpls_bos = 0;
753 return 0;
755 else
757 tx.mpls_exp = (u_int8_t) str2int (f2);
758 if (tx.mpls_exp > 7)
760 tx.mpls_exp = 7;
761 fprintf(stderr," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n");
766 if ( (f3 = strtok (NULL, ":-.+")) != NULL ) // 3rd param set
768 if (strncmp(f3,"S",1)==0)
770 tx.mpls_bos = 1;
771 return 0;
773 else if (strncmp(f3,"s",1)==0)
775 tx.mpls_bos = 0;
776 return 0;
778 else
780 if ((u_int16_t) str2int (f3)>255)
782 fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
783 tx.mpls_ttl = 255;
785 else
787 tx.mpls_ttl = (u_int8_t) str2int (f3);
791 if ( (f4 = strtok (NULL, ":-.+")) != NULL ) // 4th param set
794 if (strncmp(f3,"S",1)==0)
796 tx.mpls_bos = 1;
798 else if (strncmp(f3,"s",1)==0)
800 tx.mpls_bos = 0;
809 return 0;
813 // Parses str for occurence of character or sequence ch.
814 // Returns number of occurences
815 int exists(char* str, char* ch)
817 int i,match;
819 size_t len_str, len_ch;
821 len_str = strlen(str);
822 len_ch = strlen(ch);
823 match=0;
825 for (i=0; i<len_str; i++)
827 if (strcmp(str++,ch)==0) match++;
830 return match;
835 // Checks if str consists only of 0 and 1
837 // RETURN VALUE:
839 // 0 if invalid chars found or str empty
840 // n if str consists exactly of n binary digits
841 int mz_strisbinary(char *str)
843 int i, len, ret=0;
845 len = strlen(str);
846 if (len==0) return 0;
848 for (i=0; i<len; i++) {
849 if ((str[i]=='0') || (str[i]=='1')) {
850 ret++;
851 } else {
852 return 0;
855 return ret;
862 // Converts a string containing (max 8) binary digits into a number
863 // RETURN VALUE:
865 // Either the number on success
866 // Or -1 upon failure
868 int str2bin8 (char *str)
870 int i, n, ret=0;
872 n=mz_strisbinary(str);
874 if ((!n) || (n>8)) return -1;
876 for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) );
877 return ret;
883 // Converts a string containing (max 16) binary digits into a number
884 // RETURN VALUE:
886 // Either the number on success
887 // Or -1 upon failure
889 long int str2bin16 (char *str)
891 int i, n;
892 long int ret=0;
894 n=mz_strisbinary(str);
896 if ((!n) || (n>16)) return -1;
898 for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) ); // C is great ;-)
899 return ret;
904 // Converts a char into a string containing ones and zeros
906 // EXAMPLE:
908 // char c = 0x81; char str[16];
909 // char2bits(c, str);
910 // printf("%s\n",str); => "1 0 0 0 0 0 0 1"
912 int char2bits (char c, char *str)
914 int i,j=1;
915 char tmp[]="0 0 0 0 0 0 0 0";
917 for (i=0; i<8; i++)
919 if (c&j) tmp[14-i*2]='1';
920 j=j*2;
923 strncpy(str, tmp, 15);
924 return 0;
928 // Takes filename and prepends valid configuration directory
930 // 1) prefer configurable mz_default_config_path[]
931 // 2) otherwise use MZ_DEFAULT_CONFIG_PATH
933 // NOTE: 'filename' finally holds the full path
934 // and must therefore be big enough
937 // RETURN VALUE:
938 // 0 upon success
939 // 1 upon failure
941 int getfullpath_cfg (char *filename)
943 int lenf, lenp;
944 char tmp[32];
946 lenf = strnlen(filename, 32);
948 // filename not given?
949 if ((lenf==0) || (lenf==32)) return 1;
951 strncpy(tmp, filename, 32);
953 // Prefer user-defined path if provided:
954 lenp = strnlen(mz_default_config_path,255);
956 if (lenp) {
957 if (strncmp(mz_default_config_path+lenp-1, "/",1))
958 strncat(mz_default_config_path, "/",1);
959 snprintf(filename, 255, "%s%s",mz_default_config_path,tmp);
961 else {
962 lenp = strlen(MZ_DEFAULT_CONFIG_PATH);
963 snprintf(filename, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH,tmp);
966 if ((lenf+lenp)>255) return 1;
968 return 0;
973 // Takes filename and prepends valid logging directory
975 // 1) prefer configurable mz_default_log_path[]
976 // 2) otherwise use MZ_DEFAULT_LOG_PATH
978 // NOTE: filename is overwritten and must be big enough to hold full path!
980 int getfullpath_log (char *filename)
982 int lenf, lenp;
983 char tmp[32];
985 lenf = strnlen(filename, 32);
987 // filename not given?
988 if ((lenf==0) || (lenf==32)) return 1;
990 strncpy(tmp, filename, 32);
992 // Prefer user-defined path if provided:
993 lenp = strnlen(mz_default_log_path,255);
994 if (lenp) {
995 if (strncmp(mz_default_log_path+lenp-1, "/",1))
996 strncat(mz_default_log_path, "/",1);
997 snprintf(filename, 255, "%s%s",mz_default_log_path,tmp);
999 else {
1000 lenp = strlen(MZ_DEFAULT_LOG_PATH);
1001 snprintf(filename, 255, "%s%s",MZ_DEFAULT_LOG_PATH,tmp);
1004 if ((lenf+lenp)>255) return 1;
1006 return 0;
1009 // Behaves much like strncpy but additionally ensures
1010 // that dest is always \0-terminated.
1012 // USAGE NOTE: If you know exactly the length n of your string,
1013 // then you must provide n+1 to support the termination character.
1015 // EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n)
1016 // would result in dest={H,e,l,l,\0}.
1017 // Therefore the correct usage is:
1018 // mz_strncpy(dest, src, strlen(src)+1);
1019 // =============
1021 // RETURN VALUE: pointer to dest
1022 char * mz_strncpy(char *dest, const char *src, size_t n)
1024 char *tmp;
1025 tmp = strncpy(dest, src, n);
1026 dest[n-1]='\0';
1027 return tmp;
1033 // Helper function to count the number of arguments
1034 // in the Mausezahn argument string (comma separated args)
1036 // RETURN VALUE: Number of arguments
1038 // TODO: Improve it. Use strtok.
1040 int number_of_args (char *str)
1042 int len=0, i=0, commas=1;
1043 if ((len=strnlen(str,MAX_PAYLOAD_SIZE))<2) return 0; // no valid argument
1044 for (i=0; i<len; i++) if (str[i]==',') commas++;
1045 if (str[len-1]==',') commas--; // comma at the end!
1046 return commas;
1051 // Checks if str consists only of digits 0..9
1053 // RETURN VALUE:
1055 // 0 if invalid chars found or str empty
1056 // n if str consists exactly of n digits
1057 int mz_strisnum(char *str)
1059 int i, len, ret=0;
1061 len = strlen(str);
1062 if (len==0) return 0;
1064 for (i=0; i<len; i++) {
1065 if (isdigit(str[i])) {
1066 ret++;
1067 } else {
1068 return 0;
1071 return ret;
1075 // Checks if str consists only of hex digits 0..9 and a..f
1077 // RETURN VALUE:
1079 // 0 if invalid chars found or str empty
1080 // n if str consists exactly of n digits
1081 int mz_strishex(char *str)
1083 int i, len, ret=0;
1085 len = strlen(str);
1086 if (len==0) return 0;
1088 for (i=0; i<len; i++) {
1089 if (isxdigit(str[i])) {
1090 ret++;
1091 } else {
1092 return 0;
1095 return ret;
1099 // Returns an 4-byte random number
1101 u_int32_t mz_rand32 (void)
1103 static unsigned int r=0;
1104 srand(r);
1105 r=rand();
1106 return (r<<16 | r);
1114 // Compares user-provided string with a specified string.
1116 // Return value:
1118 // 0 if at least min characters match
1119 // 1 if at least one character of usr does NOT match the corresponding character in str.
1121 // Note: Case-insensitive!
1122 // Goal: Should be more practical and secure than strcmp (and related)
1123 int mz_strcmp(char* usr_orig, char* str_orig, int min)
1125 int i, same=0, usrlen, max;
1126 char usr[80], str[80];
1128 usrlen = strlen(usr_orig);
1129 max = strlen(str_orig);
1131 strncpy(usr, usr_orig, 80);
1132 strncpy(str, str_orig, 80);
1134 // User provided not enough or too many chars
1135 if ((usrlen<min) || (usrlen>max)) return 1;
1137 // now check how many bytes really match
1138 for (i=0; i<usrlen; i++) {
1139 if (strncasecmp(&usr[i], &str[i], 1)==0) {
1140 same++;
1144 if (same<usrlen) return 1;
1146 return 0;
1153 // PURPOSE:
1155 // Maps an arbitrary number of tokens from 'str' which are separated by
1156 // a character 'delim' into provided arguments.
1158 // USAGE EXAMPLE:
1160 // char str[]="Am:Dam:Des";
1161 // char t1[64], t2[64], t3[64], t4[64];
1163 // mz_tok (str, ":", 4, t1, t2, t3, t4)
1165 // => t1="Am", t2="Dam", t3="Des", t4=NULL
1167 // NOTE:
1169 // 1. If the delimiter symbol occurs twice without gap, it is interpreted
1170 // as 'fill-up' command. To avoid ambiguities this may only occur once.
1171 // See the IPv6 address format shortcuts as similar example.
1173 // 2. If there are less tokens than allowed, the arguments are filled up
1174 // in order, while the remaining are casted to NULL:
1176 // 3. str must be smaller than 4096 bytes!
1178 // 4. To mitigate buffer overflow problems, the maximum token size is
1179 // currently limited to 64 bytes. Therefore it is recommended to
1180 // allocate 64 bytes for each argument.
1182 // RETURN VALUE: Number of returned tokens or -1 upon error
1184 int mz_tok(char * str, char * delim, int anz, ...)
1187 va_list ap;
1188 int i=0, n=0, len, llen, rlen, ltok=0, rtok=0;
1189 char *d, *l, *r, *token, *saveptr, *arg;
1190 char str2[4096], delim2[4]="", delim3[4]="";;
1192 if (strlen(delim)!=1) return -1; // delim must contain a single character!
1193 strncpy(str2, str, 4095); // protect the original str from strtok => operate on a copy only
1194 len = strlen(str2);
1196 // Check if some tokens are omitted (::)
1197 strncpy(delim2, delim, 1); strncat(delim2, delim, 1); // create the double-delim
1198 strncpy(delim3, delim2, 2); strncat(delim3, delim, 1); // create the double-delim
1199 if (strstr(str2, delim3)!=NULL) return -1; // Error: ':::' occured!
1201 if ( (d=strstr(str2, delim2))!=NULL ) { // delim2 ('::') found
1202 // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::"
1203 if (strlen(d)>2) { // '::' is not at the end of str2
1204 r=d+2; // r points to beginning of right string
1205 if (strstr(r, delim2)!=NULL) return -1; // Error: '::' occurs more than once!
1206 rtok++; // there is at least one token in the right string
1207 rlen = strlen(r);
1208 for(i=0;i<rlen;i++) if (strncmp(r++,delim,1)==0) rtok++;
1210 else
1211 rlen = 0;
1213 if (rlen<(len-2)) { // '::' is not at the beginning of str2
1214 l=d-1; // l points to end of left string
1215 ltok++;
1216 llen = len - rlen - 2;
1217 for(i=0;i<llen;i++) if (strncmp(l--,delim,1)==0) ltok++;
1219 //printf("ltok=%i, rtok=%i\n",ltok,rtok);
1220 if ((ltok+rtok)>anz) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping)
1222 else
1223 ltok=len+1; // makes subsequent algorithm to ignore exception handling
1227 rtok=anz-rtok;
1228 va_start(ap, anz);
1230 token = strtok_r(str2, delim, &saveptr);
1231 if (token==NULL) { va_end(ap); return n; }
1233 for(i=0; i<anz; i++) {
1234 arg = va_arg(ap, char *);
1235 if ( (token==NULL) || // less tokens than arguments => assign NULL to the remaining arguments!
1236 ((i>=ltok) && (i<rtok))) {
1237 arg[0] = 0x00;
1239 else { // we still have tokens...
1240 n++;
1241 strncpy(arg, token, 64);
1242 token = strtok_r(NULL, delim, &saveptr);
1246 va_end(ap);
1247 return n;
1256 // PURPOSE: Simplify reading of user delay specifications.
1257 // Parse 'a' and 'b' and configure a struct timespec, i. e. seconds and nanoseconds.
1259 // Typically 'a' contains only the value and 'b' the unit.
1260 // But if b==NULL then 'a' may also contain the unit such as "100msec"
1262 // Allowed units are: nsec, usec, sec, min, hour
1264 // NOTE: If no unit is given then assume msec as default unit
1266 // RETURN VALUE: 0 upon success, 1 upon error (bad arguments)
1268 int delay_parse (struct timespec *t, char *a, char *b)
1270 int i;
1271 unsigned int sfactor=0, nfactor=1000000; // assume msec as default unit
1272 unsigned long long delay, sdelay, ndelay;
1274 if (b==NULL) { // only one argument, but may contain an unit (such as '314sec')
1275 if (strstr(a, "msec")) {
1276 nfactor=1000000;
1278 else if (strstr(a, "usec")) {
1279 nfactor=1000;
1281 else if (strstr(a, "nsec")) {
1282 nfactor=1;
1284 else if (strstr(a, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1285 sfactor=1;
1286 nfactor=0;
1288 else if (strstr(a, "min")) {
1289 sfactor=60;
1290 nfactor=0;
1292 else if (strstr(a, "hour")) {
1293 sfactor=3600;
1294 nfactor=0;
1296 else { // Unit not found; check for non-digits!
1297 // NOTE: we do not currently catch wrong strings that contain sec, usec, or msec.
1299 for (i=0; i<strlen(a); i++) {
1300 if (!isdigit(a[i])) return 1; // invalid unit
1302 nfactor=1000000; // no unit given => assume msec
1304 } else { // caller specified two arguments
1305 if (mz_strcmp(b,"nsec", 1)==0)
1306 nfactor=1;
1307 else if (mz_strcmp(b,"usec", 1)==0)
1308 nfactor=1000;
1309 else if (mz_strcmp(b,"msec", 1)==0)
1310 nfactor=1000000;
1311 else if (mz_strcmp(b,"sec", 1)==0) {
1312 sfactor=1;
1313 nfactor=0;
1315 else if (mz_strcmp(b,"min", 1)==0) {
1316 sfactor=60;
1317 nfactor=0;
1319 else if (mz_strcmp(b,"hour", 1)==0) {
1320 sfactor=3600;
1321 nfactor=0;
1323 else return 1; // Invalid unit
1326 // Get user-defined actual value:
1327 delay = strtoull(a, (char **)NULL, 10);
1328 if ((errno==ERANGE) || (delay>999999999L)) { // see man 2 nanosleep
1329 return 2; // Value too large! Supported range is from 0 to 999999999
1332 sdelay = delay * sfactor;
1333 ndelay = delay * nfactor;
1335 if (ndelay>999999999L) {
1336 sdelay = ndelay/1000000000L;
1337 ndelay = ndelay - (sdelay*1000000000L);
1340 t->tv_sec = sdelay;
1341 t->tv_nsec = ndelay;
1342 return 0;