2 * Mausezahn - A fast versatile traffic generator
3 * Copyright (C) 2008-2010 Herbert Haas
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.
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
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:
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
53 ////////////////////////////////////////////////////////////////////////////////////////////
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.
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
;
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
);
85 if ( (subtoken
= strtok_r(str2
, " =", &saveptr2
))!=NULL
)
87 if (strcasecmp(subtoken
,arg_name
)==0)
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);
97 strcpy(arg_value
,subtoken
);
109 // Convert str to (unsigned long) int
110 // Return value: the unsigned long int
111 unsigned long int str2int(char *str
)
117 i
= strtoul(str
, (char **)NULL
, 10);
119 if ((errno
== ERANGE
&& (i
== ULONG_MAX
))
120 || (errno
!= 0 && i
== 0))
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
;
138 i
= strtoull(str
, (char **)NULL
, 10);
140 if ((errno
== ERANGE
&& (i
== ULLONG_MAX
))
141 || (errno
!= 0 && i
== 0))
150 // Convert hex-str to (unsigned long) int
151 // Return value: the unsigned long int
152 unsigned long int xstr2int(char *str
)
158 i
= strtoul(str
, (char **)NULL
, 16);
160 if ((errno
== ERANGE
&& (i
== ULONG_MAX
))
161 || (errno
!= 0 && i
== 0))
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
;
179 i
= strtoull(str
, (char **)NULL
, 16);
181 if ((errno
== ERANGE
&& (i
== ULLONG_MAX
))
182 || (errno
!= 0 && i
== 0))
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
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
)
219 found_slash
=0, found_dash
=0;
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
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 "-" !!!
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
)
255 tx
.ip_dst_isrange
= 1;
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
);
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;
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
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
)
317 found_slash
=0, found_dash
=0;
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
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 "-" !!!
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
)
354 tx
.ip_src_isrange
= 1;
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
);
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;
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.
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
)
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
;
422 return 1; // wrong argument
426 // Did the user really specify a dst-address?
427 if (strnlen(eth_mac_txt
, 18)==0)
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);
447 // Do you want your own interface MAC?
448 else if (strncmp(eth_mac_txt
, "own", 3)==0)
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!
479 str2hex_mac(eth_mac_txt
, eth_mac
);
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
496 // b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start
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
;
519 char *start_str
, *stop_str
;
522 // Check which port to manage
523 if (sp_or_dp
== DST_PORT
)
526 start
= &tx
.dp_start
;
528 isrange
= &tx
.dp_isrange
;
530 else if (sp_or_dp
== SRC_PORT
)
533 start
= &tx
.sp_start
;
535 isrange
= &tx
.sp_isrange
;
543 len
= strnlen(arg
,12);
544 if (len
==0) return 1; // error
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");
565 tmp2
= str2int (stop_str
);
566 if ( (tmp2
<0)||(tmp2
>65535))
568 fprintf(stderr
," mz/get_port_range: Invalid port range!\n");
573 if (tmp1
>tmp2
) // swap start/stop values!
584 else // single port number
586 tmp1
= str2int (arg
);
587 if ( (tmp1
<0)||(tmp1
>65535)) tmp1
=0;
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
)
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
);
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
;
688 strncpy(params
, p
, 256);
691 if (strncmp(params
,"help",4)==0)
696 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
697 "| where each header may consist of the following parameters:\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"
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"
716 "| multiple headers:\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"
722 "| Valid delimiters inside a header: : - . +\n"
730 if ( (f1
= strtok (params
, ":-.+")) == NULL
)
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)
751 else if (strncmp(f2
,"s",1)==0)
758 tx
.mpls_exp
= (u_int8_t
) str2int (f2
);
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)
774 else if (strncmp(f3
,"s",1)==0)
781 if ((u_int16_t
) str2int (f3
)>255)
783 fprintf(stderr
," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
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)
799 else if (strncmp(f3
,"s",1)==0)
814 // Parses str for occurence of character or sequence ch.
815 // Returns number of occurences
816 int exists(char* str
, char* ch
)
820 size_t len_str
, len_ch
;
822 len_str
= strlen(str
);
826 for (i
=0; i
<len_str
; i
++)
828 if (strcmp(str
++,ch
)==0) match
++;
836 // Checks if str consists only of 0 and 1
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
)
847 if (len
==0) return 0;
849 for (i
=0; i
<len
; i
++) {
850 if ((str
[i
]=='0') || (str
[i
]=='1')) {
863 // Converts a string containing (max 8) binary digits into a number
866 // Either the number on success
867 // Or -1 upon failure
869 int str2bin8 (char *str
)
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
) );
884 // Converts a string containing (max 16) binary digits into a number
887 // Either the number on success
888 // Or -1 upon failure
890 long int str2bin16 (char *str
)
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 ;-)
905 // Converts a char into a string containing ones and zeros
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
)
916 char tmp
[]="0 0 0 0 0 0 0 0";
920 if (c
&j
) tmp
[14-i
*2]='1';
924 strncpy(str
, tmp
, 15);
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
942 int getfullpath_cfg (char *filename
)
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);
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
);
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;
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
)
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);
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
);
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;
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);
1022 // RETURN VALUE: pointer to dest
1023 char * mz_strncpy(char *dest
, const char *src
, size_t n
)
1026 tmp
= strncpy(dest
, src
, n
);
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!
1052 // Checks if str consists only of digits 0..9
1056 // 0 if invalid chars found or str empty
1057 // n if str consists exactly of n digits
1058 int mz_strisnum(char *str
)
1063 if (len
==0) return 0;
1065 for (i
=0; i
<len
; i
++) {
1066 if (isdigit(str
[i
])) {
1076 // Checks if str consists only of hex digits 0..9 and a..f
1080 // 0 if invalid chars found or str empty
1081 // n if str consists exactly of n digits
1082 int mz_strishex(char *str
)
1087 if (len
==0) return 0;
1089 for (i
=0; i
<len
; i
++) {
1090 if (isxdigit(str
[i
])) {
1100 // Returns an 4-byte random number
1102 u_int32_t
mz_rand32 (void)
1104 static unsigned int r
=0;
1115 // Compares user-provided string with a specified string.
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) {
1145 if (same
<usrlen
) return 1;
1156 // Maps an arbitrary number of tokens from 'str' which are separated by
1157 // a character 'delim' into provided arguments.
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
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
, ...)
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
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
1209 for(i
=0;i
<rlen
;i
++) if (strncmp(r
++,delim
,1)==0) rtok
++;
1214 if (rlen
<(len
-2)) { // '::' is not at the beginning of str2
1215 l
=d
-1; // l points to end of left string
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)
1224 ltok
=len
+1; // makes subsequent algorithm to ignore exception handling
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
))) {
1240 else { // we still have tokens...
1242 strncpy(arg
, token
, 64);
1243 token
= strtok_r(NULL
, delim
, &saveptr
);
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
)
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")) {
1279 else if (strstr(a
, "usec")) {
1282 else if (strstr(a
, "nsec")) {
1285 else if (strstr(a
, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1289 else if (strstr(a
, "min")) {
1293 else if (strstr(a
, "hour")) {
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)
1308 else if (mz_strcmp(b
,"usec", 1)==0)
1310 else if (mz_strcmp(b
,"msec", 1)==0)
1312 else if (mz_strcmp(b
,"sec", 1)==0) {
1316 else if (mz_strcmp(b
,"min", 1)==0) {
1320 else if (mz_strcmp(b
,"hour", 1)==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);
1342 t
->tv_nsec
= ndelay
;