Skip to content

Commit 3eab64d

Browse files
committed
Add routines to parse numeric command-line arguments, and use them.
They do various error checks; use them instead of strto*() or atoi().
1 parent dcb1b03 commit 3eab64d

File tree

1 file changed

+158
-40
lines changed

1 file changed

+158
-40
lines changed

tcpdump.c

Lines changed: 158 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,7 @@ The Regents of the University of California. All rights reserved.\n";
165165
#endif
166166

167167
static int Bflag; /* buffer size */
168-
#ifdef HAVE_PCAP_DUMP_FTELL64
169168
static int64_t Cflag; /* rotate dump files after this many bytes */
170-
#else
171-
static long Cflag; /* rotate dump files after this many bytes */
172-
#endif
173169
static int Cflag_count; /* Keep track of which file number we're writing */
174170
static int Dflag; /* list available devices and exit */
175171
#ifdef HAVE_PCAP_FINDALLDEVS_EX
@@ -219,6 +215,12 @@ static int infoprint;
219215
char *program_name;
220216

221217
/* Forwards */
218+
static int parse_int(const char *argname, const char *string, char **endp,
219+
int minval, int maxval, int base);
220+
static u_int parse_u_int(const char *argname, const char *string, char **endp,
221+
u_int minval, u_int maxval, int base);
222+
static int64_t parse_int64(const char *argname, const char *string,
223+
char **endp, int64_t minval, int64_t maxval, int base);
222224
static void (*setsignal (int sig, void (*func)(int)))(int);
223225
static void cleanup(int);
224226
static void child_cleanup(int);
@@ -1422,7 +1424,6 @@ main(int argc, char **argv)
14221424
const char *chroot_dir = NULL;
14231425
#endif
14241426
char *ret = NULL;
1425-
char *end;
14261427
pcap_if_t *devlist;
14271428
long devnum;
14281429
int status;
@@ -1645,26 +1646,24 @@ main(int argc, char **argv)
16451646
break;
16461647

16471648
case 'B':
1648-
Bflag = atoi(optarg)*1024;
1649-
if (Bflag <= 0)
1650-
error("invalid packet buffer size %s", optarg);
1649+
Bflag = parse_int("packet buffer size", optarg, NULL, 1,
1650+
INT_MAX, 10);
1651+
/*
1652+
* Will multiplying it by 1024 overflow?
1653+
*/
1654+
if (Bflag > INT_MAX / 1024)
1655+
error("packet buffer size %s is too large", optarg);
1656+
Bflag *= 1024;
16511657
break;
16521658

16531659
case 'c':
1654-
cnt = atoi(optarg);
1655-
if (cnt <= 0)
1656-
error("invalid packet count %s", optarg);
1660+
cnt = parse_int("packet count", optarg, NULL, 1,
1661+
INT_MAX, 10);
16571662
break;
16581663

16591664
case 'C':
1660-
errno = 0;
1661-
#ifdef HAVE_PCAP_DUMP_FTELL64
1662-
Cflag = strtoint64_t(optarg, &endp, 10);
1663-
#else
1664-
Cflag = strtol(optarg, &endp, 10);
1665-
#endif
1666-
if (endp == optarg || errno != 0 || Cflag <= 0)
1667-
error("invalid file size %s", optarg);
1665+
Cflag = parse_int64("file size", optarg, &endp, 1,
1666+
INT64_MAX, 10);
16681667

16691668
if (*endp == '\0') {
16701669
/*
@@ -1709,7 +1708,7 @@ main(int argc, char **argv)
17091708
break;
17101709

17111710
default:
1712-
error("invalid file size %s", optarg);
1711+
error("invalid file size %s (invalid units)", optarg);
17131712
}
17141713

17151714
/*
@@ -1720,7 +1719,7 @@ main(int argc, char **argv)
17201719
endp++;
17211720
if (*endp != '\0') {
17221721
/* Yes - error */
1723-
error("invalid file size %s", optarg);
1722+
error("invalid file size %s (invalid units)", optarg);
17241723
}
17251724
}
17261725

@@ -1774,9 +1773,8 @@ main(int argc, char **argv)
17741773
break;
17751774

17761775
case 'G':
1777-
Gflag = atoi(optarg);
1778-
if (Gflag < 0)
1779-
error("invalid number of seconds %s", optarg);
1776+
Gflag = parse_int("number of seconds", optarg, NULL, 0,
1777+
INT_MAX, 10);
17801778

17811779
/* We will create one file initially. */
17821780
Gflag_count = 0;
@@ -1895,11 +1893,8 @@ main(int argc, char **argv)
18951893
break;
18961894

18971895
case 's':
1898-
ndo->ndo_snaplen = (int)strtol(optarg, &end, 0);
1899-
if (optarg == end || *end != '\0'
1900-
|| ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN)
1901-
error("invalid snaplen %s (must be >= 0 and <= %d)",
1902-
optarg, MAXIMUM_SNAPLEN);
1896+
ndo->ndo_snaplen = parse_int("snaplen", optarg, NULL,
1897+
0, MAXIMUM_SNAPLEN, 0);
19031898
break;
19041899

19051900
case 'S':
@@ -1978,9 +1973,8 @@ main(int argc, char **argv)
19781973
break;
19791974

19801975
case 'W':
1981-
Wflag = atoi(optarg);
1982-
if (Wflag <= 0)
1983-
error("invalid number of output files %s", optarg);
1976+
Wflag = parse_int("number of output files", optarg,
1977+
NULL, 1, INT_MAX, 10);
19841978
WflagChars = getWflagChars(Wflag);
19851979
break;
19861980

@@ -2056,17 +2050,13 @@ main(int argc, char **argv)
20562050
case OPTION_PRINT_SAMPLING:
20572051
print = 1;
20582052
++ndo->ndo_Sflag;
2059-
ndo->ndo_print_sampling = atoi(optarg);
2060-
if (ndo->ndo_print_sampling <= 0)
2061-
error("invalid print sampling %s", optarg);
2053+
ndo->ndo_print_sampling = parse_int("print sampling",
2054+
optarg, NULL, 1, INT_MAX, 10);
20622055
break;
20632056

20642057
case OPTION_SKIP:
2065-
errno = 0;
2066-
packets_skipped = (u_int)strtoul(optarg, &end, 0);
2067-
if (optarg[0] == '-' || optarg == end || *end != '\0' ||
2068-
errno != 0)
2069-
error("invalid packet skipped %s", optarg);
2058+
packets_skipped = parse_u_int("packet skip count",
2059+
optarg, NULL, 1, UINT_MAX, 0);
20702060
break;
20712061

20722062
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
@@ -2829,6 +2819,134 @@ DIAG_ON_ASSIGN_ENUM
28292819
exit_tcpdump(status == -1 ? S_ERR_HOST_PROGRAM : S_SUCCESS);
28302820
}
28312821

2822+
/*
2823+
* Routines to parse numerical command-line arguments and check for
2824+
* errors, including "too large for that type".
2825+
*/
2826+
static int
2827+
parse_int(const char *argname, const char *string, char **endpp,
2828+
int minval, int maxval, int base)
2829+
{
2830+
long val;
2831+
char *endp;
2832+
2833+
errno = 0;
2834+
val = strtol(string, &endp, base);
2835+
2836+
/*
2837+
* Did it either not parse any of the string, find extra stuff
2838+
* at the end that the caller isn't interested in, or get
2839+
* another parsing error?
2840+
*/
2841+
if (string == endp || (endpp == NULL && *endp != '\0') ||
2842+
(val == 0 && errno == EINVAL)) {
2843+
error("invalid %s \"%s\" (not a valid number)", argname,
2844+
string);
2845+
}
2846+
2847+
/*
2848+
* Did it get a value that's out of range?
2849+
*/
2850+
if (((val == LONG_MAX || val == LONG_MIN) && errno == ERANGE) ||
2851+
val < minval || val > maxval) {
2852+
error("invalid %s %s (must be >= %d and <= %d)",
2853+
argname, string, minval, maxval);
2854+
}
2855+
2856+
/*
2857+
* OK, it passes all the tests.
2858+
*/
2859+
if (endpp != NULL)
2860+
*endpp = endp;
2861+
return ((int)val);
2862+
}
2863+
2864+
static u_int
2865+
parse_u_int(const char *argname, const char *string, char **endpp,
2866+
u_int minval, u_int maxval, int base)
2867+
{
2868+
unsigned long val;
2869+
char *endp;
2870+
2871+
errno = 0;
2872+
2873+
/*
2874+
* strtoul() does *NOT* report an error if the string
2875+
* begins with a minus sign. We do.
2876+
*/
2877+
if (*string == '-') {
2878+
error("invalid %s \"%s\" (not a valid unsigned number)",
2879+
argname, string);
2880+
}
2881+
2882+
val = strtoul(string, &endp, base);
2883+
2884+
/*
2885+
* Did it either not parse any of the string, find extra stuff
2886+
* at the end that the caller isn't interested in, or get
2887+
* another parsing error?
2888+
*/
2889+
if (string == endp || (endpp == NULL && *endp != '\0') ||
2890+
(val == 0 && errno == EINVAL)) {
2891+
error("invalid %s \"%s\" (not a valid unsigned number)",
2892+
argname, string);
2893+
}
2894+
2895+
/*
2896+
* Did it get a value that's out of range?
2897+
*/
2898+
if ((val == ULONG_MAX && errno == ERANGE) ||
2899+
val < minval || val > maxval) {
2900+
error("invalid %s %s (must be >= %u and <= %u)",
2901+
argname, string, minval, maxval);
2902+
}
2903+
2904+
/*
2905+
* OK, it passes all the tests.
2906+
*/
2907+
if (endpp != NULL)
2908+
*endpp = endp;
2909+
return ((u_int)val);
2910+
}
2911+
2912+
static int64_t
2913+
parse_int64(const char *argname, const char *string, char **endpp,
2914+
int64_t minval, int64_t maxval, int base)
2915+
{
2916+
intmax_t val;
2917+
char *endp;
2918+
2919+
errno = 0;
2920+
val = strtoimax(string, &endp, base);
2921+
2922+
/*
2923+
* Did it either not parse any of the string, find extra stuff
2924+
* at the end that the caller isn't interested in, or get
2925+
* another parsing error?
2926+
*/
2927+
if (string == endp || (endpp == NULL && *endp != '\0') ||
2928+
(val == 0 && errno == EINVAL)) {
2929+
error("invalid %s \"%s\" (not a valid number)", argname,
2930+
string);
2931+
}
2932+
2933+
/*
2934+
* Did it get a value that's out of range?
2935+
*/
2936+
if (((val == INTMAX_MAX || val == INTMAX_MIN) && errno == ERANGE) ||
2937+
val < minval || val > maxval) {
2938+
error("invalid %s %s (must be >= %" PRId64 " and <= %" PRId64 ")",
2939+
argname, string, minval, maxval);
2940+
}
2941+
2942+
/*
2943+
* OK, it passes all the tests.
2944+
*/
2945+
if (endpp != NULL)
2946+
*endpp = endp;
2947+
return ((int64_t)val);
2948+
}
2949+
28322950
/*
28332951
* Catch a signal.
28342952
*/

0 commit comments

Comments
 (0)