This is a more powerful version of atoi_warn that can
- check minimum and maximum values
- report error seperately from parsed value
This can be used to simplify a lot of option parsing.
Change-Id: Ibc7526d59c1de17a0f9d8ed88f75c6f070ab11e7
Signed-off-by: Frank Lichtenheld <frank@lichtenheld.com>
Acked-by: Arne Schwabe <arne-openvpn@rfc2549.org>
Message-Id: <20250902144657.11854-1-gert@greenie.muc.de>
URL: https://sourceforge.net/p/openvpn/mailman/message/59228172/
Signed-off-by: Gert Doering <gert@greenie.muc.de>
| ... | ... |
@@ -6411,16 +6411,12 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 6411 | 6411 |
} |
| 6412 | 6412 |
else if (streq(p[0], "management-log-cache") && p[1] && !p[2]) |
| 6413 | 6413 |
{
|
| 6414 |
- int cache; |
|
| 6415 |
- |
|
| 6416 | 6414 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 6417 |
- cache = atoi_warn(p[1], msglevel); |
|
| 6418 |
- if (cache < 1) |
|
| 6415 |
+ if (!atoi_constrained(p[1], &options->management_log_history_cache, |
|
| 6416 |
+ p[0], 1, INT_MAX, msglevel)) |
|
| 6419 | 6417 |
{
|
| 6420 |
- msg(msglevel, "--management-log-cache parameter is out of range"); |
|
| 6421 | 6418 |
goto err; |
| 6422 | 6419 |
} |
| 6423 |
- options->management_log_history_cache = cache; |
|
| 6424 | 6420 |
} |
| 6425 | 6421 |
#endif /* ifdef ENABLE_MANAGEMENT */ |
| 6426 | 6422 |
#ifdef ENABLE_PLUGIN |
| ... | ... |
@@ -6969,16 +6965,11 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 6969 | 6969 |
} |
| 6970 | 6970 |
else if (streq(p[0], "status-version") && p[1] && !p[2]) |
| 6971 | 6971 |
{
|
| 6972 |
- int version; |
|
| 6973 |
- |
|
| 6974 | 6972 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 6975 |
- version = atoi_warn(p[1], msglevel); |
|
| 6976 |
- if (version < 1 || version > 3) |
|
| 6973 |
+ if (!atoi_constrained(p[1], &options->status_file_version, p[0], 1, 3, msglevel)) |
|
| 6977 | 6974 |
{
|
| 6978 |
- msg(msglevel, "--status-version must be 1 to 3"); |
|
| 6979 | 6975 |
goto err; |
| 6980 | 6976 |
} |
| 6981 |
- options->status_file_version = version; |
|
| 6982 | 6977 |
} |
| 6983 | 6978 |
else if (streq(p[0], "remap-usr1") && p[1] && !p[2]) |
| 6984 | 6979 |
{
|
| ... | ... |
@@ -7151,16 +7142,11 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 7151 | 7151 |
} |
| 7152 | 7152 |
else if (streq(p[0], "shaper") && p[1] && !p[2]) |
| 7153 | 7153 |
{
|
| 7154 |
- int shaper; |
|
| 7155 |
- |
|
| 7156 | 7154 |
VERIFY_PERMISSION(OPT_P_SHAPER); |
| 7157 |
- shaper = atoi_warn(p[1], msglevel); |
|
| 7158 |
- if (shaper < SHAPER_MIN || shaper > SHAPER_MAX) |
|
| 7155 |
+ if (!atoi_constrained(p[1], &options->shaper, p[0], SHAPER_MIN, SHAPER_MAX, msglevel)) |
|
| 7159 | 7156 |
{
|
| 7160 |
- msg(msglevel, "Bad shaper value, must be between %d and %d", SHAPER_MIN, SHAPER_MAX); |
|
| 7161 | 7157 |
goto err; |
| 7162 | 7158 |
} |
| 7163 |
- options->shaper = shaper; |
|
| 7164 | 7159 |
} |
| 7165 | 7160 |
else if (streq(p[0], "port") && p[1] && !p[2]) |
| 7166 | 7161 |
{
|
| ... | ... |
@@ -7739,7 +7725,11 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 7739 | 7739 |
else if (streq(p[0], "script-security") && p[1] && !p[2]) |
| 7740 | 7740 |
{
|
| 7741 | 7741 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 7742 |
- script_security_set(atoi_warn(p[1], msglevel)); |
|
| 7742 |
+ int security; |
|
| 7743 |
+ if (atoi_constrained(p[1], &security, p[0], SSEC_NONE, SSEC_PW_ENV, msglevel)) |
|
| 7744 |
+ {
|
|
| 7745 |
+ script_security_set(security); |
|
| 7746 |
+ } |
|
| 7743 | 7747 |
} |
| 7744 | 7748 |
else if (streq(p[0], "mssfix") && !p[3]) |
| 7745 | 7749 |
{
|
| ... | ... |
@@ -7959,11 +7949,9 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 7959 | 7959 |
int real, virtual; |
| 7960 | 7960 |
|
| 7961 | 7961 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 7962 |
- real = atoi_warn(p[1], msglevel); |
|
| 7963 |
- virtual = atoi_warn(p[2], msglevel); |
|
| 7964 |
- if (real < 1 || virtual < 1) |
|
| 7962 |
+ if (!atoi_constrained(p[1], &real, "hash-size real", 1, INT_MAX, msglevel) |
|
| 7963 |
+ || !atoi_constrained(p[2], &virtual, "hash-size virtual", 1, INT_MAX, msglevel)) |
|
| 7965 | 7964 |
{
|
| 7966 |
- msg(msglevel, "--hash-size sizes must be >= 1 (preferably a power of 2)"); |
|
| 7967 | 7965 |
goto err; |
| 7968 | 7966 |
} |
| 7969 | 7967 |
options->real_hash_size = real; |
| ... | ... |
@@ -7974,11 +7962,9 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 7974 | 7974 |
int cf_max, cf_per; |
| 7975 | 7975 |
|
| 7976 | 7976 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 7977 |
- cf_max = atoi_warn(p[1], msglevel); |
|
| 7978 |
- cf_per = atoi_warn(p[2], msglevel); |
|
| 7979 |
- if (cf_max < 0 || cf_per < 0) |
|
| 7977 |
+ if (!atoi_constrained(p[1], &cf_max, "connect-freq n", 1, INT_MAX, msglevel) |
|
| 7978 |
+ || !atoi_constrained(p[2], &cf_per, "connect-freq seconds", 1, INT_MAX, msglevel)) |
|
| 7980 | 7979 |
{
|
| 7981 |
- msg(msglevel, "--connect-freq parms must be > 0"); |
|
| 7982 | 7980 |
goto err; |
| 7983 | 7981 |
} |
| 7984 | 7982 |
options->cf_max = cf_max; |
| ... | ... |
@@ -7986,15 +7972,12 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 7986 | 7986 |
} |
| 7987 | 7987 |
else if (streq(p[0], "connect-freq-initial") && p[1] && p[2] && !p[3]) |
| 7988 | 7988 |
{
|
| 7989 |
- long cf_max, cf_per; |
|
| 7989 |
+ int cf_max, cf_per; |
|
| 7990 | 7990 |
|
| 7991 | 7991 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 7992 |
- char *e1, *e2; |
|
| 7993 |
- cf_max = strtol(p[1], &e1, 10); |
|
| 7994 |
- cf_per = strtol(p[2], &e2, 10); |
|
| 7995 |
- if (cf_max < 0 || cf_per < 0 || *e1 != '\0' || *e2 != '\0') |
|
| 7992 |
+ if (!atoi_constrained(p[1], &cf_max, "connect-freq-initial n", 1, INT_MAX, msglevel) |
|
| 7993 |
+ || !atoi_constrained(p[2], &cf_per, "connect-freq-initial seconds", 1, INT_MAX, msglevel)) |
|
| 7996 | 7994 |
{
|
| 7997 |
- msg(msglevel, "--connect-freq-initial parameters must be integers and >= 0"); |
|
| 7998 | 7995 |
goto err; |
| 7999 | 7996 |
} |
| 8000 | 7997 |
options->cf_initial_max = cf_max; |
| ... | ... |
@@ -8002,21 +7985,11 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 8002 | 8002 |
} |
| 8003 | 8003 |
else if (streq(p[0], "max-clients") && p[1] && !p[2]) |
| 8004 | 8004 |
{
|
| 8005 |
- int max_clients; |
|
| 8006 |
- |
|
| 8007 | 8005 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 8008 |
- max_clients = atoi_warn(p[1], msglevel); |
|
| 8009 |
- if (max_clients < 0) |
|
| 8006 |
+ if (!atoi_constrained(p[1], &options->max_clients, p[0], 1, MAX_PEER_ID, msglevel)) |
|
| 8010 | 8007 |
{
|
| 8011 |
- msg(msglevel, "--max-clients must be at least 1"); |
|
| 8012 | 8008 |
goto err; |
| 8013 | 8009 |
} |
| 8014 |
- if (max_clients >= MAX_PEER_ID) /* max peer-id value */ |
|
| 8015 |
- {
|
|
| 8016 |
- msg(msglevel, "--max-clients must be less than %d", MAX_PEER_ID); |
|
| 8017 |
- goto err; |
|
| 8018 |
- } |
|
| 8019 |
- options->max_clients = max_clients; |
|
| 8020 | 8010 |
} |
| 8021 | 8011 |
else if (streq(p[0], "max-routes-per-client") && p[1] && !p[2]) |
| 8022 | 8012 |
{
|
| ... | ... |
@@ -8188,27 +8161,13 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 8188 | 8188 |
} |
| 8189 | 8189 |
else if (streq(p[0], "bcast-buffers") && p[1] && !p[2]) |
| 8190 | 8190 |
{
|
| 8191 |
- int n_bcast_buf; |
|
| 8192 |
- |
|
| 8193 | 8191 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 8194 |
- n_bcast_buf = atoi_warn(p[1], msglevel); |
|
| 8195 |
- if (n_bcast_buf < 1) |
|
| 8196 |
- {
|
|
| 8197 |
- msg(msglevel, "--bcast-buffers parameter must be > 0"); |
|
| 8198 |
- } |
|
| 8199 |
- options->n_bcast_buf = n_bcast_buf; |
|
| 8192 |
+ atoi_constrained(p[1], &options->n_bcast_buf, p[0], 1, INT_MAX, msglevel); |
|
| 8200 | 8193 |
} |
| 8201 | 8194 |
else if (streq(p[0], "tcp-queue-limit") && p[1] && !p[2]) |
| 8202 | 8195 |
{
|
| 8203 |
- int tcp_queue_limit; |
|
| 8204 |
- |
|
| 8205 | 8196 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 8206 |
- tcp_queue_limit = atoi_warn(p[1], msglevel); |
|
| 8207 |
- if (tcp_queue_limit < 1) |
|
| 8208 |
- {
|
|
| 8209 |
- msg(msglevel, "--tcp-queue-limit parameter must be > 0"); |
|
| 8210 |
- } |
|
| 8211 |
- options->tcp_queue_limit = tcp_queue_limit; |
|
| 8197 |
+ atoi_constrained(p[1], &options->tcp_queue_limit, p[0], 1, INT_MAX, msglevel); |
|
| 8212 | 8198 |
} |
| 8213 | 8199 |
#if PORT_SHARE |
| 8214 | 8200 |
else if (streq(p[0], "port-share") && p[1] && p[2] && !p[4]) |
| ... | ... |
@@ -8354,21 +8313,24 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 8354 | 8354 |
int ageing_time, check_interval; |
| 8355 | 8355 |
|
| 8356 | 8356 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 8357 |
- ageing_time = atoi_warn(p[1], msglevel); |
|
| 8357 |
+ if (!atoi_constrained(p[1], &ageing_time, "stale-routes-check age", 1, INT_MAX, msglevel)) |
|
| 8358 |
+ {
|
|
| 8359 |
+ goto err; |
|
| 8360 |
+ } |
|
| 8361 |
+ |
|
| 8358 | 8362 |
if (p[2]) |
| 8359 | 8363 |
{
|
| 8360 |
- check_interval = atoi_warn(p[2], msglevel); |
|
| 8364 |
+ if (!atoi_constrained(p[2], &check_interval, |
|
| 8365 |
+ "stale-routes-check interval", 1, INT_MAX, msglevel)) |
|
| 8366 |
+ {
|
|
| 8367 |
+ goto err; |
|
| 8368 |
+ } |
|
| 8361 | 8369 |
} |
| 8362 | 8370 |
else |
| 8363 | 8371 |
{
|
| 8364 | 8372 |
check_interval = ageing_time; |
| 8365 | 8373 |
} |
| 8366 | 8374 |
|
| 8367 |
- if (ageing_time < 1 || check_interval < 1) |
|
| 8368 |
- {
|
|
| 8369 |
- msg(msglevel, "--stale-routes-check aging time and check interval must be >= 1"); |
|
| 8370 |
- goto err; |
|
| 8371 |
- } |
|
| 8372 | 8375 |
options->stale_routes_ageing_time = ageing_time; |
| 8373 | 8376 |
options->stale_routes_check_interval = check_interval; |
| 8374 | 8377 |
} |
| ... | ... |
@@ -8386,7 +8348,7 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 8386 | 8386 |
else if (streq(p[0], "push-continuation") && p[1] && !p[2]) |
| 8387 | 8387 |
{
|
| 8388 | 8388 |
VERIFY_PERMISSION(OPT_P_PULL_MODE); |
| 8389 |
- options->push_continuation = atoi_warn(p[1], msglevel); |
|
| 8389 |
+ atoi_constrained(p[1], &options->push_continuation, p[0], 0, 2, msglevel); |
|
| 8390 | 8390 |
} |
| 8391 | 8391 |
else if (streq(p[0], "auth-user-pass") && !p[2]) |
| 8392 | 8392 |
{
|
| ... | ... |
@@ -8505,33 +8467,23 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 8505 | 8505 |
{
|
| 8506 | 8506 |
if (!streq(p[2], "default")) |
| 8507 | 8507 |
{
|
| 8508 |
- int offset = atoi_warn(p[2], msglevel); |
|
| 8508 |
+ int offset; |
|
| 8509 | 8509 |
|
| 8510 |
- if (!(offset > -256 && offset < 256)) |
|
| 8510 |
+ if (!atoi_constrained(p[2], &offset, "ip-win32 offset", -256, 256, msglevel)) |
|
| 8511 | 8511 |
{
|
| 8512 |
- msg(msglevel, |
|
| 8513 |
- "--ip-win32 dynamic [offset] [lease-time]: offset (%d) must be > -256 and < 256", |
|
| 8514 |
- offset); |
|
| 8515 | 8512 |
goto err; |
| 8516 | 8513 |
} |
| 8517 |
- |
|
| 8518 | 8514 |
to->dhcp_masq_custom_offset = true; |
| 8519 | 8515 |
to->dhcp_masq_offset = offset; |
| 8520 | 8516 |
} |
| 8521 | 8517 |
|
| 8522 | 8518 |
if (p[3]) |
| 8523 | 8519 |
{
|
| 8524 |
- const int min_lease = 30; |
|
| 8525 |
- int lease_time; |
|
| 8526 |
- lease_time = atoi_warn(p[3], msglevel); |
|
| 8527 |
- if (lease_time < min_lease) |
|
| 8520 |
+ if (!atoi_constrained(p[3], &to->dhcp_lease_time, |
|
| 8521 |
+ "ip-win32 lease time", 30, INT_MAX, msglevel)) |
|
| 8528 | 8522 |
{
|
| 8529 |
- msg(msglevel, |
|
| 8530 |
- "--ip-win32 dynamic [offset] [lease-time]: lease time parameter (%d) must be at least %d seconds", |
|
| 8531 |
- lease_time, min_lease); |
|
| 8532 | 8523 |
goto err; |
| 8533 | 8524 |
} |
| 8534 |
- to->dhcp_lease_time = lease_time; |
|
| 8535 | 8525 |
} |
| 8536 | 8526 |
} |
| 8537 | 8527 |
} |
| ... | ... |
@@ -8629,8 +8581,7 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 8629 | 8629 |
} |
| 8630 | 8630 |
else if (streq(p[1], "NBT") && p[2] && !p[3]) |
| 8631 | 8631 |
{
|
| 8632 |
- int t; |
|
| 8633 |
- t = atoi_warn(p[2], msglevel); |
|
| 8632 |
+ int t = atoi_warn(p[2], msglevel); |
|
| 8634 | 8633 |
if (!(t == 1 || t == 2 || t == 4 || t == 8)) |
| 8635 | 8634 |
{
|
| 8636 | 8635 |
msg(msglevel, "--dhcp-option NBT: parameter (%d) must be 1, 2, 4, or 8", t); |
| ... | ... |
@@ -8704,15 +8655,11 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 8704 | 8704 |
} |
| 8705 | 8705 |
else if (streq(p[0], "tap-sleep") && p[1] && !p[2]) |
| 8706 | 8706 |
{
|
| 8707 |
- int s; |
|
| 8708 | 8707 |
VERIFY_PERMISSION(OPT_P_DHCPDNS); |
| 8709 |
- s = atoi_warn(p[1], msglevel); |
|
| 8710 |
- if (s < 0 || s >= 256) |
|
| 8708 |
+ if (!atoi_constrained(p[1], &options->tuntap_options.tap_sleep, p[0], 0, 255, msglevel)) |
|
| 8711 | 8709 |
{
|
| 8712 |
- msg(msglevel, "--tap-sleep parameter must be between 0 and 255"); |
|
| 8713 | 8710 |
goto err; |
| 8714 | 8711 |
} |
| 8715 |
- options->tuntap_options.tap_sleep = s; |
|
| 8716 | 8712 |
} |
| 8717 | 8713 |
else if (streq(p[0], "dhcp-renew") && !p[1]) |
| 8718 | 8714 |
{
|
| ... | ... |
@@ -9152,30 +9099,19 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 9152 | 9152 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 9153 | 9153 |
if (p[1]) |
| 9154 | 9154 |
{
|
| 9155 |
- int replay_window; |
|
| 9156 |
- |
|
| 9157 |
- replay_window = atoi_warn(p[1], msglevel); |
|
| 9158 |
- if (!(MIN_SEQ_BACKTRACK <= replay_window && replay_window <= MAX_SEQ_BACKTRACK)) |
|
| 9155 |
+ if (!atoi_constrained(p[1], &options->replay_window, "replay-window windows size", |
|
| 9156 |
+ MIN_SEQ_BACKTRACK, MAX_SEQ_BACKTRACK, msglevel)) |
|
| 9159 | 9157 |
{
|
| 9160 |
- msg(msglevel, "replay-window window size parameter (%d) must be between %d and %d", |
|
| 9161 |
- replay_window, MIN_SEQ_BACKTRACK, MAX_SEQ_BACKTRACK); |
|
| 9162 | 9158 |
goto err; |
| 9163 | 9159 |
} |
| 9164 |
- options->replay_window = replay_window; |
|
| 9165 | 9160 |
|
| 9166 | 9161 |
if (p[2]) |
| 9167 | 9162 |
{
|
| 9168 |
- int replay_time; |
|
| 9169 |
- |
|
| 9170 |
- replay_time = atoi_warn(p[2], msglevel); |
|
| 9171 |
- if (!(MIN_TIME_BACKTRACK <= replay_time && replay_time <= MAX_TIME_BACKTRACK)) |
|
| 9163 |
+ if (!atoi_constrained(p[2], &options->replay_time, "replay-window time window", |
|
| 9164 |
+ MIN_TIME_BACKTRACK, MAX_TIME_BACKTRACK, msglevel)) |
|
| 9172 | 9165 |
{
|
| 9173 |
- msg(msglevel, |
|
| 9174 |
- "replay-window time window parameter (%d) must be between %d and %d", |
|
| 9175 |
- replay_time, MIN_TIME_BACKTRACK, MAX_TIME_BACKTRACK); |
|
| 9176 | 9166 |
goto err; |
| 9177 | 9167 |
} |
| 9178 |
- options->replay_time = replay_time; |
|
| 9179 | 9168 |
} |
| 9180 | 9169 |
} |
| 9181 | 9170 |
else |
| ... | ... |
@@ -9771,7 +9707,7 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 9771 | 9771 |
else if (!p[2]) |
| 9772 | 9772 |
{
|
| 9773 | 9773 |
char *endp = NULL; |
| 9774 |
- int i = strtol(provider, &endp, 10); |
|
| 9774 |
+ long i = strtol(provider, &endp, 10); |
|
| 9775 | 9775 |
|
| 9776 | 9776 |
if (*endp == 0) |
| 9777 | 9777 |
{
|
| ... | ... |
@@ -9842,7 +9778,7 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, |
| 9842 | 9842 |
else if (streq(p[0], "pkcs11-pin-cache") && p[1] && !p[2]) |
| 9843 | 9843 |
{
|
| 9844 | 9844 |
VERIFY_PERMISSION(OPT_P_GENERAL); |
| 9845 |
- options->pkcs11_pin_cache_period = atoi_warn(p[1], msglevel); |
|
| 9845 |
+ options->pkcs11_pin_cache_period = positive_atoi(p[1], msglevel); |
|
| 9846 | 9846 |
} |
| 9847 | 9847 |
else if (streq(p[0], "pkcs11-id") && p[1] && !p[2]) |
| 9848 | 9848 |
{
|
| ... | ... |
@@ -146,6 +146,37 @@ atoi_warn(const char *str, int msglevel) |
| 146 | 146 |
return (int)i; |
| 147 | 147 |
} |
| 148 | 148 |
|
| 149 |
+bool |
|
| 150 |
+atoi_constrained(const char *str, int *value, const char *name, int min, int max, int msglevel) |
|
| 151 |
+{
|
|
| 152 |
+ ASSERT(min < max); |
|
| 153 |
+ |
|
| 154 |
+ char *endptr; |
|
| 155 |
+ long long i = strtoll(str, &endptr, 10); |
|
| 156 |
+ if (i < INT_MIN || *endptr != '\0' || i > INT_MAX) |
|
| 157 |
+ {
|
|
| 158 |
+ msg(msglevel, "%s: Cannot parse '%s' as integer", name, str); |
|
| 159 |
+ return false; |
|
| 160 |
+ } |
|
| 161 |
+ if (i < min || i > max) |
|
| 162 |
+ {
|
|
| 163 |
+ if (max == INT_MAX) /* nicer message for common case */ |
|
| 164 |
+ {
|
|
| 165 |
+ msg(msglevel, "%s: Must be an integer >= %d, not %lld", |
|
| 166 |
+ name, min, i); |
|
| 167 |
+ } |
|
| 168 |
+ else |
|
| 169 |
+ {
|
|
| 170 |
+ msg(msglevel, "%s: Must be an integer between %d and %d, not %lld", |
|
| 171 |
+ name, min, max, i); |
|
| 172 |
+ } |
|
| 173 |
+ return false; |
|
| 174 |
+ } |
|
| 175 |
+ |
|
| 176 |
+ *value = i; |
|
| 177 |
+ return true; |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 149 | 180 |
static const char *updatable_options[] = { "block-ipv6", "block-outside-dns",
|
| 150 | 181 |
"dhcp-option", "dns", |
| 151 | 182 |
"ifconfig", "ifconfig-ipv6", |
| ... | ... |
@@ -41,11 +41,23 @@ int positive_atoi(const char *str, int msglevel); |
| 41 | 41 |
|
| 42 | 42 |
/** |
| 43 | 43 |
* Converts a str to an integer if the string can be represented as an |
| 44 |
- * integer number. Otherwise print a warning with msglevel and return 0 |
|
| 44 |
+ * integer number. Otherwise print a warning with \p msglevel and return 0 |
|
| 45 | 45 |
*/ |
| 46 | 46 |
int atoi_warn(const char *str, int msglevel); |
| 47 | 47 |
|
| 48 | 48 |
/** |
| 49 |
+ * Converts a str to an integer if the string can be represented as an |
|
| 50 |
+ * integer number and is between \p min and \p max. |
|
| 51 |
+ * The integer is stored in \p value. |
|
| 52 |
+ * On error, print a warning with \p msglevel using \p name. \p value is |
|
| 53 |
+ * not changed on error. |
|
| 54 |
+ * |
|
| 55 |
+ * @return \c true if the integer has been parsed and stored in value, \c false otherwise |
|
| 56 |
+ */ |
|
| 57 |
+bool atoi_constrained(const char *str, int *value, const char *name, int min, int max, |
|
| 58 |
+ int msglevel); |
|
| 59 |
+ |
|
| 60 |
+/** |
|
| 49 | 61 |
* Filter an option line by all pull filters. |
| 50 | 62 |
* |
| 51 | 63 |
* If a match is found, the line is modified depending on |
| ... | ... |
@@ -351,6 +351,14 @@ test_atoi_variants(void **state) |
| 351 | 351 |
assert_int_equal(atoi_warn("0", msglevel), 0);
|
| 352 | 352 |
assert_int_equal(atoi_warn("-1194", msglevel), -1194);
|
| 353 | 353 |
|
| 354 |
+ int parameter = 0; |
|
| 355 |
+ assert_true(atoi_constrained("1234", ¶meter, "test", 0, INT_MAX, msglevel));
|
|
| 356 |
+ assert_int_equal(parameter, 1234); |
|
| 357 |
+ assert_true(atoi_constrained("0", ¶meter, "test", -1, 0, msglevel));
|
|
| 358 |
+ assert_int_equal(parameter, 0); |
|
| 359 |
+ assert_true(atoi_constrained("-1194", ¶meter, "test", INT_MIN, INT_MAX, msglevel));
|
|
| 360 |
+ assert_int_equal(parameter, -1194); |
|
| 361 |
+ |
|
| 354 | 362 |
CLEAR(mock_msg_buf); |
| 355 | 363 |
assert_int_equal(positive_atoi("-1234", msglevel), 0);
|
| 356 | 364 |
assert_string_equal(mock_msg_buf, "Cannot parse argument '-1234' as non-negative integer"); |
| ... | ... |
@@ -365,6 +373,12 @@ test_atoi_variants(void **state) |
| 365 | 365 |
assert_string_equal(mock_msg_buf, "Cannot parse argument '2147483653' as integer"); |
| 366 | 366 |
|
| 367 | 367 |
CLEAR(mock_msg_buf); |
| 368 |
+ parameter = -42; |
|
| 369 |
+ assert_false(atoi_constrained("2147483653", ¶meter, "test", 0, INT_MAX, msglevel));
|
|
| 370 |
+ assert_string_equal(mock_msg_buf, "test: Cannot parse '2147483653' as integer"); |
|
| 371 |
+ assert_int_equal(parameter, -42); |
|
| 372 |
+ |
|
| 373 |
+ CLEAR(mock_msg_buf); |
|
| 368 | 374 |
assert_int_equal(positive_atoi("foo77", msglevel), 0);
|
| 369 | 375 |
assert_string_equal(mock_msg_buf, "Cannot parse argument 'foo77' as non-negative integer"); |
| 370 | 376 |
|
| ... | ... |
@@ -373,6 +387,18 @@ test_atoi_variants(void **state) |
| 373 | 373 |
assert_string_equal(mock_msg_buf, "Cannot parse argument '77foo' as non-negative integer"); |
| 374 | 374 |
|
| 375 | 375 |
CLEAR(mock_msg_buf); |
| 376 |
+ parameter = -42; |
|
| 377 |
+ assert_false(atoi_constrained("foo77", ¶meter, "test", 0, INT_MAX, msglevel));
|
|
| 378 |
+ assert_string_equal(mock_msg_buf, "test: Cannot parse 'foo77' as integer"); |
|
| 379 |
+ assert_int_equal(parameter, -42); |
|
| 380 |
+ |
|
| 381 |
+ CLEAR(mock_msg_buf); |
|
| 382 |
+ parameter = -42; |
|
| 383 |
+ assert_false(atoi_constrained("77foo", ¶meter, "test", 0, INT_MAX, msglevel));
|
|
| 384 |
+ assert_string_equal(mock_msg_buf, "test: Cannot parse '77foo' as integer"); |
|
| 385 |
+ assert_int_equal(parameter, -42); |
|
| 386 |
+ |
|
| 387 |
+ CLEAR(mock_msg_buf); |
|
| 376 | 388 |
assert_int_equal(atoi_warn("foo77", msglevel), 0);
|
| 377 | 389 |
assert_string_equal(mock_msg_buf, "Cannot parse argument 'foo77' as integer"); |
| 378 | 390 |
|
| ... | ... |
@@ -380,6 +406,31 @@ test_atoi_variants(void **state) |
| 380 | 380 |
assert_int_equal(atoi_warn("77foo", msglevel), 0);
|
| 381 | 381 |
assert_string_equal(mock_msg_buf, "Cannot parse argument '77foo' as integer"); |
| 382 | 382 |
|
| 383 |
+ /* special tests for _constrained */ |
|
| 384 |
+ CLEAR(mock_msg_buf); |
|
| 385 |
+ parameter = -42; |
|
| 386 |
+ assert_false(atoi_constrained("77", ¶meter, "test", 0, 76, msglevel));
|
|
| 387 |
+ assert_string_equal(mock_msg_buf, "test: Must be an integer between 0 and 76, not 77"); |
|
| 388 |
+ assert_int_equal(parameter, -42); |
|
| 389 |
+ |
|
| 390 |
+ CLEAR(mock_msg_buf); |
|
| 391 |
+ parameter = -42; |
|
| 392 |
+ assert_false(atoi_constrained("-77", ¶meter, "test", -76, 76, msglevel));
|
|
| 393 |
+ assert_string_equal(mock_msg_buf, "test: Must be an integer between -76 and 76, not -77"); |
|
| 394 |
+ assert_int_equal(parameter, -42); |
|
| 395 |
+ |
|
| 396 |
+ CLEAR(mock_msg_buf); |
|
| 397 |
+ parameter = -42; |
|
| 398 |
+ assert_false(atoi_constrained("-77", ¶meter, "test", 0, INT_MAX, msglevel));
|
|
| 399 |
+ assert_string_equal(mock_msg_buf, "test: Must be an integer >= 0, not -77"); |
|
| 400 |
+ assert_int_equal(parameter, -42); |
|
| 401 |
+ |
|
| 402 |
+ CLEAR(mock_msg_buf); |
|
| 403 |
+ parameter = -42; |
|
| 404 |
+ assert_false(atoi_constrained("0", ¶meter, "test", 1, INT_MAX, msglevel));
|
|
| 405 |
+ assert_string_equal(mock_msg_buf, "test: Must be an integer >= 1, not 0"); |
|
| 406 |
+ assert_int_equal(parameter, -42); |
|
| 407 |
+ |
|
| 383 | 408 |
mock_set_debug_level(saved_log_level); |
| 384 | 409 |
} |
| 385 | 410 |
|