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);
690 if (strncmp(params
,"help",4)==0)
695 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
696 "| where each header may consist of the following parameters:\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"
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"
715 "| multiple headers:\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"
721 "| Valid delimiters inside a header: : - . +\n"
729 if ( (f1
= strtok (params
, ":-.+")) == NULL
)
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)
750 else if (strncmp(f2
,"s",1)==0)
757 tx
.mpls_exp
= (u_int8_t
) str2int (f2
);
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)
773 else if (strncmp(f3
,"s",1)==0)
780 if ((u_int16_t
) str2int (f3
)>255)
782 fprintf(stderr
," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
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)
798 else if (strncmp(f3
,"s",1)==0)
813 // Parses str for occurence of character or sequence ch.
814 // Returns number of occurences
815 int exists(char* str
, char* ch
)
819 size_t len_str
, len_ch
;
821 len_str
= strlen(str
);
825 for (i
=0; i
<len_str
; i
++)
827 if (strcmp(str
++,ch
)==0) match
++;
835 // Checks if str consists only of 0 and 1
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
)
846 if (len
==0) return 0;
848 for (i
=0; i
<len
; i
++) {
849 if ((str
[i
]=='0') || (str
[i
]=='1')) {
862 // Converts a string containing (max 8) binary digits into a number
865 // Either the number on success
866 // Or -1 upon failure
868 int str2bin8 (char *str
)
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
) );
883 // Converts a string containing (max 16) binary digits into a number
886 // Either the number on success
887 // Or -1 upon failure
889 long int str2bin16 (char *str
)
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 ;-)
904 // Converts a char into a string containing ones and zeros
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
)
915 char tmp
[]="0 0 0 0 0 0 0 0";
919 if (c
&j
) tmp
[14-i
*2]='1';
923 strncpy(str
, tmp
, 15);
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
941 int getfullpath_cfg (char *filename
)
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);
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
);
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;
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
)
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);
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
);
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;
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);
1021 // RETURN VALUE: pointer to dest
1022 char * mz_strncpy(char *dest
, const char *src
, size_t n
)
1025 tmp
= strncpy(dest
, src
, n
);
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!
1051 // Checks if str consists only of digits 0..9
1055 // 0 if invalid chars found or str empty
1056 // n if str consists exactly of n digits
1057 int mz_strisnum(char *str
)
1062 if (len
==0) return 0;
1064 for (i
=0; i
<len
; i
++) {
1065 if (isdigit(str
[i
])) {
1075 // Checks if str consists only of hex digits 0..9 and a..f
1079 // 0 if invalid chars found or str empty
1080 // n if str consists exactly of n digits
1081 int mz_strishex(char *str
)
1086 if (len
==0) return 0;
1088 for (i
=0; i
<len
; i
++) {
1089 if (isxdigit(str
[i
])) {
1099 // Returns an 4-byte random number
1101 u_int32_t
mz_rand32 (void)
1103 static unsigned int r
=0;
1114 // Compares user-provided string with a specified string.
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) {
1144 if (same
<usrlen
) return 1;
1155 // Maps an arbitrary number of tokens from 'str' which are separated by
1156 // a character 'delim' into provided arguments.
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
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
, ...)
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
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
1208 for(i
=0;i
<rlen
;i
++) if (strncmp(r
++,delim
,1)==0) rtok
++;
1213 if (rlen
<(len
-2)) { // '::' is not at the beginning of str2
1214 l
=d
-1; // l points to end of left string
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)
1223 ltok
=len
+1; // makes subsequent algorithm to ignore exception handling
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
))) {
1239 else { // we still have tokens...
1241 strncpy(arg
, token
, 64);
1242 token
= strtok_r(NULL
, delim
, &saveptr
);
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
)
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")) {
1278 else if (strstr(a
, "usec")) {
1281 else if (strstr(a
, "nsec")) {
1284 else if (strstr(a
, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1288 else if (strstr(a
, "min")) {
1292 else if (strstr(a
, "hour")) {
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)
1307 else if (mz_strcmp(b
,"usec", 1)==0)
1309 else if (mz_strcmp(b
,"msec", 1)==0)
1311 else if (mz_strcmp(b
,"sec", 1)==0) {
1315 else if (mz_strcmp(b
,"min", 1)==0) {
1319 else if (mz_strcmp(b
,"hour", 1)==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);
1341 t
->tv_nsec
= ndelay
;