strtoofft: reduce integer overflow risks globally

... make sure we bail out on overflows.

Reported-by: Brian Carpenter
Closes #1758
This commit is contained in:
Daniel Stenberg 2017-08-14 23:33:23 +02:00
parent b53b4e4424
commit ff50fe0348
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
13 changed files with 196 additions and 131 deletions

View File

@ -593,15 +593,21 @@ Curl_cookie_add(struct Curl_easy *data,
} while(semiptr); } while(semiptr);
if(co->maxage) { if(co->maxage) {
co->expires = CURLofft offt;
curlx_strtoofft((*co->maxage=='\"')? offt = curlx_strtoofft((*co->maxage=='\"')?
&co->maxage[1]:&co->maxage[0], NULL, 10); &co->maxage[1]:&co->maxage[0], NULL, 10,
&co->expires);
if(offt == CURL_OFFT_FLOW)
/* overflow, used max value */
co->expires = CURL_OFF_T_MAX;
else if(!offt) {
if(CURL_OFF_T_MAX - now < co->expires) if(CURL_OFF_T_MAX - now < co->expires)
/* avoid overflow */ /* would overflow */
co->expires = CURL_OFF_T_MAX; co->expires = CURL_OFF_T_MAX;
else else
co->expires += now; co->expires += now;
} }
}
else if(co->expirestr) { else if(co->expirestr) {
/* Note that if the date couldn't get parsed for whatever reason, /* Note that if the date couldn't get parsed for whatever reason,
the cookie will be treated as a session cookie */ the cookie will be treated as a session cookie */
@ -753,7 +759,8 @@ Curl_cookie_add(struct Curl_easy *data,
co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE; co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
break; break;
case 4: case 4:
co->expires = curlx_strtoofft(ptr, NULL, 10); if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
badcookie = TRUE;
break; break;
case 5: case 5:
co->name = strdup(ptr); co->name = strdup(ptr);

View File

@ -139,26 +139,28 @@ static CURLcode file_range(struct connectdata *conn)
struct Curl_easy *data = conn->data; struct Curl_easy *data = conn->data;
if(data->state.use_range && data->state.range) { if(data->state.use_range && data->state.range) {
from=curlx_strtoofft(data->state.range, &ptr, 0); CURLofft from_t;
CURLofft to_t;
from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from);
if(from_t == CURL_OFFT_FLOW)
return CURLE_RANGE_ERROR;
while(*ptr && (ISSPACE(*ptr) || (*ptr=='-'))) while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
ptr++; ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0); to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
if(ptr == ptr2) { if(to_t == CURL_OFFT_FLOW)
/* we didn't get any digit */ return CURLE_RANGE_ERROR;
to=-1; if((to_t == CURL_OFFT_INVAL) && !from_t) {
}
if((-1 == to) && (from>=0)) {
/* X - */ /* X - */
data->state.resume_from = from; data->state.resume_from = from;
DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n", DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
from)); from));
} }
else if(from < 0) { else if((from_t == CURL_OFFT_INVAL) && !to_t) {
/* -Y */ /* -Y */
data->req.maxdownload = -from; data->req.maxdownload = to;
data->state.resume_from = from; data->state.resume_from = -to;
DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n", DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
-from)); to));
} }
else { else {
/* X-Y */ /* X-Y */

View File

@ -2260,11 +2260,13 @@ static CURLcode ftp_state_size_resp(struct connectdata *conn,
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
struct Curl_easy *data=conn->data; struct Curl_easy *data=conn->data;
curl_off_t filesize; curl_off_t filesize = -1;
char *buf = data->state.buffer; char *buf = data->state.buffer;
/* get the size from the ascii string: */ /* get the size from the ascii string: */
filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1; if(ftpcode == 213)
/* ignores parsing errors, which will make the size remain unknown */
(void)curlx_strtoofft(buf+4, NULL, 0, &filesize);
if(instate == FTP_SIZE) { if(instate == FTP_SIZE) {
#ifdef CURL_FTP_HTTPSTYLE_HEAD #ifdef CURL_FTP_HTTPSTYLE_HEAD
@ -2435,7 +2437,7 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn,
/* if we have nothing but digits: */ /* if we have nothing but digits: */
if(bytes++) { if(bytes++) {
/* get the number! */ /* get the number! */
size = curlx_strtoofft(bytes, NULL, 0); (void)curlx_strtoofft(bytes, NULL, 0, &size);
} }
} }
} }
@ -3466,31 +3468,32 @@ static CURLcode ftp_range(struct connectdata *conn)
{ {
curl_off_t from, to; curl_off_t from, to;
char *ptr; char *ptr;
char *ptr2;
struct Curl_easy *data = conn->data; struct Curl_easy *data = conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc; struct ftp_conn *ftpc = &conn->proto.ftpc;
if(data->state.use_range && data->state.range) { if(data->state.use_range && data->state.range) {
from=curlx_strtoofft(data->state.range, &ptr, 0); CURLofft from_t;
CURLofft to_t;
from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from);
if(from_t == CURL_OFFT_FLOW)
return CURLE_RANGE_ERROR;
while(*ptr && (ISSPACE(*ptr) || (*ptr=='-'))) while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
ptr++; ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0); to_t = curlx_strtoofft(ptr, NULL, 0, &to);
if(ptr == ptr2) { if(to_t == CURL_OFFT_FLOW)
/* we didn't get any digit */ return CURLE_RANGE_ERROR;
to=-1; if((to_t == CURL_OFFT_INVAL) && !from_t) {
}
if((-1 == to) && (from>=0)) {
/* X - */ /* X - */
data->state.resume_from = from; data->state.resume_from = from;
DEBUGF(infof(conn->data, "FTP RANGE %" CURL_FORMAT_CURL_OFF_T DEBUGF(infof(conn->data, "FTP RANGE %" CURL_FORMAT_CURL_OFF_T
" to end of file\n", from)); " to end of file\n", from));
} }
else if(from < 0) { else if(!to_t && (from_t == CURL_OFFT_INVAL)) {
/* -Y */ /* -Y */
data->req.maxdownload = -from; data->req.maxdownload = to;
data->state.resume_from = from; data->state.resume_from = -to;
DEBUGF(infof(conn->data, "FTP RANGE the last %" CURL_FORMAT_CURL_OFF_T DEBUGF(infof(conn->data, "FTP RANGE the last %" CURL_FORMAT_CURL_OFF_T
" bytes\n", -from)); " bytes\n", to));
} }
else { else {
/* X-Y */ /* X-Y */

View File

@ -609,7 +609,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
char *p; char *p;
curl_off_t fsize; curl_off_t fsize;
finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
fsize = curlx_strtoofft(finfo->b_data+parser->item_offset, &p, 10); if(!curlx_strtoofft(finfo->b_data+parser->item_offset,
&p, 10, &fsize)) {
if(p[0] == '\0' && fsize != CURL_OFF_T_MAX && if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
fsize != CURL_OFF_T_MIN) { fsize != CURL_OFF_T_MIN) {
parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
@ -620,6 +621,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
parser->state.UNIX.main = PL_UNIX_TIME; parser->state.UNIX.main = PL_UNIX_TIME;
parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
} }
}
else if(!ISDIGIT(c)) { else if(!ISDIGIT(c)) {
PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
return bufflen; return bufflen;
@ -935,19 +937,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
} }
else { else {
char *endptr; char *endptr;
finfo->size = curlx_strtoofft(finfo->b_data + if(curlx_strtoofft(finfo->b_data +
parser->item_offset, parser->item_offset,
&endptr, 10); &endptr, 10, &finfo->size)) {
if(!*endptr) {
if(finfo->size == CURL_OFF_T_MAX ||
finfo->size == CURL_OFF_T_MIN) {
if(errno == ERANGE) {
PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
return bufflen;
}
}
}
else {
PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
return bufflen; return bufflen;
} }

View File

@ -3486,7 +3486,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
/* Check for Content-Length: header lines to get size */ /* Check for Content-Length: header lines to get size */
if(!k->ignorecl && !data->set.ignorecl && if(!k->ignorecl && !data->set.ignorecl &&
checkprefix("Content-Length:", k->p)) { checkprefix("Content-Length:", k->p)) {
curl_off_t contentlength = curlx_strtoofft(k->p+15, NULL, 10); curl_off_t contentlength;
if(!curlx_strtoofft(k->p+15, NULL, 10, &contentlength)) {
if(data->set.max_filesize && if(data->set.max_filesize &&
contentlength > data->set.max_filesize) { contentlength > data->set.max_filesize) {
failf(data, "Maximum file size exceeded"); failf(data, "Maximum file size exceeded");
@ -3509,6 +3510,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
", closing after transfer\n", contentlength); ", closing after transfer\n", contentlength);
} }
} }
else
infof(data, "Illegal Content-Length: header\n");
}
/* check for Content-Type: header lines to get the MIME-type */ /* check for Content-Type: header lines to get the MIME-type */
else if(checkprefix("Content-Type:", k->p)) { else if(checkprefix("Content-Type:", k->p)) {
char *contenttype = Curl_copy_header_value(k->p); char *contenttype = Curl_copy_header_value(k->p);
@ -3682,12 +3686,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
/* if it truly stopped on a digit */ /* if it truly stopped on a digit */
if(ISDIGIT(*ptr)) { if(ISDIGIT(*ptr)) {
k->offset = curlx_strtoofft(ptr, NULL, 10); if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) {
if(data->state.resume_from == k->offset) if(data->state.resume_from == k->offset)
/* we asked for a resume and we got it */ /* we asked for a resume and we got it */
k->content_range = TRUE; k->content_range = TRUE;
} }
}
else else
data->state.resume_from = 0; /* get everything */ data->state.resume_from = 0; /* get everything */
} }

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -158,9 +158,7 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
return CHUNKE_ILLEGAL_HEX; return CHUNKE_ILLEGAL_HEX;
} }
ch->datasize=curlx_strtoofft(ch->hexbuffer, &endptr, 16); if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
if((ch->datasize == CURL_OFF_T_MAX) && (errno == ERANGE))
/* overflow is an error */
return CHUNKE_ILLEGAL_HEX; return CHUNKE_ILLEGAL_HEX;
ch->state = CHUNK_LF; /* now wait for the CRLF */ ch->state = CHUNK_LF; /* now wait for the CRLF */
} }

View File

@ -523,8 +523,8 @@ static CURLcode CONNECT(struct connectdata *conn,
k->httpcode); k->httpcode);
} }
else { else {
s->cl = curlx_strtoofft(s->line_start + (void)curlx_strtoofft(s->line_start +
strlen("Content-Length:"), NULL, 10); strlen("Content-Length:"), NULL, 10, &s->cl);
} }
} }
else if(Curl_compareheader(s->line_start, "Connection:", "close")) else if(Curl_compareheader(s->line_start, "Connection:", "close"))

View File

@ -1070,11 +1070,12 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
if(*ptr == '{') { if(*ptr == '{') {
char *endptr; char *endptr;
size = curlx_strtoofft(ptr + 1, &endptr, 10); if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
if(endptr - ptr > 1 && endptr[0] == '}' && if(endptr - ptr > 1 && endptr[0] == '}' &&
endptr[1] == '\r' && endptr[2] == '\0') endptr[1] == '\r' && endptr[2] == '\0')
parsed = TRUE; parsed = TRUE;
} }
}
if(parsed) { if(parsed) {
infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n", infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",

View File

@ -2233,18 +2233,25 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
curl_off_t from, to; curl_off_t from, to;
char *ptr; char *ptr;
char *ptr2; char *ptr2;
CURLofft to_t;
CURLofft from_t;
from=curlx_strtoofft(conn->data->state.range, &ptr, 0); from_t = curlx_strtoofft(conn->data->state.range, &ptr, 0, &from);
if(from_t == CURL_OFFT_FLOW)
return CURLE_RANGE_ERROR;
while(*ptr && (ISSPACE(*ptr) || (*ptr=='-'))) while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
ptr++; ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0); to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
if((ptr == ptr2) /* no "to" value given */ if(to_t == CURL_OFFT_FLOW)
return CURLE_RANGE_ERROR;
if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
|| (to >= size)) { || (to >= size)) {
to = size - 1; to = size - 1;
} }
if(from < 0) { if(from_t) {
/* from is relative to end of file */ /* from is relative to end of file */
from += size; from = size - to;
to = size - 1;
} }
if(from > size) { if(from > size) {
failf(data, "Offset (%" failf(data, "Offset (%"

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -20,6 +20,7 @@
* *
***************************************************************************/ ***************************************************************************/
#include <errno.h>
#include "curl_setup.h" #include "curl_setup.h"
#include "strtoofft.h" #include "strtoofft.h"
@ -32,6 +33,30 @@
* https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
*/ */
#if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
# ifdef HAVE_STRTOLL
# define strtooff strtoll
# else
# if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64)
# if defined(_SAL_VERSION)
_Check_return_ _CRTIMP __int64 __cdecl _strtoi64(
_In_z_ const char *_String,
_Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix);
# else
_CRTIMP __int64 __cdecl _strtoi64(const char *_String,
char **_EndPtr, int _Radix);
# endif
# define strtooff _strtoi64
# else
curl_off_t curlx_strtoll(const char *nptr, char **endptr, int base);
# define strtooff curlx_strtoll
# define NEED_CURL_STRTOLL 1
# endif
# endif
#else
# define strtooff strtol
#endif
#ifdef NEED_CURL_STRTOLL #ifdef NEED_CURL_STRTOLL
/* Range tests can be used for alphanum decoding if characters are consecutive, /* Range tests can be used for alphanum decoding if characters are consecutive,
@ -48,11 +73,10 @@ static const char valchars[] =
static int get_char(char c, int base); static int get_char(char c, int base);
/** /**
* Emulated version of the strtoll function. This extracts a long long * Custom version of the strtooff function. This extracts a curl_off_t
* value from the given input string and returns it. * value from the given input string and returns it.
*/ */
curl_off_t static curl_off_t strtooff(const char *nptr, char **endptr, int base)
curlx_strtoll(const char *nptr, char **endptr, int base)
{ {
char *end; char *end;
int is_negative = 0; int is_negative = 0;
@ -186,3 +210,34 @@ static int get_char(char c, int base)
return value; return value;
} }
#endif /* Only present if we need strtoll, but don't have it. */ #endif /* Only present if we need strtoll, but don't have it. */
/*
* Parse a *positive* up to 64 bit number written in ascii.
*/
CURLofft curlx_strtoofft(const char *str, char **endp, int base,
curl_off_t *num)
{
char *end;
curl_off_t number;
errno = 0;
*num = 0; /* clear by default */
while(str && *str && ISSPACE(*str))
str++;
if('-' == *str) {
if(endp)
*endp = (char *)str; /* didn't actually move */
return CURL_OFFT_INVAL; /* nothing parsed */
}
number = strtooff(str, &end, base);
if(endp)
*endp = end;
if(errno == ERANGE)
/* overflow/underflow */
return CURL_OFFT_FLOW;
else if(str == end)
/* nothing parsed */
return CURL_OFFT_INVAL;
*num = number;
return CURL_OFFT_OK;
}

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -40,30 +40,6 @@
* of 'long' the conversion function to use is strtol(). * of 'long' the conversion function to use is strtol().
*/ */
#if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
# ifdef HAVE_STRTOLL
# define curlx_strtoofft strtoll
# else
# if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64)
# if defined(_SAL_VERSION)
_Check_return_ _CRTIMP __int64 __cdecl _strtoi64(
_In_z_ const char *_String,
_Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix);
# else
_CRTIMP __int64 __cdecl _strtoi64(const char *_String,
char **_EndPtr, int _Radix);
# endif
# define curlx_strtoofft _strtoi64
# else
curl_off_t curlx_strtoll(const char *nptr, char **endptr, int base);
# define curlx_strtoofft curlx_strtoll
# define NEED_CURL_STRTOLL 1
# endif
# endif
#else
# define curlx_strtoofft strtol
#endif
#if (CURL_SIZEOF_CURL_OFF_T == 4) #if (CURL_SIZEOF_CURL_OFF_T == 4)
# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF)
#else #else
@ -72,4 +48,13 @@
#endif #endif
#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1)) #define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1))
typedef enum {
CURL_OFFT_OK, /* parsed fine */
CURL_OFFT_FLOW, /* over or underflow */
CURL_OFFT_INVAL /* nothing was parsed */
} CURLofft;
CURLofft curlx_strtoofft(const char *str, char **endp, int base,
curl_off_t *num);
#endif /* HEADER_CURL_STRTOOFFT_H */ #endif /* HEADER_CURL_STRTOOFFT_H */

View File

@ -590,7 +590,11 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
{ {
/* We support G, M, K too */ /* We support G, M, K too */
char *unit; char *unit;
curl_off_t value = curlx_strtoofft(nextarg, &unit, 0); curl_off_t value;
if(curlx_strtoofft(nextarg, &unit, 0, &value)) {
warnf(global, "unsupported rate\n");
return PARAM_BAD_USE;
}
if(!*unit) if(!*unit)
unit = (char *)"b"; unit = (char *)"b";
@ -1843,10 +1847,13 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) { if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
char buffer[32]; char buffer[32];
curl_off_t off; curl_off_t off;
if(curlx_strtoofft(nextarg, NULL, 10, &off)) {
warnf(global, "unsupported range point\n");
return PARAM_BAD_USE;
}
warnf(global, warnf(global,
"A specified range MUST include at least one dash (-). " "A specified range MUST include at least one dash (-). "
"Appending one for you!\n"); "Appending one for you!\n");
off = curlx_strtoofft(nextarg, NULL, 10);
snprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off); snprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off);
Curl_safefree(config->range); Curl_safefree(config->range);
config->range = strdup(buffer); config->range = strdup(buffer);

View File

@ -400,9 +400,13 @@ ParameterError str2offset(curl_off_t *val, const char *str)
return PARAM_NEGATIVE_NUMERIC; return PARAM_NEGATIVE_NUMERIC;
#if(CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG) #if(CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
*val = curlx_strtoofft(str, &endptr, 0); {
if((*val == CURL_OFF_T_MAX || *val == CURL_OFF_T_MIN) && (errno == ERANGE)) CURLofft offt = curlx_strtoofft(str, &endptr, 0, val);
if(CURL_OFFT_FLOW == offt)
return PARAM_NUMBER_TOO_LARGE; return PARAM_NUMBER_TOO_LARGE;
else if(CURL_OFFT_INVAL == offt)
return PARAM_BAD_NUMERIC;
}
#else #else
errno = 0; errno = 0;
*val = strtol(str, &endptr, 0); *val = strtol(str, &endptr, 0);