1
0
mirror of https://github.com/moparisthebest/curl synced 2024-11-11 20:15:03 -05:00

urlglob: better detect unclosed braces, empty lists and overflows

A rather big overhaul and cleanup.

1 - curl wouldn't properly detect and reject globbing that ended with an
open brace if there were brackets or braces before it. Like "{}{" or
"[0-1]{"

2 - curl wouldn't properly reject empty lists so that "{}{}" would
result in curl getting (nil) strings in the output.

3 - By using strtoul() instead of sscanf() the code will now detected
over and underflows. It now also better parses the step argument to only
accept positive numbers and only step counters that is smaller than the
delta between the maximum and minimum numbers.

4 - By switching to unsigned longs instead of signed ints for the
counters, the max values for []-ranges are now very large (on 64bit
machines).

5 - Bumped the maximum number of globs in a single URL to 100 (from 10)

6 - Simplified the code somewhat and now it stores fixed strings as
single- entry lists. That's also one of the reasons why I did (5) as now
all strings between "globs" will take a slot in the array.

Added test 1234 and 1235 to verify. Updated test 87.

This commit fixes three separate bug reports.

Bug: http://curl.haxx.se/bug/view.cgi?id=1264
Bug: http://curl.haxx.se/bug/view.cgi?id=1265
Bug: http://curl.haxx.se/bug/view.cgi?id=1266
Reported-by: Will Dietz
This commit is contained in:
Daniel Stenberg 2013-08-15 13:05:25 +02:00
parent 10afe7cf10
commit 5ca96cb844
7 changed files with 421 additions and 258 deletions

View File

@ -196,6 +196,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
bool stillflags; bool stillflags;
int res = 0; int res = 0;
int i; int i;
unsigned long li;
bool orig_noprogress; bool orig_noprogress;
bool orig_isatty; bool orig_isatty;
@ -465,10 +466,10 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
for(urlnode = config->url_list; urlnode; urlnode = urlnode->next) { for(urlnode = config->url_list; urlnode; urlnode = urlnode->next) {
int up; /* upload file counter within a single upload glob */ unsigned long up; /* upload file counter within a single upload glob */
char *infiles; /* might be a glob pattern */ char *infiles; /* might be a glob pattern */
char *outfiles; char *outfiles;
int infilenum; unsigned long infilenum;
URLGlob *inglob; URLGlob *inglob;
int metalink = 0; /* nonzero for metalink download. */ int metalink = 0; /* nonzero for metalink download. */
@ -533,7 +534,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
char *uploadfile; /* a single file, never a glob */ char *uploadfile; /* a single file, never a glob */
int separator; int separator;
URLGlob *urls; URLGlob *urls;
int urlnum; unsigned long urlnum;
uploadfile = NULL; uploadfile = NULL;
urls = NULL; urls = NULL;
@ -583,7 +584,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1); separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1);
/* Here's looping around each globbed URL */ /* Here's looping around each globbed URL */
for(i = 0 ; i < urlnum; i++) { for(li = 0 ; li < urlnum; li++) {
int infd; int infd;
bool infdopen; bool infdopen;
@ -628,7 +629,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
if(res) if(res)
goto show_error; goto show_error;
} }
else if(!i) { else if(!li) {
this_url = strdup(urlnode->url); this_url = strdup(urlnode->url);
if(!this_url) { if(!this_url) {
res = CURLE_OUT_OF_MEMORY; res = CURLE_OUT_OF_MEMORY;
@ -863,8 +864,8 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
} }
if(urlnum > 1 && !(config->mute)) { if(urlnum > 1 && !(config->mute)) {
fprintf(config->errors, "\n[%d/%d]: %s --> %s\n", fprintf(config->errors, "\n[%lu/%lu]: %s --> %s\n",
i+1, urlnum, this_url, outfile ? outfile : "<stdout>"); li+1, urlnum, this_url, outfile ? outfile : "<stdout>");
if(separator) if(separator)
printf("%s%s\n", CURLseparator, this_url); printf("%s%s\n", CURLseparator, this_url);
} }

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2013, 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
@ -35,39 +35,53 @@ typedef enum {
GLOB_ERROR GLOB_ERROR
} GlobCode; } GlobCode;
/* void glob_cleanup(URLGlob* glob);
* glob_word()
*
* Input a full globbed string, set the forth argument to the amount of
* strings we get out of this. Return GlobCode.
*/
static GlobCode glob_word(URLGlob *, /* object anchor */
char *, /* globbed string */
size_t, /* position */
int *); /* returned number of strings */
static GlobCode glob_set(URLGlob *glob, char *pattern, static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount)
size_t pos, int *amount) {
URLPattern *pat = &glob->pattern[glob->size];
pat->type = UPTSet;
pat->content.Set.size = 1;
pat->content.Set.ptr_s = 0;
pat->globindex = -1;
(*amount)++;
pat->content.Set.elements = malloc(sizeof(char*));
if(!pat->content.Set.elements) {
snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
return GLOB_NO_MEM;
}
pat->content.Set.elements[0] = strdup(glob->glob_buffer);
if(!pat->content.Set.elements[0]) {
snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
return GLOB_NO_MEM;
}
return GLOB_OK;
}
static GlobCode glob_set(URLGlob *glob, char **patternp,
size_t pos, unsigned long *amount,
int globindex)
{ {
/* processes a set expression with the point behind the opening '{' /* processes a set expression with the point behind the opening '{'
','-separated elements are collected until the next closing '}' ','-separated elements are collected until the next closing '}'
*/ */
URLPattern *pat; URLPattern *pat;
GlobCode res;
bool done = FALSE; bool done = FALSE;
char* buf = glob->glob_buffer; char *buf = glob->glob_buffer;
char *pattern = *patternp;
char *opattern = pattern;
pat = &glob->pattern[glob->size / 2]; pat = &glob->pattern[glob->size];
/* patterns 0,1,2,... correspond to size=1,3,5,... */ /* patterns 0,1,2,... correspond to size=1,3,5,... */
pat->type = UPTSet; pat->type = UPTSet;
pat->content.Set.size = 0; pat->content.Set.size = 0;
pat->content.Set.ptr_s = 0; pat->content.Set.ptr_s = 0;
pat->content.Set.elements = NULL; pat->content.Set.elements = NULL;
pat->globindex = globindex;
if(++glob->size > (GLOB_PATTERN_NUM*2)) {
snprintf(glob->errormsg, sizeof(glob->errormsg), "too many globs used\n");
return GLOB_ERROR;
}
while(!done) { while(!done) {
switch (*pattern) { switch (*pattern) {
@ -82,60 +96,46 @@ static GlobCode glob_set(URLGlob *glob, char *pattern,
"nested braces not supported at pos %zu\n", pos); "nested braces not supported at pos %zu\n", pos);
return GLOB_ERROR; return GLOB_ERROR;
case ',':
case '}': /* set element completed */ case '}': /* set element completed */
if(opattern == pattern) {
snprintf(glob->errormsg, sizeof(glob->errormsg),
"no string within braces at pos %zu\n", pos);
return GLOB_ERROR;
}
/* add 1 since it'll be incremented below */
(*amount) *= (pat->content.Set.size+1);
/* fall-through */
case ',':
*buf = '\0'; *buf = '\0';
if(pat->content.Set.elements) { if(pat->content.Set.elements) {
char **new_arr = realloc(pat->content.Set.elements, char **new_arr = realloc(pat->content.Set.elements,
(pat->content.Set.size + 1) * sizeof(char*)); (pat->content.Set.size + 1) * sizeof(char*));
if(!new_arr) { if(!new_arr) {
short elem; snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
for(elem = 0; elem < pat->content.Set.size; elem++) return GLOB_NO_MEM;
Curl_safefree(pat->content.Set.elements[elem]);
Curl_safefree(pat->content.Set.elements);
pat->content.Set.ptr_s = 0;
pat->content.Set.size = 0;
} }
pat->content.Set.elements = new_arr; pat->content.Set.elements = new_arr;
} }
else else
pat->content.Set.elements = malloc(sizeof(char*)); pat->content.Set.elements = malloc(sizeof(char*));
if(!pat->content.Set.elements) { if(!pat->content.Set.elements) {
snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n"); snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
return GLOB_NO_MEM; return GLOB_NO_MEM;
} }
pat->content.Set.elements[pat->content.Set.size] = pat->content.Set.elements[pat->content.Set.size] =
strdup(glob->glob_buffer); strdup(glob->glob_buffer);
if(!pat->content.Set.elements[pat->content.Set.size]) { if(!pat->content.Set.elements[pat->content.Set.size]) {
short elem;
for(elem = 0; elem < pat->content.Set.size; elem++)
Curl_safefree(pat->content.Set.elements[elem]);
Curl_safefree(pat->content.Set.elements);
pat->content.Set.ptr_s = 0;
pat->content.Set.size = 0;
snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n"); snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
return GLOB_NO_MEM; return GLOB_NO_MEM;
} }
++pat->content.Set.size; ++pat->content.Set.size;
if(*pattern == '}') { if(*pattern == '}') {
/* entire set pattern completed */ pattern++; /* pass the closing brace */
int wordamount;
/* always check for a literal (may be "") between patterns */
res = glob_word(glob, ++pattern, ++pos, &wordamount);
if(res) {
short elem;
for(elem = 0; elem < pat->content.Set.size; elem++)
Curl_safefree(pat->content.Set.elements[elem]);
Curl_safefree(pat->content.Set.elements);
pat->content.Set.ptr_s = 0;
pat->content.Set.size = 0;
return res;
}
*amount = pat->content.Set.size * wordamount;
done = TRUE; done = TRUE;
continue; continue;
} }
@ -161,11 +161,14 @@ static GlobCode glob_set(URLGlob *glob, char *pattern,
++pos; ++pos;
} }
} }
*patternp = pattern; /* return with the new position */
return GLOB_OK; return GLOB_OK;
} }
static GlobCode glob_range(URLGlob *glob, char *pattern, static GlobCode glob_range(URLGlob *glob, char **patternp,
size_t pos, int *amount) size_t pos, unsigned long *amount,
int globindex)
{ {
/* processes a range expression with the point behind the opening '[' /* processes a range expression with the point behind the opening '['
- char range: e.g. "a-z]", "B-Q]" - char range: e.g. "a-z]", "B-Q]"
@ -174,77 +177,68 @@ static GlobCode glob_range(URLGlob *glob, char *pattern,
expression is checked for well-formedness and collected until the next ']' expression is checked for well-formedness and collected until the next ']'
*/ */
URLPattern *pat; URLPattern *pat;
char *c;
char sep;
char sep2;
int step;
int rc; int rc;
GlobCode res; char *pattern = *patternp;
int wordamount = 1; char *c;
pat = &glob->pattern[glob->size / 2]; pat = &glob->pattern[glob->size];
/* patterns 0,1,2,... correspond to size=1,3,5,... */ pat->globindex = globindex;
if(++glob->size > (GLOB_PATTERN_NUM*2)) {
snprintf(glob->errormsg, sizeof(glob->errormsg), "too many globs used\n");
return GLOB_ERROR;
}
if(ISALPHA(*pattern)) { if(ISALPHA(*pattern)) {
/* character range detected */ /* character range detected */
char min_c; char min_c;
char max_c; char max_c;
int step=1;
pat->type = UPTCharRange; pat->type = UPTCharRange;
rc = sscanf(pattern, "%c-%c%c%d%c", &min_c, &max_c, &sep, &step, &sep2); rc = sscanf(pattern, "%c-%c", &min_c, &max_c);
if((rc < 3) || (min_c >= max_c) || ((max_c - min_c) > ('z' - 'a'))) { if((rc == 2) && (pattern[3] == ':')) {
char *endp;
unsigned long lstep;
errno = 0;
lstep = strtoul(&pattern[3], &endp, 10);
if(errno || (*endp != ']'))
step = -1;
else {
pattern = endp+1;
step = (int)lstep;
if(step > (max_c - min_c))
step = -1;
}
}
else
pattern+=3;
if((rc != 2) || (min_c >= max_c) || ((max_c - min_c) > ('z' - 'a')) ||
(step < 0) ) {
/* the pattern is not well-formed */ /* the pattern is not well-formed */
snprintf(glob->errormsg, sizeof(glob->errormsg), snprintf(glob->errormsg, sizeof(glob->errormsg),
"error: bad range specification after pos %zu\n", pos); "error: bad range specification after pos %zu\n", pos);
return GLOB_ERROR; return GLOB_ERROR;
} }
/* check the (first) separating character */
if((sep != ']') && (sep != ':')) {
snprintf(glob->errormsg, sizeof(glob->errormsg),
"error: unsupported character (%c) after range at pos %zu\n",
sep, pos);
return GLOB_ERROR;
}
/* if there was a ":[num]" thing, use that as step or else use 1 */ /* if there was a ":[num]" thing, use that as step or else use 1 */
pat->content.CharRange.step = pat->content.CharRange.step = step;
((sep == ':') && (rc == 5) && (sep2 == ']')) ? step : 1;
pat->content.CharRange.ptr_c = pat->content.CharRange.min_c = min_c; pat->content.CharRange.ptr_c = pat->content.CharRange.min_c = min_c;
pat->content.CharRange.max_c = max_c; pat->content.CharRange.max_c = max_c;
*amount *= (pat->content.CharRange.max_c -
pat->content.CharRange.min_c + 1);
} }
else if(ISDIGIT(*pattern)) { else if(ISDIGIT(*pattern)) {
/* numeric range detected */ /* numeric range detected */
int min_n; unsigned long min_n;
int max_n; unsigned long max_n;
unsigned long step_n;
char *endp;
pat->type = UPTNumRange; pat->type = UPTNumRange;
pat->content.NumRange.padlength = 0; pat->content.NumRange.padlength = 0;
rc = sscanf(pattern, "%d-%d%c%d%c", &min_n, &max_n, &sep, &step, &sep2);
if((rc < 2) || (min_n > max_n)) {
/* the pattern is not well-formed */
snprintf(glob->errormsg, sizeof(glob->errormsg),
"error: bad range specification after pos %zu\n", pos);
return GLOB_ERROR;
}
pat->content.NumRange.ptr_n = pat->content.NumRange.min_n = min_n;
pat->content.NumRange.max_n = max_n;
/* if there was a ":[num]" thing, use that as step or else use 1 */
pat->content.NumRange.step =
((sep == ':') && (rc == 5) && (sep2 == ']')) ? step : 1;
if(*pattern == '0') { if(*pattern == '0') {
/* leading zero specified */ /* leading zero specified, count them! */
c = pattern; c = pattern;
while(ISDIGIT(*c)) { while(ISDIGIT(*c)) {
c++; c++;
@ -252,6 +246,50 @@ static GlobCode glob_range(URLGlob *glob, char *pattern,
instances of this pattern */ instances of this pattern */
} }
} }
errno = 0;
min_n = strtoul(pattern, &endp, 10);
if(errno || (endp == pattern))
endp=NULL;
else {
if(*endp != '-')
endp = NULL;
else {
pattern = endp+1;
errno = 0;
max_n = strtoul(pattern, &endp, 10);
if(errno || (*endp == ':')) {
pattern = endp+1;
errno = 0;
step_n = strtoul(pattern, &endp, 10);
if(errno)
/* over/underflow situation */
endp = NULL;
}
else
step_n = 1;
if(*endp == ']') {
pattern= endp+1;
}
else
endp = NULL;
}
}
if(!endp || (min_n > max_n) || (step_n > (max_n - min_n))) {
/* the pattern is not well-formed */
snprintf(glob->errormsg, sizeof(glob->errormsg),
"error: bad range specification after pos %zu\n", pos);
return GLOB_ERROR;
}
/* typecasting to ints are fine here since we make sure above that we
are within 31 bits */
pat->content.NumRange.ptr_n = pat->content.NumRange.min_n = min_n;
pat->content.NumRange.max_n = max_n;
pat->content.NumRange.step = step_n;
*amount *= (pat->content.NumRange.max_n -
pat->content.NumRange.min_n + 1);
} }
else { else {
snprintf(glob->errormsg, sizeof(glob->errormsg), snprintf(glob->errormsg, sizeof(glob->errormsg),
@ -259,105 +297,87 @@ static GlobCode glob_range(URLGlob *glob, char *pattern,
return GLOB_ERROR; return GLOB_ERROR;
} }
c = (char*)strchr(pattern, ']'); /* continue after next ']' */ *patternp = pattern;
if(c) return GLOB_OK;
c++;
else {
snprintf(glob->errormsg, sizeof(glob->errormsg), "missing ']'");
return GLOB_ERROR; /* missing ']' */
}
/* always check for a literal (may be "") between patterns */
res = glob_word(glob, c, pos + (c - pattern), &wordamount);
if(res == GLOB_ERROR) {
wordamount = 1;
res = GLOB_OK;
}
if(!res) {
if(pat->type == UPTCharRange)
*amount = wordamount * (pat->content.CharRange.max_c -
pat->content.CharRange.min_c + 1);
else
*amount = wordamount * (pat->content.NumRange.max_n -
pat->content.NumRange.min_n + 1);
}
return res; /* GLOB_OK or GLOB_NO_MEM */
} }
static GlobCode glob_word(URLGlob *glob, char *pattern, static GlobCode glob_parse(URLGlob *glob, char *pattern,
size_t pos, int *amount) size_t pos, unsigned long *amount)
{ {
/* processes a literal string component of a URL /* processes a literal string component of a URL
special characters '{' and '[' branch to set/range processing functions special characters '{' and '[' branch to set/range processing functions
*/ */
char* buf = glob->glob_buffer; char* buf = glob->glob_buffer;
size_t litindex;
GlobCode res = GLOB_OK; GlobCode res = GLOB_OK;
int globindex = 0; /* count "actual" globs */
*amount = 1; /* default is one single string */ while(*pattern && !res) {
int sublen = 0;
while(*pattern && *pattern != '{' && *pattern != '[') {
if(*pattern == '}' || *pattern == ']') {
snprintf(glob->errormsg, sizeof(glob->errormsg),
"unmatched close brace/bracket at pos %zu\n", pos);
return GLOB_ERROR;
}
while(*pattern != '\0' && *pattern != '{' && *pattern != '[') { /* only allow \ to escape known "special letters" */
if(*pattern == '}' || *pattern == ']') { if(*pattern == '\\' &&
(*(pattern+1) == '{' || *(pattern+1) == '[' ||
*(pattern+1) == '}' || *(pattern+1) == ']') ) {
/* escape character, skip '\' */
++pattern;
++pos;
}
*buf++ = *pattern++; /* copy character to literal */
++pos;
sublen++;
}
if(sublen) {
/* we got a literal string, add it as a single-item list */
*buf = '\0';
res = glob_fixed(glob, amount);
}
else {
if(!*amount)
*amount = 1;
switch (*pattern) {
case '\0': /* done */
break;
case '{':
/* process set pattern */
pattern++;
res = glob_set(glob, &pattern, ++pos, amount, globindex++);
break;
case '[':
/* process range pattern */
pattern++;
res = glob_range(glob, &pattern, ++pos, amount, globindex++);
break;
}
}
if(++glob->size > GLOB_PATTERN_NUM) {
snprintf(glob->errormsg, sizeof(glob->errormsg), snprintf(glob->errormsg, sizeof(glob->errormsg),
"unmatched close brace/bracket at pos %zu\n", pos); "too many globs used\n");
return GLOB_ERROR; return GLOB_ERROR;
} }
/* only allow \ to escape known "special letters" */
if(*pattern == '\\' &&
(*(pattern+1) == '{' || *(pattern+1) == '[' ||
*(pattern+1) == '}' || *(pattern+1) == ']') ) {
/* escape character, skip '\' */
++pattern;
++pos;
}
*buf++ = *pattern++; /* copy character to literal */
++pos;
} }
*buf = '\0';
litindex = glob->size / 2;
/* literals 0,1,2,... correspond to size=0,2,4,... */
glob->literal[litindex] = strdup(glob->glob_buffer);
if(!glob->literal[litindex]) {
snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
return GLOB_NO_MEM;
}
++glob->size;
switch (*pattern) {
case '\0':
/* singular URL processed */
break;
case '{':
/* process set pattern */
res = glob_set(glob, ++pattern, ++pos, amount);
break;
case '[':
/* process range pattern */
res = glob_range(glob, ++pattern, ++pos, amount);
break;
}
if(res)
Curl_safefree(glob->literal[litindex]);
return res; return res;
} }
int glob_url(URLGlob** glob, char* url, int *urlnum, FILE *error) int glob_url(URLGlob** glob, char* url, unsigned long *urlnum, FILE *error)
{ {
/* /*
* We can deal with any-size, just make a buffer with the same length * We can deal with any-size, just make a buffer with the same length
* as the specified URL! * as the specified URL!
*/ */
URLGlob *glob_expand; URLGlob *glob_expand;
int amount; unsigned long amount = 0;
char *glob_buffer; char *glob_buffer;
GlobCode res; GlobCode res;
@ -372,12 +392,10 @@ int glob_url(URLGlob** glob, char* url, int *urlnum, FILE *error)
Curl_safefree(glob_buffer); Curl_safefree(glob_buffer);
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
} }
glob_expand->size = 0;
glob_expand->urllen = strlen(url); glob_expand->urllen = strlen(url);
glob_expand->glob_buffer = glob_buffer; glob_expand->glob_buffer = glob_buffer;
glob_expand->beenhere = 0;
res = glob_word(glob_expand, url, 1, &amount); res = glob_parse(glob_expand, url, 1, &amount);
if(!res) if(!res)
*urlnum = amount; *urlnum = amount;
else { else {
@ -388,8 +406,7 @@ int glob_url(URLGlob** glob, char* url, int *urlnum, FILE *error)
glob_expand->errormsg); glob_expand->errormsg);
} }
/* it failed, we cleanup */ /* it failed, we cleanup */
Curl_safefree(glob_buffer); glob_cleanup(glob_expand);
Curl_safefree(glob_expand);
*urlnum = 1; *urlnum = 1;
return (res == GLOB_NO_MEM) ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT; return (res == GLOB_NO_MEM) ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT;
} }
@ -404,19 +421,14 @@ void glob_cleanup(URLGlob* glob)
int elem; int elem;
for(i = glob->size - 1; i < glob->size; --i) { for(i = glob->size - 1; i < glob->size; --i) {
if(!(i & 1)) { /* even indexes contain literals */ if((glob->pattern[i].type == UPTSet) &&
Curl_safefree(glob->literal[i/2]); (glob->pattern[i].content.Set.elements)) {
} for(elem = glob->pattern[i].content.Set.size - 1;
else { /* odd indexes contain sets or ranges */ elem >= 0;
if((glob->pattern[i/2].type == UPTSet) && --elem) {
(glob->pattern[i/2].content.Set.elements)) { Curl_safefree(glob->pattern[i].content.Set.elements[elem]);
for(elem = glob->pattern[i/2].content.Set.size - 1;
elem >= 0;
--elem) {
Curl_safefree(glob->pattern[i/2].content.Set.elements[elem]);
}
Curl_safefree(glob->pattern[i/2].content.Set.elements);
} }
Curl_safefree(glob->pattern[i].content.Set.elements);
} }
} }
Curl_safefree(glob->glob_buffer); Curl_safefree(glob->glob_buffer);
@ -426,7 +438,6 @@ void glob_cleanup(URLGlob* glob)
int glob_next_url(char **globbed, URLGlob *glob) int glob_next_url(char **globbed, URLGlob *glob)
{ {
URLPattern *pat; URLPattern *pat;
char *lit;
size_t i; size_t i;
size_t j; size_t j;
size_t len; size_t len;
@ -442,7 +453,7 @@ int glob_next_url(char **globbed, URLGlob *glob)
/* implement a counter over the index ranges of all patterns, /* implement a counter over the index ranges of all patterns,
starting with the rightmost pattern */ starting with the rightmost pattern */
for(i = glob->size / 2 - 1; carry && (i < glob->size); --i) { for(i = glob->size - 1; carry && (i < glob->size); --i) {
carry = FALSE; carry = FALSE;
pat = &glob->pattern[i]; pat = &glob->pattern[i];
switch (pat->type) { switch (pat->type) {
@ -480,38 +491,30 @@ int glob_next_url(char **globbed, URLGlob *glob)
} }
for(j = 0; j < glob->size; ++j) { for(j = 0; j < glob->size; ++j) {
if(!(j&1)) { /* every other term (j even) is a literal */ pat = &glob->pattern[j];
lit = glob->literal[j/2]; switch(pat->type) {
len = snprintf(buf, buflen, "%s", lit); case UPTSet:
buf += len; if(pat->content.Set.elements) {
buflen -= len; len = strlen(pat->content.Set.elements[pat->content.Set.ptr_s]);
} snprintf(buf, buflen, "%s",
else { /* the rest (i odd) are patterns */ pat->content.Set.elements[pat->content.Set.ptr_s]);
pat = &glob->pattern[j/2];
switch(pat->type) {
case UPTSet:
if(pat->content.Set.elements) {
len = strlen(pat->content.Set.elements[pat->content.Set.ptr_s]);
snprintf(buf, buflen, "%s",
pat->content.Set.elements[pat->content.Set.ptr_s]);
buf += len;
buflen -= len;
}
break;
case UPTCharRange:
*buf++ = pat->content.CharRange.ptr_c;
break;
case UPTNumRange:
len = snprintf(buf, buflen, "%0*d",
pat->content.NumRange.padlength,
pat->content.NumRange.ptr_n);
buf += len; buf += len;
buflen -= len; buflen -= len;
break;
default:
printf("internal error: invalid pattern type (%d)\n", (int)pat->type);
return CURLE_FAILED_INIT;
} }
break;
case UPTCharRange:
*buf++ = pat->content.CharRange.ptr_c;
break;
case UPTNumRange:
len = snprintf(buf, buflen, "%0*ld",
pat->content.NumRange.padlength,
pat->content.NumRange.ptr_n);
buf += len;
buflen -= len;
break;
default:
printf("internal error: invalid pattern type (%d)\n", (int)pat->type);
return CURLE_FAILED_INIT;
} }
} }
*buf = '\0'; *buf = '\0';
@ -549,34 +552,44 @@ int glob_match_url(char **result, char *filename, URLGlob *glob)
unsigned long i; unsigned long i;
char *ptr = filename; char *ptr = filename;
unsigned long num = strtoul(&filename[1], &filename, 10); unsigned long num = strtoul(&filename[1], &filename, 10);
i = num - 1UL; URLPattern *pat =NULL;
if(num && (i <= glob->size / 2)) { if(num < glob->size) {
URLPattern pat = glob->pattern[i]; num--; /* make it zero based */
switch (pat.type) { /* find the correct glob entry */
for(i=0; i<glob->size; i++) {
if(glob->pattern[i].globindex == (int)num) {
pat = &glob->pattern[i];
break;
}
}
}
if(pat) {
switch (pat->type) {
case UPTSet: case UPTSet:
if(pat.content.Set.elements) { if(pat->content.Set.elements) {
appendthis = pat.content.Set.elements[pat.content.Set.ptr_s]; appendthis = pat->content.Set.elements[pat->content.Set.ptr_s];
appendlen = appendlen =
strlen(pat.content.Set.elements[pat.content.Set.ptr_s]); strlen(pat->content.Set.elements[pat->content.Set.ptr_s]);
} }
break; break;
case UPTCharRange: case UPTCharRange:
numbuf[0] = pat.content.CharRange.ptr_c; numbuf[0] = pat->content.CharRange.ptr_c;
numbuf[1] = 0; numbuf[1] = 0;
appendthis = numbuf; appendthis = numbuf;
appendlen = 1; appendlen = 1;
break; break;
case UPTNumRange: case UPTNumRange:
snprintf(numbuf, sizeof(numbuf), "%0*d", snprintf(numbuf, sizeof(numbuf), "%0*d",
pat.content.NumRange.padlength, pat->content.NumRange.padlength,
pat.content.NumRange.ptr_n); pat->content.NumRange.ptr_n);
appendthis = numbuf; appendthis = numbuf;
appendlen = strlen(numbuf); appendlen = strlen(numbuf);
break; break;
default: default:
printf("internal error: invalid pattern type (%d)\n", fprintf(stderr, "internal error: invalid pattern type (%d)\n",
(int)pat.type); (int)pat->type);
Curl_safefree(target); Curl_safefree(target);
return CURLE_FAILED_INIT; return CURLE_FAILED_INIT;
} }

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2013, 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
@ -31,11 +31,13 @@ typedef enum {
typedef struct { typedef struct {
URLPatternType type; URLPatternType type;
int globindex; /* the number of this particular glob or -1 if not used
within {} or [] */
union { union {
struct { struct {
char **elements; char **elements;
short size; int size;
short ptr_s; int ptr_s;
} Set; } Set;
struct { struct {
char min_c; char min_c;
@ -44,21 +46,20 @@ typedef struct {
int step; int step;
} CharRange; } CharRange;
struct { struct {
int min_n; unsigned long min_n;
int max_n; unsigned long max_n;
short padlength; int padlength;
int ptr_n; unsigned long ptr_n;
int step; unsigned long step;
} NumRange ; } NumRange ;
} content; } content;
} URLPattern; } URLPattern;
/* the total number of globs supported */ /* the total number of globs supported */
#define GLOB_PATTERN_NUM 9 #define GLOB_PATTERN_NUM 100
typedef struct { typedef struct {
char *literal[10]; URLPattern pattern[GLOB_PATTERN_NUM];
URLPattern pattern[GLOB_PATTERN_NUM+1];
size_t size; size_t size;
size_t urllen; size_t urllen;
char *glob_buffer; char *glob_buffer;
@ -66,7 +67,7 @@ typedef struct {
char errormsg[80]; /* error message buffer */ char errormsg[80]; /* error message buffer */
} URLGlob; } URLGlob;
int glob_url(URLGlob**, char*, int *, FILE *); int glob_url(URLGlob**, char*, unsigned long *, FILE *);
int glob_next_url(char **, URLGlob *); int glob_next_url(char **, URLGlob *);
int glob_match_url(char **, char*, URLGlob *); int glob_match_url(char **, char*, URLGlob *);
void glob_cleanup(URLGlob* glob); void glob_cleanup(URLGlob* glob);

View File

@ -93,7 +93,7 @@ test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
test1208 test1209 test1210 test1211 test1212 test1213 test1214 test1215 \ test1208 test1209 test1210 test1211 test1212 test1213 test1214 test1215 \
test1216 test1217 test1218 test1219 \ test1216 test1217 test1218 test1219 \
test1220 test1221 test1222 test1223 test1224 test1225 test1226 test1227 \ test1220 test1221 test1222 test1223 test1224 test1225 test1226 test1227 \
test1228 test1229 test1230 test1231 test1232 test1233 \ test1228 test1229 test1230 test1231 test1232 test1233 test1234 test1235 \
\ \
test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 \ test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 \
test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 \ test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 \

32
tests/data/test1234 Normal file
View File

@ -0,0 +1,32 @@
<testcase>
<info>
<keywords>
{} list
FAILURE
</keywords>
</info>
# Server-side
<reply>
</reply>
# Client-side
<client>
<server>
none
</server>
<name>
abusing {}-globbing
</name>
<command>
"%HOSTIP:%HTTPPORT/1234[0-1]{" "%HOSTIP:%HTTPPORT/{}{}{}{"
</command>
</client>
# Verify data after the test has been "shot"
<verify>
# 3 == CURLE_URL_MALFORMAT
<errorcode>
3
</errorcode>
</verify>
</testcase>

94
tests/data/test1235 Normal file
View File

@ -0,0 +1,94 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
{} list
</keywords>
</info>
# Server-side
<reply>
<data1>
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 15
the number one
</data1>
<data2>
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 16
two is nice too
</data2>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
multiple requests using {}{} in the URL
</name>
<command>
"%HOSTIP:%HTTPPORT/{1235,1235}{0001,0002}"
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol>
GET /12350001 HTTP/1.1
User-Agent: curl/7.8.1-pre3 (sparc-sun-solaris2.7) libcurl 7.8.1-pre3 (OpenSSL 0.9.6a) (krb4 enabled)
Host: %HOSTIP:%HTTPPORT
Accept: */*
GET /12350002 HTTP/1.1
User-Agent: curl/7.8.1-pre3 (sparc-sun-solaris2.7) libcurl 7.8.1-pre3 (OpenSSL 0.9.6a) (krb4 enabled)
Host: %HOSTIP:%HTTPPORT
Accept: */*
GET /12350001 HTTP/1.1
User-Agent: curl/7.8.1-pre3 (sparc-sun-solaris2.7) libcurl 7.8.1-pre3 (OpenSSL 0.9.6a) (krb4 enabled)
Host: %HOSTIP:%HTTPPORT
Accept: */*
GET /12350002 HTTP/1.1
User-Agent: curl/7.8.1-pre3 (sparc-sun-solaris2.7) libcurl 7.8.1-pre3 (OpenSSL 0.9.6a) (krb4 enabled)
Host: %HOSTIP:%HTTPPORT
Accept: */*
</protocol>
<stdout>
--_curl_--%HOSTIP:%HTTPPORT/12350001
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 15
the number one
--_curl_--%HOSTIP:%HTTPPORT/12350002
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 16
two is nice too
--_curl_--%HOSTIP:%HTTPPORT/12350001
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 15
the number one
--_curl_--%HOSTIP:%HTTPPORT/12350002
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 16
two is nice too
</stdout>
</verify>
</testcase>

View File

@ -8,29 +8,51 @@ FAILURE
# #
# Server-side # Server-side
<reply> <reply>
<data1>
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 15
the number one
</data1>
<data2>
HTTP/1.1 200 OK
Funny-head: yesyes
Content-Length: 16
two is nice too
</data2>
</reply> </reply>
# #
# Client-side # Client-side
<client> <client>
<server> <server>
none http
</server> </server>
<features> <features>
http http
</features> </features>
<name> <name>
urlglob with bad -o #[num] usage urlglob with out of range -o #[num] usage
</name> </name>
<command option="no-output"> <command option="no-output">
"http://%HOSTIP:%HTTPPORT/[870001-870003]" -o "log/dumpit#2.dump" "http://%HOSTIP:%HTTPPORT/[870001-870002]" -o "log/dumpit#2.dump"
</command> </command>
</client> </client>
# #
# Verify data after the test has been "shot" # Verify data after the test has been "shot". Note that the command line
# will write both responses into the same file name so only the second
# survives
#
<verify> <verify>
<errorcode> <file name="log/dumpit#2.dump" [mode="text"]>
2 HTTP/1.1 200 OK
</errorcode> Funny-head: yesyes
Content-Length: 16
two is nice too
</file>
</verify> </verify>
</testcase> </testcase>