trafgen: man: Add help for VLAN header function
[netsniff-ng.git] / staging / tools.c
blobbe8eaceb4f5681ddf0952dbab7a3a3f4bf029a48
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);
689 params[255] = '\0';
691 if (strncmp(params,"help",4)==0)
693 fprintf(stderr,"\n"
694 MAUSEZAHN_VERSION
695 "\n"
696 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
697 "| where each header may consist of the following parameters:\n"
698 "|\n"
699 "| label ... the MPLS label (mandatory, 0..1048575)\n"
700 "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n"
701 "| TTL ..... Time To Live (default: 255)\n"
702 "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n"
703 "| will have BOS=1. If desired you can set this flag for any header\n"
704 "| inbetween but this will lead to an invalid packet. Simply use\n"
705 "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n"
706 "| the LAST argument.\n"
707 "|\n"
708 "| Examples:\n"
709 "|\n"
710 "| -M 800 .... label=800\n"
711 "| -M 800:6 .... label=800 and CoS=6\n"
712 "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n"
713 "| -M 800:S .... label=800 and BOS=1\n"
714 "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n"
715 "|\n"
716 "| multiple headers:\n"
717 "|\n"
718 "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n"
719 "| middle label=20 with CoS=7,\n"
720 "| inner label=30 (this one is closest to L3).\n"
721 "|\n"
722 "| Valid delimiters inside a header: : - . +\n"
723 "|\n"
724 "\n");
725 exit (0);
727 else
730 if ( (f1 = strtok (params, ":-.+")) == NULL )
732 return 1; // error!
735 tx.mpls_label = (u_int32_t) str2int (f1);
736 if (tx.mpls_label>1048575)
738 tx.mpls_label = 1048575; // 2^20
739 fprintf(stderr," Warning: MPLS label too big! Reduced to maximum allowed value.\n");
744 if ( (f2 = strtok (NULL, ":-.+")) != NULL ) // 2nd param set
746 if (strncmp(f2,"S",1)==0)
748 tx.mpls_bos = 1;
749 return 0;
751 else if (strncmp(f2,"s",1)==0)
753 tx.mpls_bos = 0;
754 return 0;
756 else
758 tx.mpls_exp = (u_int8_t) str2int (f2);
759 if (tx.mpls_exp > 7)
761 tx.mpls_exp = 7;
762 fprintf(stderr," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n");
767 if ( (f3 = strtok (NULL, ":-.+")) != NULL ) // 3rd param set
769 if (strncmp(f3,"S",1)==0)
771 tx.mpls_bos = 1;
772 return 0;
774 else if (strncmp(f3,"s",1)==0)
776 tx.mpls_bos = 0;
777 return 0;
779 else
781 if ((u_int16_t) str2int (f3)>255)
783 fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
784 tx.mpls_ttl = 255;
786 else
788 tx.mpls_ttl = (u_int8_t) str2int (f3);
792 if ( (f4 = strtok (NULL, ":-.+")) != NULL ) // 4th param set
795 if (strncmp(f3,"S",1)==0)
797 tx.mpls_bos = 1;
799 else if (strncmp(f3,"s",1)==0)
801 tx.mpls_bos = 0;
810 return 0;
814 // Parses str for occurence of character or sequence ch.
815 // Returns number of occurences
816 int exists(char* str, char* ch)
818 int i,match;
820 size_t len_str, len_ch;
822 len_str = strlen(str);
823 len_ch = strlen(ch);
824 match=0;
826 for (i=0; i<len_str; i++)
828 if (strcmp(str++,ch)==0) match++;
831 return match;
836 // Checks if str consists only of 0 and 1
838 // RETURN VALUE:
840 // 0 if invalid chars found or str empty
841 // n if str consists exactly of n binary digits
842 int mz_strisbinary(char *str)
844 int i, len, ret=0;
846 len = strlen(str);
847 if (len==0) return 0;
849 for (i=0; i<len; i++) {
850 if ((str[i]=='0') || (str[i]=='1')) {
851 ret++;
852 } else {
853 return 0;
856 return ret;
863 // Converts a string containing (max 8) binary digits into a number
864 // RETURN VALUE:
866 // Either the number on success
867 // Or -1 upon failure
869 int str2bin8 (char *str)
871 int i, n, ret=0;
873 n=mz_strisbinary(str);
875 if ((!n) || (n>8)) return -1;
877 for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) );
878 return ret;
884 // Converts a string containing (max 16) binary digits into a number
885 // RETURN VALUE:
887 // Either the number on success
888 // Or -1 upon failure
890 long int str2bin16 (char *str)
892 int i, n;
893 long int ret=0;
895 n=mz_strisbinary(str);
897 if ((!n) || (n>16)) return -1;
899 for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) ); // C is great ;-)
900 return ret;
905 // Converts a char into a string containing ones and zeros
907 // EXAMPLE:
909 // char c = 0x81; char str[16];
910 // char2bits(c, str);
911 // printf("%s\n",str); => "1 0 0 0 0 0 0 1"
913 int char2bits (char c, char *str)
915 int i,j=1;
916 char tmp[]="0 0 0 0 0 0 0 0";
918 for (i=0; i<8; i++)
920 if (c&j) tmp[14-i*2]='1';
921 j=j*2;
924 strncpy(str, tmp, 15);
925 return 0;
929 // Takes filename and prepends valid configuration directory
931 // 1) prefer configurable mz_default_config_path[]
932 // 2) otherwise use MZ_DEFAULT_CONFIG_PATH
934 // NOTE: 'filename' finally holds the full path
935 // and must therefore be big enough
938 // RETURN VALUE:
939 // 0 upon success
940 // 1 upon failure
942 int getfullpath_cfg (char *filename)
944 int lenf, lenp;
945 char tmp[32];
947 lenf = strnlen(filename, 32);
949 // filename not given?
950 if ((lenf==0) || (lenf==32)) return 1;
952 strncpy(tmp, filename, 32);
954 // Prefer user-defined path if provided:
955 lenp = strnlen(mz_default_config_path,255);
957 if (lenp) {
958 if (strncmp(mz_default_config_path+lenp-1, "/",1))
959 strncat(mz_default_config_path, "/",1);
960 snprintf(filename, 255, "%s%s",mz_default_config_path,tmp);
962 else {
963 lenp = strlen(MZ_DEFAULT_CONFIG_PATH);
964 snprintf(filename, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH,tmp);
967 if ((lenf+lenp)>255) return 1;
969 return 0;
974 // Takes filename and prepends valid logging directory
976 // 1) prefer configurable mz_default_log_path[]
977 // 2) otherwise use MZ_DEFAULT_LOG_PATH
979 // NOTE: filename is overwritten and must be big enough to hold full path!
981 int getfullpath_log (char *filename)
983 int lenf, lenp;
984 char tmp[32];
986 lenf = strnlen(filename, 32);
988 // filename not given?
989 if ((lenf==0) || (lenf==32)) return 1;
991 strncpy(tmp, filename, 32);
993 // Prefer user-defined path if provided:
994 lenp = strnlen(mz_default_log_path,255);
995 if (lenp) {
996 if (strncmp(mz_default_log_path+lenp-1, "/",1))
997 strncat(mz_default_log_path, "/",1);
998 snprintf(filename, 255, "%s%s",mz_default_log_path,tmp);
1000 else {
1001 lenp = strlen(MZ_DEFAULT_LOG_PATH);
1002 snprintf(filename, 255, "%s%s",MZ_DEFAULT_LOG_PATH,tmp);
1005 if ((lenf+lenp)>255) return 1;
1007 return 0;
1010 // Behaves much like strncpy but additionally ensures
1011 // that dest is always \0-terminated.
1013 // USAGE NOTE: If you know exactly the length n of your string,
1014 // then you must provide n+1 to support the termination character.
1016 // EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n)
1017 // would result in dest={H,e,l,l,\0}.
1018 // Therefore the correct usage is:
1019 // mz_strncpy(dest, src, strlen(src)+1);
1020 // =============
1022 // RETURN VALUE: pointer to dest
1023 char * mz_strncpy(char *dest, const char *src, size_t n)
1025 char *tmp;
1026 tmp = strncpy(dest, src, n);
1027 dest[n-1]='\0';
1028 return tmp;
1034 // Helper function to count the number of arguments
1035 // in the Mausezahn argument string (comma separated args)
1037 // RETURN VALUE: Number of arguments
1039 // TODO: Improve it. Use strtok.
1041 int number_of_args (char *str)
1043 int len=0, i=0, commas=1;
1044 if ((len=strnlen(str,MAX_PAYLOAD_SIZE))<2) return 0; // no valid argument
1045 for (i=0; i<len; i++) if (str[i]==',') commas++;
1046 if (str[len-1]==',') commas--; // comma at the end!
1047 return commas;
1052 // Checks if str consists only of digits 0..9
1054 // RETURN VALUE:
1056 // 0 if invalid chars found or str empty
1057 // n if str consists exactly of n digits
1058 int mz_strisnum(char *str)
1060 int i, len, ret=0;
1062 len = strlen(str);
1063 if (len==0) return 0;
1065 for (i=0; i<len; i++) {
1066 if (isdigit(str[i])) {
1067 ret++;
1068 } else {
1069 return 0;
1072 return ret;
1076 // Checks if str consists only of hex digits 0..9 and a..f
1078 // RETURN VALUE:
1080 // 0 if invalid chars found or str empty
1081 // n if str consists exactly of n digits
1082 int mz_strishex(char *str)
1084 int i, len, ret=0;
1086 len = strlen(str);
1087 if (len==0) return 0;
1089 for (i=0; i<len; i++) {
1090 if (isxdigit(str[i])) {
1091 ret++;
1092 } else {
1093 return 0;
1096 return ret;
1100 // Returns an 4-byte random number
1102 u_int32_t mz_rand32 (void)
1104 static unsigned int r=0;
1105 srand(r);
1106 r=rand();
1107 return (r<<16 | r);
1115 // Compares user-provided string with a specified string.
1117 // Return value:
1119 // 0 if at least min characters match
1120 // 1 if at least one character of usr does NOT match the corresponding character in str.
1122 // Note: Case-insensitive!
1123 // Goal: Should be more practical and secure than strcmp (and related)
1124 int mz_strcmp(char* usr_orig, char* str_orig, int min)
1126 int i, same=0, usrlen, max;
1127 char usr[80], str[80];
1129 usrlen = strlen(usr_orig);
1130 max = strlen(str_orig);
1132 strncpy(usr, usr_orig, 80);
1133 strncpy(str, str_orig, 80);
1135 // User provided not enough or too many chars
1136 if ((usrlen<min) || (usrlen>max)) return 1;
1138 // now check how many bytes really match
1139 for (i=0; i<usrlen; i++) {
1140 if (strncasecmp(&usr[i], &str[i], 1)==0) {
1141 same++;
1145 if (same<usrlen) return 1;
1147 return 0;
1154 // PURPOSE:
1156 // Maps an arbitrary number of tokens from 'str' which are separated by
1157 // a character 'delim' into provided arguments.
1159 // USAGE EXAMPLE:
1161 // char str[]="Am:Dam:Des";
1162 // char t1[64], t2[64], t3[64], t4[64];
1164 // mz_tok (str, ":", 4, t1, t2, t3, t4)
1166 // => t1="Am", t2="Dam", t3="Des", t4=NULL
1168 // NOTE:
1170 // 1. If the delimiter symbol occurs twice without gap, it is interpreted
1171 // as 'fill-up' command. To avoid ambiguities this may only occur once.
1172 // See the IPv6 address format shortcuts as similar example.
1174 // 2. If there are less tokens than allowed, the arguments are filled up
1175 // in order, while the remaining are casted to NULL:
1177 // 3. str must be smaller than 4096 bytes!
1179 // 4. To mitigate buffer overflow problems, the maximum token size is
1180 // currently limited to 64 bytes. Therefore it is recommended to
1181 // allocate 64 bytes for each argument.
1183 // RETURN VALUE: Number of returned tokens or -1 upon error
1185 int mz_tok(char * str, char * delim, int anz, ...)
1188 va_list ap;
1189 int i=0, n=0, len, llen, rlen, ltok=0, rtok=0;
1190 char *d, *l, *r, *token, *saveptr, *arg;
1191 char str2[4096], delim2[4]="", delim3[4]="";;
1193 if (strlen(delim)!=1) return -1; // delim must contain a single character!
1194 strncpy(str2, str, 4095); // protect the original str from strtok => operate on a copy only
1195 len = strlen(str2);
1197 // Check if some tokens are omitted (::)
1198 strncpy(delim2, delim, 1); strncat(delim2, delim, 1); // create the double-delim
1199 strncpy(delim3, delim2, 2); strncat(delim3, delim, 1); // create the double-delim
1200 if (strstr(str2, delim3)!=NULL) return -1; // Error: ':::' occured!
1202 if ( (d=strstr(str2, delim2))!=NULL ) { // delim2 ('::') found
1203 // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::"
1204 if (strlen(d)>2) { // '::' is not at the end of str2
1205 r=d+2; // r points to beginning of right string
1206 if (strstr(r, delim2)!=NULL) return -1; // Error: '::' occurs more than once!
1207 rtok++; // there is at least one token in the right string
1208 rlen = strlen(r);
1209 for(i=0;i<rlen;i++) if (strncmp(r++,delim,1)==0) rtok++;
1211 else
1212 rlen = 0;
1214 if (rlen<(len-2)) { // '::' is not at the beginning of str2
1215 l=d-1; // l points to end of left string
1216 ltok++;
1217 llen = len - rlen - 2;
1218 for(i=0;i<llen;i++) if (strncmp(l--,delim,1)==0) ltok++;
1220 //printf("ltok=%i, rtok=%i\n",ltok,rtok);
1221 if ((ltok+rtok)>anz) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping)
1223 else
1224 ltok=len+1; // makes subsequent algorithm to ignore exception handling
1228 rtok=anz-rtok;
1229 va_start(ap, anz);
1231 token = strtok_r(str2, delim, &saveptr);
1232 if (token==NULL) { va_end(ap); return n; }
1234 for(i=0; i<anz; i++) {
1235 arg = va_arg(ap, char *);
1236 if ( (token==NULL) || // less tokens than arguments => assign NULL to the remaining arguments!
1237 ((i>=ltok) && (i<rtok))) {
1238 arg[0] = 0x00;
1240 else { // we still have tokens...
1241 n++;
1242 strncpy(arg, token, 64);
1243 token = strtok_r(NULL, delim, &saveptr);
1247 va_end(ap);
1248 return n;
1257 // PURPOSE: Simplify reading of user delay specifications.
1258 // Parse 'a' and 'b' and configure a struct timespec, i. e. seconds and nanoseconds.
1260 // Typically 'a' contains only the value and 'b' the unit.
1261 // But if b==NULL then 'a' may also contain the unit such as "100msec"
1263 // Allowed units are: nsec, usec, sec, min, hour
1265 // NOTE: If no unit is given then assume msec as default unit
1267 // RETURN VALUE: 0 upon success, 1 upon error (bad arguments)
1269 int delay_parse (struct timespec *t, char *a, char *b)
1271 int i;
1272 unsigned int sfactor=0, nfactor=1000000; // assume msec as default unit
1273 unsigned long long delay, sdelay, ndelay;
1275 if (b==NULL) { // only one argument, but may contain an unit (such as '314sec')
1276 if (strstr(a, "msec")) {
1277 nfactor=1000000;
1279 else if (strstr(a, "usec")) {
1280 nfactor=1000;
1282 else if (strstr(a, "nsec")) {
1283 nfactor=1;
1285 else if (strstr(a, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1286 sfactor=1;
1287 nfactor=0;
1289 else if (strstr(a, "min")) {
1290 sfactor=60;
1291 nfactor=0;
1293 else if (strstr(a, "hour")) {
1294 sfactor=3600;
1295 nfactor=0;
1297 else { // Unit not found; check for non-digits!
1298 // NOTE: we do not currently catch wrong strings that contain sec, usec, or msec.
1300 for (i=0; i<strlen(a); i++) {
1301 if (!isdigit(a[i])) return 1; // invalid unit
1303 nfactor=1000000; // no unit given => assume msec
1305 } else { // caller specified two arguments
1306 if (mz_strcmp(b,"nsec", 1)==0)
1307 nfactor=1;
1308 else if (mz_strcmp(b,"usec", 1)==0)
1309 nfactor=1000;
1310 else if (mz_strcmp(b,"msec", 1)==0)
1311 nfactor=1000000;
1312 else if (mz_strcmp(b,"sec", 1)==0) {
1313 sfactor=1;
1314 nfactor=0;
1316 else if (mz_strcmp(b,"min", 1)==0) {
1317 sfactor=60;
1318 nfactor=0;
1320 else if (mz_strcmp(b,"hour", 1)==0) {
1321 sfactor=3600;
1322 nfactor=0;
1324 else return 1; // Invalid unit
1327 // Get user-defined actual value:
1328 delay = strtoull(a, (char **)NULL, 10);
1329 if ((errno==ERANGE) || (delay>999999999L)) { // see man 2 nanosleep
1330 return 2; // Value too large! Supported range is from 0 to 999999999
1333 sdelay = delay * sfactor;
1334 ndelay = delay * nfactor;
1336 if (ndelay>999999999L) {
1337 sdelay = ndelay/1000000000L;
1338 ndelay = ndelay - (sdelay*1000000000L);
1341 t->tv_sec = sdelay;
1342 t->tv_nsec = ndelay;
1343 return 0;