[curl/f19] extend URL parser to support IPv6 zone identifiers (#680996)
Kamil Dudka
kdudka at fedoraproject.org
Sat May 10 17:42:06 UTC 2014
commit 73f933c433a2f39906eb3e2fe4691d52c0ab123e
Author: Kamil Dudka <kdudka at redhat.com>
Date: Wed Apr 2 15:25:45 2014 +0200
extend URL parser to support IPv6 zone identifiers (#680996)
0020-curl-7.29.0-9317eced.patch | 2235 +++++++++++++++++++++++++++++++++++++++
curl.spec | 9 +-
2 files changed, 2243 insertions(+), 1 deletions(-)
---
diff --git a/0020-curl-7.29.0-9317eced.patch b/0020-curl-7.29.0-9317eced.patch
new file mode 100644
index 0000000..b7e56b4
--- /dev/null
+++ b/0020-curl-7.29.0-9317eced.patch
@@ -0,0 +1,2235 @@
+From 2188bcf999693fea405b692d84cef1c4a3fcbfa0 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Thu, 15 Aug 2013 13:05:25 +0200
+Subject: [PATCH 01/13] 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
+
+[upstream commit 5ca96cb84410270e233c92bf1b2583cba40c3fad]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_operate.c | 15 +-
+ src/tool_urlglob.c | 473 +++++++++++++++++++++++++------------------------
+ src/tool_urlglob.h | 25 +--
+ tests/data/Makefile.am | 2 +-
+ tests/data/test1234 | 32 ++++
+ tests/data/test1235 | 94 ++++++++++
+ tests/data/test87 | 36 +++-
+ 7 files changed, 420 insertions(+), 257 deletions(-)
+ create mode 100644 tests/data/test1234
+ create mode 100644 tests/data/test1235
+
+diff --git a/src/tool_operate.c b/src/tool_operate.c
+index ed60e70..dbbbc26 100644
+--- a/src/tool_operate.c
++++ b/src/tool_operate.c
+@@ -136,6 +136,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
+ bool stillflags;
+ int res = 0;
+ int i;
++ unsigned long li;
+
+ bool orig_noprogress;
+ bool orig_isatty;
+@@ -405,10 +406,10 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
+
+ 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 *outfiles;
+- int infilenum;
++ unsigned long infilenum;
+ URLGlob *inglob;
+
+ int metalink = 0; /* nonzero for metalink download. */
+@@ -473,7 +474,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
+ char *uploadfile; /* a single file, never a glob */
+ int separator;
+ URLGlob *urls;
+- int urlnum;
++ unsigned long urlnum;
+
+ uploadfile = NULL;
+ urls = NULL;
+@@ -523,7 +524,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
+ separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1);
+
+ /* Here's looping around each globbed URL */
+- for(i = 0 ; i < urlnum; i++) {
++ for(li = 0 ; li < urlnum; li++) {
+
+ int infd;
+ bool infdopen;
+@@ -568,7 +569,7 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
+ if(res)
+ goto show_error;
+ }
+- else if(!i) {
++ else if(!li) {
+ this_url = strdup(urlnode->url);
+ if(!this_url) {
+ res = CURLE_OUT_OF_MEMORY;
+@@ -777,8 +778,8 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
+ }
+
+ if(urlnum > 1 && !(config->mute)) {
+- fprintf(config->errors, "\n[%d/%d]: %s --> %s\n",
+- i+1, urlnum, this_url, outfile ? outfile : "<stdout>");
++ fprintf(config->errors, "\n[%lu/%lu]: %s --> %s\n",
++ li+1, urlnum, this_url, outfile ? outfile : "<stdout>");
+ if(separator)
+ printf("%s%s\n", CURLseparator, this_url);
+ }
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index 2821d00..0e454c1 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -5,7 +5,7 @@
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel at haxx.se>, et al.
++ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel at haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+@@ -35,39 +35,53 @@ typedef enum {
+ GLOB_ERROR
+ } GlobCode;
+
+-/*
+- * 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,
+- size_t pos, int *amount)
++void glob_cleanup(URLGlob* glob);
++
++static GlobCode glob_fixed(URLGlob *glob, unsigned long *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 '{'
+ ','-separated elements are collected until the next closing '}'
+ */
+ URLPattern *pat;
+- GlobCode res;
+ 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,... */
+ pat->type = UPTSet;
+ pat->content.Set.size = 0;
+ pat->content.Set.ptr_s = 0;
+ pat->content.Set.elements = NULL;
+-
+- if(++glob->size > (GLOB_PATTERN_NUM*2)) {
+- snprintf(glob->errormsg, sizeof(glob->errormsg), "too many globs used\n");
+- return GLOB_ERROR;
+- }
++ pat->globindex = globindex;
+
+ while(!done) {
+ switch (*pattern) {
+@@ -82,60 +96,46 @@ static GlobCode glob_set(URLGlob *glob, char *pattern,
+ "nested braces not supported at pos %zu\n", pos);
+ return GLOB_ERROR;
+
+- case ',':
+ 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';
+ if(pat->content.Set.elements) {
+ char **new_arr = realloc(pat->content.Set.elements,
+ (pat->content.Set.size + 1) * sizeof(char*));
+ if(!new_arr) {
+- 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");
++ return GLOB_NO_MEM;
+ }
++
+ pat->content.Set.elements = new_arr;
+ }
+ else
+ 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[pat->content.Set.size] =
+ strdup(glob->glob_buffer);
+ 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");
+ return GLOB_NO_MEM;
+ }
+ ++pat->content.Set.size;
+
+ if(*pattern == '}') {
+- /* entire set pattern completed */
+- 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;
+-
++ pattern++; /* pass the closing brace */
+ done = TRUE;
+ continue;
+ }
+@@ -161,11 +161,14 @@ static GlobCode glob_set(URLGlob *glob, char *pattern,
+ ++pos;
+ }
+ }
++
++ *patternp = pattern; /* return with the new position */
+ return GLOB_OK;
+ }
+
+-static GlobCode glob_range(URLGlob *glob, char *pattern,
+- size_t pos, int *amount)
++static GlobCode glob_range(URLGlob *glob, char **patternp,
++ size_t pos, unsigned long *amount,
++ int globindex)
+ {
+ /* processes a range expression with the point behind the opening '['
+ - char range: e.g. "a-z]", "B-Q]"
+@@ -174,84 +177,119 @@ static GlobCode glob_range(URLGlob *glob, char *pattern,
+ expression is checked for well-formedness and collected until the next ']'
+ */
+ URLPattern *pat;
+- char *c;
+- char sep;
+- char sep2;
+- int step;
+ int rc;
+- GlobCode res;
+- int wordamount = 1;
++ char *pattern = *patternp;
++ char *c;
+
+- pat = &glob->pattern[glob->size / 2];
+- /* patterns 0,1,2,... correspond to size=1,3,5,... */
+- if(++glob->size > (GLOB_PATTERN_NUM*2)) {
+- snprintf(glob->errormsg, sizeof(glob->errormsg), "too many globs used\n");
+- return GLOB_ERROR;
+- }
++ pat = &glob->pattern[glob->size];
++ pat->globindex = globindex;
+
+ if(ISALPHA(*pattern)) {
+ /* character range detected */
+ char min_c;
+ char max_c;
++ int step=1;
+
+ 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'))) {
+- /* the pattern is not well-formed */
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "error: bad range specification after pos %zu\n", pos);
+- return GLOB_ERROR;
++ 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;
+
+- /* check the (first) separating character */
+- if((sep != ']') && (sep != ':')) {
++ if((rc != 2) || (min_c >= max_c) || ((max_c - min_c) > ('z' - 'a')) ||
++ (step < 0) ) {
++ /* the pattern is not well-formed */
+ snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "error: unsupported character (%c) after range at pos %zu\n",
+- sep, pos);
++ "error: bad range specification after pos %zu\n", pos);
+ return GLOB_ERROR;
+ }
+
+ /* if there was a ":[num]" thing, use that as step or else use 1 */
+- pat->content.CharRange.step =
+- ((sep == ':') && (rc == 5) && (sep2 == ']')) ? step : 1;
+-
++ pat->content.CharRange.step = step;
+ pat->content.CharRange.ptr_c = pat->content.CharRange.min_c = min_c;
+ pat->content.CharRange.max_c = max_c;
++
++ *amount *= (pat->content.CharRange.max_c -
++ pat->content.CharRange.min_c + 1);
+ }
+ else if(ISDIGIT(*pattern)) {
+ /* numeric range detected */
+- int min_n;
+- int max_n;
++ unsigned long min_n;
++ unsigned long max_n;
++ unsigned long step_n;
++ char *endp;
+
+ pat->type = UPTNumRange;
+ pat->content.NumRange.padlength = 0;
+
+- rc = sscanf(pattern, "%d-%d%c%d%c", &min_n, &max_n, &sep, &step, &sep2);
++ if(*pattern == '0') {
++ /* leading zero specified, count them! */
++ c = pattern;
++ while(ISDIGIT(*c)) {
++ c++;
++ ++pat->content.NumRange.padlength; /* padding length is set for all
++ instances of this pattern */
++ }
++ }
+
+- if((rc < 2) || (min_n > max_n)) {
++ 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;
+
+- /* 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') {
+- /* leading zero specified */
+- c = pattern;
+- while(ISDIGIT(*c)) {
+- c++;
+- ++pat->content.NumRange.padlength; /* padding length is set for all
+- instances of this pattern */
+- }
+- }
++ *amount *= (pat->content.NumRange.max_n -
++ pat->content.NumRange.min_n + 1);
+ }
+ else {
+ snprintf(glob->errormsg, sizeof(glob->errormsg),
+@@ -259,105 +297,87 @@ static GlobCode glob_range(URLGlob *glob, char *pattern,
+ return GLOB_ERROR;
+ }
+
+- c = (char*)strchr(pattern, ']'); /* continue after next ']' */
+- if(c)
+- 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 */
++ *patternp = pattern;
++ return GLOB_OK;
+ }
+
+-static GlobCode glob_word(URLGlob *glob, char *pattern,
+- size_t pos, int *amount)
++static GlobCode glob_parse(URLGlob *glob, char *pattern,
++ size_t pos, unsigned long *amount)
+ {
+ /* processes a literal string component of a URL
+ special characters '{' and '[' branch to set/range processing functions
+ */
+ char* buf = glob->glob_buffer;
+- size_t litindex;
+ GlobCode res = GLOB_OK;
++ int globindex = 0; /* count "actual" globs */
++
++ 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;
++ }
+
+- *amount = 1; /* default is one single string */
++ /* only allow \ to escape known "special letters" */
++ if(*pattern == '\\' &&
++ (*(pattern+1) == '{' || *(pattern+1) == '[' ||
++ *(pattern+1) == '}' || *(pattern+1) == ']') ) {
+
+- while(*pattern != '\0' && *pattern != '{' && *pattern != '[') {
+- if(*pattern == '}' || *pattern == ']') {
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "unmatched close brace/bracket at pos %zu\n", pos);
+- return GLOB_ERROR;
++ /* 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;
+
+- /* only allow \ to escape known "special letters" */
+- if(*pattern == '\\' &&
+- (*(pattern+1) == '{' || *(pattern+1) == '[' ||
+- *(pattern+1) == '}' || *(pattern+1) == ']') ) {
++ switch (*pattern) {
++ case '\0': /* done */
++ break;
+
+- /* escape character, skip '\' */
+- ++pattern;
+- ++pos;
++ 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;
++ }
+ }
+- *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]);
++ if(++glob->size > GLOB_PATTERN_NUM) {
++ snprintf(glob->errormsg, sizeof(glob->errormsg),
++ "too many globs used\n");
++ return GLOB_ERROR;
++ }
+
++ }
+ 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
+ * as the specified URL!
+ */
+ URLGlob *glob_expand;
+- int amount;
++ unsigned long amount = 0;
+ char *glob_buffer;
+ GlobCode res;
+
+@@ -372,12 +392,10 @@ int glob_url(URLGlob** glob, char* url, int *urlnum, FILE *error)
+ Curl_safefree(glob_buffer);
+ return CURLE_OUT_OF_MEMORY;
+ }
+- glob_expand->size = 0;
+ glob_expand->urllen = strlen(url);
+ 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)
+ *urlnum = amount;
+ else {
+@@ -388,8 +406,7 @@ int glob_url(URLGlob** glob, char* url, int *urlnum, FILE *error)
+ glob_expand->errormsg);
+ }
+ /* it failed, we cleanup */
+- Curl_safefree(glob_buffer);
+- Curl_safefree(glob_expand);
++ glob_cleanup(glob_expand);
+ *urlnum = 1;
+ return (res == GLOB_NO_MEM) ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT;
+ }
+@@ -404,19 +421,14 @@ void glob_cleanup(URLGlob* glob)
+ int elem;
+
+ for(i = glob->size - 1; i < glob->size; --i) {
+- if(!(i & 1)) { /* even indexes contain literals */
+- Curl_safefree(glob->literal[i/2]);
+- }
+- else { /* odd indexes contain sets or ranges */
+- if((glob->pattern[i/2].type == UPTSet) &&
+- (glob->pattern[i/2].content.Set.elements)) {
+- 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);
++ if((glob->pattern[i].type == UPTSet) &&
++ (glob->pattern[i].content.Set.elements)) {
++ for(elem = glob->pattern[i].content.Set.size - 1;
++ elem >= 0;
++ --elem) {
++ Curl_safefree(glob->pattern[i].content.Set.elements[elem]);
+ }
++ Curl_safefree(glob->pattern[i].content.Set.elements);
+ }
+ }
+ Curl_safefree(glob->glob_buffer);
+@@ -426,7 +438,6 @@ void glob_cleanup(URLGlob* glob)
+ int glob_next_url(char **globbed, URLGlob *glob)
+ {
+ URLPattern *pat;
+- char *lit;
+ size_t i;
+ size_t j;
+ 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,
+ 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;
+ pat = &glob->pattern[i];
+ switch (pat->type) {
+@@ -480,38 +491,30 @@ int glob_next_url(char **globbed, URLGlob *glob)
+ }
+
+ for(j = 0; j < glob->size; ++j) {
+- if(!(j&1)) { /* every other term (j even) is a literal */
+- lit = glob->literal[j/2];
+- len = snprintf(buf, buflen, "%s", lit);
+- buf += len;
+- buflen -= len;
+- }
+- else { /* the rest (i odd) are patterns */
+- 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);
++ pat = &glob->pattern[j];
++ 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;
+- 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';
+@@ -549,34 +552,44 @@ int glob_match_url(char **result, char *filename, URLGlob *glob)
+ unsigned long i;
+ char *ptr = filename;
+ unsigned long num = strtoul(&filename[1], &filename, 10);
+- i = num - 1UL;
++ URLPattern *pat =NULL;
++
++ if(num < glob->size) {
++ num--; /* make it zero based */
++ /* 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(num && (i <= glob->size / 2)) {
+- URLPattern pat = glob->pattern[i];
+- switch (pat.type) {
++ if(pat) {
++ switch (pat->type) {
+ case UPTSet:
+- if(pat.content.Set.elements) {
+- appendthis = pat.content.Set.elements[pat.content.Set.ptr_s];
++ if(pat->content.Set.elements) {
++ appendthis = pat->content.Set.elements[pat->content.Set.ptr_s];
+ appendlen =
+- strlen(pat.content.Set.elements[pat.content.Set.ptr_s]);
++ strlen(pat->content.Set.elements[pat->content.Set.ptr_s]);
+ }
+ break;
+ case UPTCharRange:
+- numbuf[0] = pat.content.CharRange.ptr_c;
++ numbuf[0] = pat->content.CharRange.ptr_c;
+ numbuf[1] = 0;
+ appendthis = numbuf;
+ appendlen = 1;
+ break;
+ case UPTNumRange:
+ snprintf(numbuf, sizeof(numbuf), "%0*d",
+- pat.content.NumRange.padlength,
+- pat.content.NumRange.ptr_n);
++ pat->content.NumRange.padlength,
++ pat->content.NumRange.ptr_n);
+ appendthis = numbuf;
+ appendlen = strlen(numbuf);
+ break;
+ default:
+- printf("internal error: invalid pattern type (%d)\n",
+- (int)pat.type);
++ fprintf(stderr, "internal error: invalid pattern type (%d)\n",
++ (int)pat->type);
+ Curl_safefree(target);
+ return CURLE_FAILED_INIT;
+ }
+diff --git a/src/tool_urlglob.h b/src/tool_urlglob.h
+index 9c08137..e1e9c63 100644
+--- a/src/tool_urlglob.h
++++ b/src/tool_urlglob.h
+@@ -7,7 +7,7 @@
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel at haxx.se>, et al.
++ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel at haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+@@ -31,11 +31,13 @@ typedef enum {
+
+ typedef struct {
+ URLPatternType type;
++ int globindex; /* the number of this particular glob or -1 if not used
++ within {} or [] */
+ union {
+ struct {
+ char **elements;
+- short size;
+- short ptr_s;
++ int size;
++ int ptr_s;
+ } Set;
+ struct {
+ char min_c;
+@@ -44,21 +46,20 @@ typedef struct {
+ int step;
+ } CharRange;
+ struct {
+- int min_n;
+- int max_n;
+- short padlength;
+- int ptr_n;
+- int step;
++ unsigned long min_n;
++ unsigned long max_n;
++ int padlength;
++ unsigned long ptr_n;
++ unsigned long step;
+ } NumRange ;
+ } content;
+ } URLPattern;
+
+ /* the total number of globs supported */
+-#define GLOB_PATTERN_NUM 9
++#define GLOB_PATTERN_NUM 100
+
+ typedef struct {
+- char *literal[10];
+- URLPattern pattern[GLOB_PATTERN_NUM+1];
++ URLPattern pattern[GLOB_PATTERN_NUM];
+ size_t size;
+ size_t urllen;
+ char *glob_buffer;
+@@ -66,7 +67,7 @@ typedef struct {
+ char errormsg[80]; /* error message buffer */
+ } URLGlob;
+
+-int glob_url(URLGlob**, char*, int *, FILE *);
++int glob_url(URLGlob**, char*, unsigned long *, FILE *);
+ int glob_next_url(char **, URLGlob *);
+ int glob_match_url(char **, char*, URLGlob *);
+ void glob_cleanup(URLGlob* glob);
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 0d5c29d..f661b71 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -78,7 +78,7 @@ test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125 \
+ test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 \
+ test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
+ test1208 test1209 test1210 test1211 test1216 test1218 \
+-test1220 test1221 test1222 test1223 test1233 \
++test1220 test1221 test1222 test1223 test1233 test1234 test1235 \
+ test1300 test1301 test1302 test1303 test1304 test1305 \
+ test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
+ test1314 test1315 test1316 test1317 test1318 test1319 test1320 test1321 \
+diff --git a/tests/data/test1234 b/tests/data/test1234
+new file mode 100644
+index 0000000..9d7a79f
+--- /dev/null
++++ b/tests/data/test1234
+@@ -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>
+diff --git a/tests/data/test1235 b/tests/data/test1235
+new file mode 100644
+index 0000000..6c2a6a9
+--- /dev/null
++++ b/tests/data/test1235
+@@ -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>
+diff --git a/tests/data/test87 b/tests/data/test87
+index 40b274b..4e43679 100644
+--- a/tests/data/test87
++++ b/tests/data/test87
+@@ -8,29 +8,51 @@ FAILURE
+ #
+ # 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>
+-none
++http
+ </server>
+ <features>
+ http
+ </features>
+ <name>
+-urlglob with bad -o #[num] usage
++urlglob with out of range -o #[num] usage
+ </name>
+ <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>
+ </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>
+-<errorcode>
+-2
+-</errorcode>
++<file name="log/dumpit#2.dump" [mode="text"]>
++HTTP/1.1 200 OK
++Funny-head: yesyes
++Content-Length: 16
++
++two is nice too
++</file>
+ </verify>
+ </testcase>
+--
+1.8.3.1
+
+
+From 9eefa66b8e003814102daf1b4f1f8cbc4b1c5207 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Fri, 16 Aug 2013 11:52:59 +0200
+Subject: [PATCH 02/13] glob: error out on range overflow
+
+The new multiply() function detects range value overflows. 32bit
+machines will overflow on a 32bit boundary while 64bit hosts support
+ranges up to the full 64 bit range.
+
+Added test 1236 to verify.
+
+Bug: http://curl.haxx.se/bug/view.cgi?id=1267
+Reported-by: Will Dietz
+
+[upstream commit f15a88f2b25ee44d8c8d3bdcf2508fdf50f8b868]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 34 ++++++++++++++++++++++++++++------
+ tests/data/Makefile.am | 2 +-
+ tests/data/test1236 | 33 +++++++++++++++++++++++++++++++++
+ 3 files changed, 62 insertions(+), 7 deletions(-)
+ create mode 100644 tests/data/test1236
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index 0e454c1..ac13d68 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -62,6 +62,19 @@ static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount)
+ return GLOB_OK;
+ }
+
++/* multiply
++ *
++ * Multiplies and checks for overflow.
++ */
++static int multiply(unsigned long *amount, long with)
++{
++ unsigned long sum = *amount * with;
++ if(sum/with != *amount)
++ return 1; /* didn't fit, bail out */
++ *amount = sum;
++ return 0;
++}
++
+ static GlobCode glob_set(URLGlob *glob, char **patternp,
+ size_t pos, unsigned long *amount,
+ int globindex)
+@@ -102,8 +115,11 @@ static GlobCode glob_set(URLGlob *glob, char **patternp,
+ "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);
++ /* add 1 to size since it'll be incremented below */
++ if(multiply(amount, pat->content.Set.size+1)) {
++ strcpy(glob->errormsg, "range overflow!\n");
++ return GLOB_ERROR;
++ }
+ /* fall-through */
+ case ',':
+
+@@ -224,8 +240,11 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ pat->content.CharRange.ptr_c = pat->content.CharRange.min_c = min_c;
+ pat->content.CharRange.max_c = max_c;
+
+- *amount *= (pat->content.CharRange.max_c -
+- pat->content.CharRange.min_c + 1);
++ if(multiply(amount, (pat->content.CharRange.max_c -
++ pat->content.CharRange.min_c + 1))) {
++ strcpy(glob->errormsg, "range overflow!\n");
++ return GLOB_ERROR;
++ }
+ }
+ else if(ISDIGIT(*pattern)) {
+ /* numeric range detected */
+@@ -288,8 +307,11 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ 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);
++ if(multiply(amount, (pat->content.NumRange.max_n -
++ pat->content.NumRange.min_n + 1))) {
++ strcpy(glob->errormsg, "range overflow!\n");
++ return GLOB_ERROR;
++ }
+ }
+ else {
+ snprintf(glob->errormsg, sizeof(glob->errormsg),
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index f661b71..dd7f6f9 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -78,7 +78,7 @@ test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125 \
+ test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 \
+ test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
+ test1208 test1209 test1210 test1211 test1216 test1218 \
+-test1220 test1221 test1222 test1223 test1233 test1234 test1235 \
++test1220 test1221 test1222 test1223 test1233 test1234 test1235 test1236 \
+ test1300 test1301 test1302 test1303 test1304 test1305 \
+ test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
+ test1314 test1315 test1316 test1317 test1318 test1319 test1320 test1321 \
+diff --git a/tests/data/test1236 b/tests/data/test1236
+new file mode 100644
+index 0000000..0829be3
+--- /dev/null
++++ b/tests/data/test1236
+@@ -0,0 +1,33 @@
++<testcase>
++<info>
++<keywords>
++globbing
++FAILURE
++</keywords>
++</info>
++# Server-side
++<reply>
++</reply>
++
++# Client-side
++<client>
++<server>
++none
++</server>
++ <name>
++[] globbing overflowing the range counter
++ </name>
++# 2^62 == 4611686018427387904
++ <command>
++"%HOSTIP:%HTTPPORT/1234[0-1]{" "%HOSTIP:%HTTPPORT/[1-4611686018427387904][1-4611686018427387904]"
++</command>
++</client>
++
++# Verify data after the test has been "shot"
++<verify>
++# 3 == CURLE_URL_MALFORMAT
++<errorcode>
++3
++</errorcode>
++</verify>
++</testcase>
+--
+1.8.3.1
+
+
+From 6e7c5ae88cc75c2f00d8cc837fd1b74ee7a845ca Mon Sep 17 00:00:00 2001
+From: Steve Holme <steve_holme at hotmail.com>
+Date: Mon, 26 Aug 2013 11:41:35 +0100
+Subject: [PATCH 03/13] tool_urlglob.c: Fixed compiler warnings
+
+warning: 'variable' may be used uninitialized in this function
+
+[upstream commit 84789e12fb1d6d22532bd2ce7bfae3a160648a60]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index ac13d68..1644077 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -249,8 +249,8 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ else if(ISDIGIT(*pattern)) {
+ /* numeric range detected */
+ unsigned long min_n;
+- unsigned long max_n;
+- unsigned long step_n;
++ unsigned long max_n = 0;
++ unsigned long step_n = 0;
+ char *endp;
+
+ pat->type = UPTNumRange;
+--
+1.8.3.1
+
+
+From c05a515575dbbefee2eba21fa1c0484fbd90279c Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Fri, 6 Sep 2013 14:12:44 +0200
+Subject: [PATCH 04/13] urlglob: avoid NULL pointer dereference
+
+Thanks to clang-analyzer
+
+[upstream commit 2a7f1425d98919b362fbe979a6428fa9aeebcd78]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index 1644077..7c7bd4b 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -287,7 +287,7 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ }
+ else
+ step_n = 1;
+- if(*endp == ']') {
++ if(endp && (*endp == ']')) {
+ pattern= endp+1;
+ }
+ else
+--
+1.8.3.1
+
+
+From 2e9c57512191dd504902029201bee891fcd83000 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Fri, 6 Sep 2013 14:20:03 +0200
+Subject: [PATCH 05/13] urlglob: avoid error code translation
+
+By using the correct values from the start we don't have to translate
+them!
+
+[upstream commit d6cda9e8ababe633f4b0b58776b1a9c4534e8095]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index 7c7bd4b..647bbc5 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -31,8 +31,8 @@
+
+ typedef enum {
+ GLOB_OK,
+- GLOB_NO_MEM,
+- GLOB_ERROR
++ GLOB_NO_MEM = CURLE_OUT_OF_MEMORY,
++ GLOB_ERROR = CURLE_URL_MALFORMAT
+ } GlobCode;
+
+ void glob_cleanup(URLGlob* glob);
+@@ -423,14 +423,12 @@ int glob_url(URLGlob** glob, char* url, unsigned long *urlnum, FILE *error)
+ else {
+ if(error && glob_expand->errormsg[0]) {
+ /* send error description to the error-stream */
+- fprintf(error, "curl: (%d) [globbing] %s",
+- (res == GLOB_NO_MEM) ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT,
+- glob_expand->errormsg);
++ fprintf(error, "curl: (%d) [globbing] %s", res, glob_expand->errormsg);
+ }
+ /* it failed, we cleanup */
+ glob_cleanup(glob_expand);
+ *urlnum = 1;
+- return (res == GLOB_NO_MEM) ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT;
++ return res;
+ }
+
+ *glob = glob_expand;
+--
+1.8.3.1
+
+
+From a987dc7fa4052dc2de17ad0ef71ac67825ad67ed Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Fri, 6 Sep 2013 23:27:47 +0200
+Subject: [PATCH 06/13] urlglob: improved error messages and column number on
+ bad use
+
+Introduce a convenience macro and keep of the column better so that it
+can point out the offending column better.
+
+Updated test 75 accordingly.
+
+[upstream commit 9fa42beddc5e1f469ddf276a0715f2de82f51b6b]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 145 +++++++++++++++++++++++------------------------------
+ src/tool_urlglob.h | 3 +-
+ tests/data/test75 | 4 +-
+ 3 files changed, 68 insertions(+), 84 deletions(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index 647bbc5..5f94519 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -35,6 +35,9 @@ typedef enum {
+ GLOB_ERROR = CURLE_URL_MALFORMAT
+ } GlobCode;
+
++#define GLOBERROR(string, column, code) \
++ glob->error = string, glob->pos = column, code;
++
+ void glob_cleanup(URLGlob* glob);
+
+ static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount)
+@@ -49,15 +52,12 @@ static GlobCode glob_fixed(URLGlob *glob, unsigned long *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;
+- }
++ if(!pat->content.Set.elements)
++ return GLOBERROR("out of memory", 0, 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;
+- }
++ if(!pat->content.Set.elements[0])
++ return GLOBERROR("out of memory", 0, GLOB_NO_MEM);
+
+ return GLOB_OK;
+ }
+@@ -76,7 +76,7 @@ static int multiply(unsigned long *amount, long with)
+ }
+
+ static GlobCode glob_set(URLGlob *glob, char **patternp,
+- size_t pos, unsigned long *amount,
++ size_t *posp, unsigned long *amount,
+ int globindex)
+ {
+ /* processes a set expression with the point behind the opening '{'
+@@ -87,6 +87,7 @@ static GlobCode glob_set(URLGlob *glob, char **patternp,
+ char *buf = glob->glob_buffer;
+ char *pattern = *patternp;
+ char *opattern = pattern;
++ size_t opos = *posp-1;
+
+ pat = &glob->pattern[glob->size];
+ /* patterns 0,1,2,... correspond to size=1,3,5,... */
+@@ -99,27 +100,20 @@ static GlobCode glob_set(URLGlob *glob, char **patternp,
+ while(!done) {
+ switch (*pattern) {
+ case '\0': /* URL ended while set was still open */
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "unmatched brace at pos %zu\n", pos);
+- return GLOB_ERROR;
++ return GLOBERROR("unmatched brace", opos, GLOB_ERROR);
+
+ case '{':
+ case '[': /* no nested expressions at this time */
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "nested braces not supported at pos %zu\n", pos);
+- return GLOB_ERROR;
++ return GLOBERROR("nested brace", *posp, GLOB_ERROR);
+
+ 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;
+- }
++ if(opattern == pattern)
++ return GLOBERROR("empty string within braces", *posp, GLOB_ERROR);
++
+ /* add 1 to size since it'll be incremented below */
+- if(multiply(amount, pat->content.Set.size+1)) {
+- strcpy(glob->errormsg, "range overflow!\n");
+- return GLOB_ERROR;
+- }
++ if(multiply(amount, pat->content.Set.size+1))
++ return GLOBERROR("range overflow", 0, GLOB_ERROR);
++
+ /* fall-through */
+ case ',':
+
+@@ -127,27 +121,21 @@ static GlobCode glob_set(URLGlob *glob, char **patternp,
+ if(pat->content.Set.elements) {
+ char **new_arr = realloc(pat->content.Set.elements,
+ (pat->content.Set.size + 1) * sizeof(char*));
+- if(!new_arr) {
+- snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
+- return GLOB_NO_MEM;
+- }
++ if(!new_arr)
++ return GLOBERROR("out of memory", 0, GLOB_NO_MEM);
+
+ pat->content.Set.elements = new_arr;
+ }
+ else
+ 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;
+- }
++ if(!pat->content.Set.elements)
++ return GLOBERROR("out of memory", 0, GLOB_NO_MEM);
+
+ pat->content.Set.elements[pat->content.Set.size] =
+ strdup(glob->glob_buffer);
+- if(!pat->content.Set.elements[pat->content.Set.size]) {
+- snprintf(glob->errormsg, sizeof(glob->errormsg), "out of memory\n");
+- return GLOB_NO_MEM;
+- }
++ if(!pat->content.Set.elements[pat->content.Set.size])
++ return GLOBERROR("out of memory", 0, GLOB_NO_MEM);
+ ++pat->content.Set.size;
+
+ if(*pattern == '}') {
+@@ -158,23 +146,21 @@ static GlobCode glob_set(URLGlob *glob, char **patternp,
+
+ buf = glob->glob_buffer;
+ ++pattern;
+- ++pos;
++ ++(*posp);
+ break;
+
+ case ']': /* illegal closing bracket */
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "illegal pattern at pos %zu\n", pos);
+- return GLOB_ERROR;
++ return GLOBERROR("unexpected close bracket", *posp, GLOB_ERROR);
+
+ case '\\': /* escaped character, skip '\' */
+ if(pattern[1]) {
+ ++pattern;
+- ++pos;
++ ++(*posp);
+ }
+ /* intentional fallthrough */
+ default:
+ *buf++ = *pattern++; /* copy character to set element */
+- ++pos;
++ ++(*posp);
+ }
+ }
+
+@@ -183,7 +169,7 @@ static GlobCode glob_set(URLGlob *glob, char **patternp,
+ }
+
+ static GlobCode glob_range(URLGlob *glob, char **patternp,
+- size_t pos, unsigned long *amount,
++ size_t *posp, unsigned long *amount,
+ int globindex)
+ {
+ /* processes a range expression with the point behind the opening '['
+@@ -227,13 +213,12 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ else
+ pattern+=3;
+
++ *posp += (pattern - *patternp);
++
+ if((rc != 2) || (min_c >= max_c) || ((max_c - min_c) > ('z' - 'a')) ||
+- (step < 0) ) {
++ (step < 0) )
+ /* the pattern is not well-formed */
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "error: bad range specification after pos %zu\n", pos);
+- return GLOB_ERROR;
+- }
++ return GLOBERROR("bad range", *posp, GLOB_ERROR);
+
+ /* if there was a ":[num]" thing, use that as step or else use 1 */
+ pat->content.CharRange.step = step;
+@@ -241,10 +226,8 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ pat->content.CharRange.max_c = max_c;
+
+ if(multiply(amount, (pat->content.CharRange.max_c -
+- pat->content.CharRange.min_c + 1))) {
+- strcpy(glob->errormsg, "range overflow!\n");
+- return GLOB_ERROR;
+- }
++ pat->content.CharRange.min_c + 1)))
++ return GLOBERROR("range overflow", *posp, GLOB_ERROR);
+ }
+ else if(ISDIGIT(*pattern)) {
+ /* numeric range detected */
+@@ -295,12 +278,12 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ }
+ }
+
+- if(!endp || (min_n > max_n) || (step_n > (max_n - min_n))) {
++ *posp += (pattern - *patternp);
++
++ 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;
+- }
++ return GLOBERROR("bad range", *posp, 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;
+@@ -308,16 +291,11 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ pat->content.NumRange.step = step_n;
+
+ if(multiply(amount, (pat->content.NumRange.max_n -
+- pat->content.NumRange.min_n + 1))) {
+- strcpy(glob->errormsg, "range overflow!\n");
+- return GLOB_ERROR;
+- }
+- }
+- else {
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "illegal character in range specification at pos %zu\n", pos);
+- return GLOB_ERROR;
++ pat->content.NumRange.min_n + 1)))
++ return GLOBERROR("range overflow", *posp, GLOB_ERROR);
+ }
++ else
++ return GLOBERROR("bad range specification", *posp, GLOB_ERROR);
+
+ *patternp = pattern;
+ return GLOB_OK;
+@@ -336,11 +314,8 @@ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+ 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;
+- }
++ if(*pattern == '}' || *pattern == ']')
++ return GLOBERROR("unmatched close brace/bracket", pos, GLOB_ERROR);
+
+ /* only allow \ to escape known "special letters" */
+ if(*pattern == '\\' &&
+@@ -371,23 +346,21 @@ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+ case '{':
+ /* process set pattern */
+ pattern++;
+- res = glob_set(glob, &pattern, ++pos, amount, globindex++);
++ pos++;
++ res = glob_set(glob, &pattern, &pos, amount, globindex++);
+ break;
+
+ case '[':
+ /* process range pattern */
+ pattern++;
+- res = glob_range(glob, &pattern, ++pos, amount, globindex++);
++ pos++;
++ res = glob_range(glob, &pattern, &pos, amount, globindex++);
+ break;
+ }
+ }
+
+- if(++glob->size > GLOB_PATTERN_NUM) {
+- snprintf(glob->errormsg, sizeof(glob->errormsg),
+- "too many globs used\n");
+- return GLOB_ERROR;
+- }
+-
++ if(++glob->size > GLOB_PATTERN_NUM)
++ return GLOBERROR("too many globs", pos, GLOB_ERROR);
+ }
+ return res;
+ }
+@@ -421,9 +394,19 @@ int glob_url(URLGlob** glob, char* url, unsigned long *urlnum, FILE *error)
+ if(!res)
+ *urlnum = amount;
+ else {
+- if(error && glob_expand->errormsg[0]) {
++ if(error && glob_expand->error) {
++ char text[128];
++ const char *t;
++ if(glob_expand->pos) {
++ snprintf(text, sizeof(text), "%s in column %zu", glob_expand->error,
++ glob_expand->pos);
++ t = text;
++ }
++ else
++ t = glob_expand->error;
++
+ /* send error description to the error-stream */
+- fprintf(error, "curl: (%d) [globbing] %s", res, glob_expand->errormsg);
++ fprintf(error, "curl: (%d) [globbing] %s\n", res, t);
+ }
+ /* it failed, we cleanup */
+ glob_cleanup(glob_expand);
+diff --git a/src/tool_urlglob.h b/src/tool_urlglob.h
+index e1e9c63..9fa6f83 100644
+--- a/src/tool_urlglob.h
++++ b/src/tool_urlglob.h
+@@ -64,7 +64,8 @@ typedef struct {
+ size_t urllen;
+ char *glob_buffer;
+ char beenhere;
+- char errormsg[80]; /* error message buffer */
++ const char *error; /* error message */
++ size_t pos; /* column position of error or 0 */
+ } URLGlob;
+
+ int glob_url(URLGlob**, char*, unsigned long *, FILE *);
+diff --git a/tests/data/test75 b/tests/data/test75
+index 07f6990..a9f4d16 100644
+--- a/tests/data/test75
++++ b/tests/data/test75
+@@ -24,7 +24,7 @@ http
+ HTTP, urlglob retrieval with bad range
+ </name>
+ <command option="no-output">
+-"http://%HOSTIP:%HTTPPORT/[2-1]" -o "log/weee#1.dump" --stderr -
++"http://a-site-never-accessed.example.org/[2-1]" -o "log/weee#1.dump" --stderr -
+ </command>
+ # The error message on stdout implicitly depends on the length of the
+ # URL, so refuse to run if the length is unexpected.
+@@ -43,7 +43,7 @@ perl %SRCDIR/libtest/test75.pl http://%HOSTIP:%HTTPPORT/ 22
+ 3
+ </errorcode>
+ <stdout mode="text">
+-curl: (3) [globbing] error: bad range specification after pos 24
++curl: (3) [globbing] bad range in column 47
+ </stdout>
+ </verify>
+ </testcase>
+--
+1.8.3.1
+
+
+From 8f377feb97183859a8c93e19fdb82351d45364b1 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Tue, 22 Oct 2013 00:01:17 +0200
+Subject: [PATCH 07/13] glob: fix regression from commit 5ca96cb844
+
+Plain strings after glob ranges/lists weren't treated correctly but
+caused broken URLs to get used.
+
+Reported-by: Javier Barroso
+
+[upstream commit 867b52a7ac528cee42aa663004d57d7ebdab5ecc]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index 5f94519..4208327 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -40,7 +40,8 @@ typedef enum {
+
+ void glob_cleanup(URLGlob* glob);
+
+-static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount)
++static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount,
++ char *fixed, size_t len)
+ {
+ URLPattern *pat = &glob->pattern[glob->size];
+ pat->type = UPTSet;
+@@ -55,10 +56,13 @@ static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount)
+ if(!pat->content.Set.elements)
+ return GLOBERROR("out of memory", 0, GLOB_NO_MEM);
+
+- pat->content.Set.elements[0] = strdup(glob->glob_buffer);
++ pat->content.Set.elements[0] = malloc(len+1);
+ if(!pat->content.Set.elements[0])
+ return GLOBERROR("out of memory", 0, GLOB_NO_MEM);
+
++ memcpy(pat->content.Set.elements[0], fixed, len);
++ pat->content.Set.elements[0][len] = 0;
++
+ return GLOB_OK;
+ }
+
+@@ -307,11 +311,11 @@ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+ /* processes a literal string component of a URL
+ special characters '{' and '[' branch to set/range processing functions
+ */
+- char* buf = glob->glob_buffer;
+ GlobCode res = GLOB_OK;
+ int globindex = 0; /* count "actual" globs */
+
+ while(*pattern && !res) {
++ char *buf = glob->glob_buffer;
+ int sublen = 0;
+ while(*pattern && *pattern != '{' && *pattern != '[') {
+ if(*pattern == '}' || *pattern == ']')
+@@ -333,7 +337,7 @@ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+ if(sublen) {
+ /* we got a literal string, add it as a single-item list */
+ *buf = '\0';
+- res = glob_fixed(glob, amount);
++ res = glob_fixed(glob, amount, glob->glob_buffer, sublen);
+ }
+ else {
+ if(!*amount)
+--
+1.8.3.1
+
+
+From c02ec7eeb1406eec37420766bdd310496772c874 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Sun, 3 Nov 2013 10:08:10 +0100
+Subject: [PATCH 08/13] glob_range: pass the closing bracket for a-z ranges
+
+Regression since commit 5ca96cb844102 (release in 7.33.0)
+
+Reported-by: Marcin Gryszkalis
+
+[upstream commit bce03fe14452da555468616db52003ba05c0e288]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index 4208327..aa87e5a 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -215,7 +215,7 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ }
+ }
+ else
+- pattern+=3;
++ pattern += 4;
+
+ *posp += (pattern - *patternp);
+
+--
+1.8.3.1
+
+
+From d7f9c9489d0f6024a47f1311d25b42b957d556a5 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Thu, 28 Nov 2013 23:31:31 +0100
+Subject: [PATCH 09/13] globbing: curl glob counter mismatch with {} list use
+
+The "fixed string" function wrongly bumped the "urlnum" counter which
+made curl output the total number of URLs wrong when using
+{one,two,three} lists in globs.
+
+Reported-by: Michael-O
+Bug: http://curl.haxx.se/bug/view.cgi?id=1305
+
+[upstream commit 0dd6522036daa8468c55c4a0c6b70d1c510e879a]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 12 ++++--------
+ 1 file changed, 4 insertions(+), 8 deletions(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index aa87e5a..ec5014b 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -40,8 +40,7 @@ typedef enum {
+
+ void glob_cleanup(URLGlob* glob);
+
+-static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount,
+- char *fixed, size_t len)
++static GlobCode glob_fixed(URLGlob *glob, char *fixed, size_t len)
+ {
+ URLPattern *pat = &glob->pattern[glob->size];
+ pat->type = UPTSet;
+@@ -49,8 +48,6 @@ static GlobCode glob_fixed(URLGlob *glob, unsigned long *amount,
+ pat->content.Set.ptr_s = 0;
+ pat->globindex = -1;
+
+- (*amount)++;
+-
+ pat->content.Set.elements = malloc(sizeof(char*));
+
+ if(!pat->content.Set.elements)
+@@ -314,6 +311,8 @@ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+ GlobCode res = GLOB_OK;
+ int globindex = 0; /* count "actual" globs */
+
++ *amount = 1;
++
+ while(*pattern && !res) {
+ char *buf = glob->glob_buffer;
+ int sublen = 0;
+@@ -337,12 +336,9 @@ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+ if(sublen) {
+ /* we got a literal string, add it as a single-item list */
+ *buf = '\0';
+- res = glob_fixed(glob, amount, glob->glob_buffer, sublen);
++ res = glob_fixed(glob, glob->glob_buffer, sublen);
+ }
+ else {
+- if(!*amount)
+- *amount = 1;
+-
+ switch (*pattern) {
+ case '\0': /* done */
+ break;
+--
+1.8.3.1
+
+
+From 584265e9422e98857bbc4e050c9e9cfb3ec08209 Mon Sep 17 00:00:00 2001
+From: Paul Marks <pmarks at google.com>
+Date: Sun, 30 Mar 2014 07:50:37 +0200
+Subject: [PATCH 10/13] curl: stop interpreting IPv6 literals as glob patterns.
+
+This makes it possible to fetch from an IPv6 literal without specifying
+the -g option. Globbing remains available elsehwere in the URL.
+
+For example:
+ curl http://[::1]/file[1-3].txt
+
+This creates no ambiguity, because there is no overlap between the
+syntax of valid globs and valid IPv6 literals. Globs contain hyphens
+and at most 1 colon, while IPv6 literals have no hyphens, and at least 2
+colons.
+
+The peek_ipv6() parser simply whitelists a set of characters and counts
+colons, because the real validation happens later on. The character set
+includes A-Z, in case someone decides to implement support for scopes
+like [fe80::1%25eth0] in the future.
+
+Signed-off-by: Paul Marks <pmarks at google.com>
+
+[upstream commit 0bc4938eecccefdf8906bf9c488e4cd9c8467e99]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ src/tool_urlglob.c | 48 +++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 45 insertions(+), 3 deletions(-)
+
+diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c
+index ec5014b..943e0ab 100644
+--- a/src/tool_urlglob.c
++++ b/src/tool_urlglob.c
+@@ -5,7 +5,7 @@
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel at haxx.se>, et al.
++ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel at haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+@@ -302,6 +302,36 @@ static GlobCode glob_range(URLGlob *glob, char **patternp,
+ return GLOB_OK;
+ }
+
++static bool peek_ipv6(const char *str, size_t *skip)
++{
++ /*
++ * Scan for a potential IPv6 literal.
++ * - Valid globs contain a hyphen and <= 1 colon.
++ * - IPv6 literals contain no hyphens and >= 2 colons.
++ */
++ size_t i = 0;
++ size_t colons = 0;
++ if(str[i++] != '[') {
++ return FALSE;
++ }
++ for(;;) {
++ const char c = str[i++];
++ if(ISALNUM(c) || c == '.' || c == '%') {
++ /* ok */
++ }
++ else if(c == ':') {
++ colons++;
++ }
++ else if(c == ']') {
++ *skip = i;
++ return colons >= 2;
++ }
++ else {
++ return FALSE;
++ }
++ }
++}
++
+ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+ size_t pos, unsigned long *amount)
+ {
+@@ -315,8 +345,20 @@ static GlobCode glob_parse(URLGlob *glob, char *pattern,
+
+ while(*pattern && !res) {
+ char *buf = glob->glob_buffer;
+- int sublen = 0;
+- while(*pattern && *pattern != '{' && *pattern != '[') {
++ size_t sublen = 0;
++ while(*pattern && *pattern != '{') {
++ if(*pattern == '[') {
++ /* Skip over potential IPv6 literals. */
++ size_t skip;
++ if(peek_ipv6(pattern, &skip)) {
++ memcpy(buf, pattern, skip);
++ buf += skip;
++ pattern += skip;
++ sublen += skip;
++ continue;
++ }
++ break;
++ }
+ if(*pattern == '}' || *pattern == ']')
+ return GLOBERROR("unmatched close brace/bracket", pos, GLOB_ERROR);
+
+--
+1.8.3.1
+
+
+From 864cfb5e9ccfba43b00ce9e1ee6ecc4a4329447b Mon Sep 17 00:00:00 2001
+From: Till Maas <opensource at till.name>
+Date: Sat, 15 Mar 2014 22:42:50 +0100
+Subject: [PATCH 11/13] URL parser: IPv6 zone identifiers are now supported
+
+[upstream commit 9317eced98408c7fefa6dd5f1559050e1ec8a3b7]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ docs/KNOWN_BUGS | 12 +---------
+ docs/MANUAL | 6 ++---
+ lib/url.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++----------
+ 3 files changed, 61 insertions(+), 26 deletions(-)
+
+diff --git a/docs/KNOWN_BUGS b/docs/KNOWN_BUGS
+index 7bd6e6e..34ea148 100644
+--- a/docs/KNOWN_BUGS
++++ b/docs/KNOWN_BUGS
+@@ -146,17 +146,7 @@ may have been fixed since this was written!
+ --cflags suffers from the same effects with CFLAGS/CPPFLAGS.
+
+ 30. You need to use -g to the command line tool in order to use RFC2732-style
+- IPv6 numerical addresses in URLs.
+-
+-29. IPv6 URLs with zone ID is not nicely supported.
+- http://www.ietf.org/internet-drafts/draft-fenner-literal-zone-02.txt (expired)
+- specifies the use of a plus sign instead of a percent when specifying zone
+- IDs in URLs to get around the problem of percent signs being
+- special. According to the reporter, Firefox deals with the URL _with_ a
+- percent letter (which seems like a blatant URL spec violation).
+- libcurl supports zone IDs where the percent sign is URL-escaped (i.e. %25).
+-
+- See http://curl.haxx.se/bug/view.cgi?id=1371118
++ or RFC6874-style IPv6 numerical addresses in URLs.
+
+ 26. NTLM authentication using SSPI (on Windows) when (lib)curl is running in
+ "system context" will make it use wrong(?) user name - at least when compared
+diff --git a/docs/MANUAL b/docs/MANUAL
+index 4ad2e13..da8f602 100644
+--- a/docs/MANUAL
++++ b/docs/MANUAL
+@@ -956,9 +956,9 @@ IPv6
+ When this style is used, the -g option must be given to stop curl from
+ interpreting the square brackets as special globbing characters. Link local
+ and site local addresses including a scope identifier, such as fe80::1234%1,
+- may also be used, but the scope portion must be numeric and the percent
+- character must be URL escaped. The previous example in an SFTP URL might
+- look like:
++ may also be used, but the scope portion must be numeric or match an existing
++ network interface on Linux and the percent character must be URL escaped. The
++ previous example in an SFTP URL might look like:
+
+ sftp://[fe80::1234%251]/
+
+diff --git a/lib/url.c b/lib/url.c
+index 0174ff4..2642f92 100644
+--- a/lib/url.c
++++ b/lib/url.c
+@@ -3758,23 +3758,59 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data,
+ if(result != CURLE_OK)
+ return result;
+
+- if(conn->host.name[0] == '[') {
++ if(conn->host.name[0] == '[' && !data->state.this_is_a_follow) {
+ /* This looks like an IPv6 address literal. See if there is an address
+- scope. */
+- char *percent = strstr (conn->host.name, "%25");
++ scope if there is no location header */
++ char *percent = strchr(conn->host.name, '%');
+ if(percent) {
++ unsigned int identifier_offset = 3;
+ char *endp;
+- unsigned long scope = strtoul (percent + 3, &endp, 10);
++ unsigned long scope;
++ if(strncmp("%25", percent, 3) != 0) {
++ infof(data,
++ "Please URL encode %% as %%25, see RFC 6874.\n");
++ identifier_offset = 1;
++ }
++ scope = strtoul(percent + identifier_offset, &endp, 10);
+ if(*endp == ']') {
+ /* The address scope was well formed. Knock it out of the
+ hostname. */
+ memmove(percent, endp, strlen(endp)+1);
+- if(!data->state.this_is_a_follow)
+- /* Don't honour a scope given in a Location: header */
+- conn->scope = (unsigned int)scope;
++ conn->scope = (unsigned int)scope;
++ }
++ else {
++ /* Zone identifier is not numeric */
++#ifdef HAVE_NET_IF_H
++ char ifname[IFNAMSIZ + 2];
++ char *square_bracket;
++ unsigned int scopeidx = 0;
++ strncpy(ifname, percent + identifier_offset, IFNAMSIZ + 2);
++ /* Ensure nullbyte termination */
++ ifname[IFNAMSIZ + 1] = '\0';
++ square_bracket = strchr(ifname, ']');
++ if(square_bracket) {
++ /* Remove ']' */
++ *square_bracket = '\0';
++ scopeidx = if_nametoindex(ifname);
++ if(scopeidx == 0) {
++ infof(data, "Invalid network interface: %s; %s\n", ifname,
++ strerror(errno));
++ }
++ }
++ if(scopeidx > 0) {
++ /* Remove zone identifier from hostname */
++ memmove(percent,
++ percent + identifier_offset + strlen(ifname),
++ identifier_offset + strlen(ifname));
++ conn->scope = scopeidx;
++ }
++ else {
++#endif /* HAVE_NET_IF_H */
++ infof(data, "Invalid IPv6 address format\n");
++#ifdef HAVE_NET_IF_H
++ }
++#endif /* HAVE_NET_IF_H */
+ }
+- else
+- infof(data, "Invalid IPv6 address format\n");
+ }
+ }
+
+@@ -4123,12 +4159,21 @@ static CURLcode parse_proxy(struct SessionHandle *data,
+ /* start scanning for port number at this point */
+ portptr = proxyptr;
+
+- /* detect and extract RFC2732-style IPv6-addresses */
++ /* detect and extract RFC6874-style IPv6-addresses */
+ if(*proxyptr == '[') {
+ char *ptr = ++proxyptr; /* advance beyond the initial bracket */
+- while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '%') ||
+- (*ptr == '.')))
++ while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
++ ptr++;
++ if(*ptr == '%') {
++ /* There might be a zone identifier */
++ if(strncmp("%25", ptr, 3))
++ infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
+ ptr++;
++ /* Allow unresered characters as defined in RFC 3986 */
++ while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
++ (*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
++ ptr++;
++ }
+ if(*ptr == ']')
+ /* yeps, it ended nicely with a bracket as well */
+ *ptr++ = 0;
+--
+1.8.3.1
+
+
+From a842be97b0d8d3e1bf323bc88f2eceecd2d42087 Mon Sep 17 00:00:00 2001
+From: Dan Fandrich <dan at coneharvesters.com>
+Date: Mon, 31 Mar 2014 09:02:55 +0200
+Subject: [PATCH 12/13] docs: Removed mention of -g hack when using IPv6
+ literals
+
+This limitation was removed in commit 0bc4938e
+
+[upstream commit ed4972ffdb11fc62a8bae33ff4eafbd73973ad9f]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ docs/MANUAL | 2 +-
+ docs/TODO | 8 --------
+ 2 files changed, 1 insertion(+), 9 deletions(-)
+
+diff --git a/docs/MANUAL b/docs/MANUAL
+index da8f602..11960e1 100644
+--- a/docs/MANUAL
++++ b/docs/MANUAL
+@@ -50,7 +50,7 @@ SIMPLE USAGE
+
+ Get the main page from an IPv6 web server:
+
+- curl -g "http://[2001:1890:1112:1::20]/"
++ curl "http://[2001:1890:1112:1::20]/"
+
+ DOWNLOAD TO A FILE
+
+diff --git a/docs/TODO b/docs/TODO
+index 8b133dc..825b7af 100644
+--- a/docs/TODO
++++ b/docs/TODO
+@@ -87,7 +87,6 @@
+ 15.5 provide formpost headers
+ 15.6 url-specific options
+ 15.7 warning when setting an option
+- 15.8 IPv6 addresses with globbing
+
+ 16. Build
+ 16.1 roffit
+@@ -479,13 +478,6 @@ to provide the data to send.
+ This can be useful to tell when support for a particular feature hasn't been
+ compiled into the library.
+
+-15.8 IPv6 addresses with globbing
+-
+- Currently the command line client needs to get url globbing disabled (with
+- -g) for it to support IPv6 numerical addresses. This is a rather silly flaw
+- that should be corrected. It probably involves a smarter detection of the
+- '[' and ']' letters.
+-
+ 16. Build
+
+ 16.1 roffit
+--
+1.8.3.1
+
+
+From 4dd964688eda563ae960a9e3e573c295ca728efa Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel at haxx.se>
+Date: Mon, 31 Mar 2014 09:35:32 +0200
+Subject: [PATCH 13/13] ipv6: strip off zone identifiers in redirects too
+
+Follow up to 9317eced984 makes test 1056 work again.
+
+[upstream commit 13682d1a24bba5386530805d8fbcf987b19c3552]
+
+Signed-off-by: Kamil Dudka <kdudka at redhat.com>
+---
+ lib/url.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/url.c b/lib/url.c
+index 2642f92..c551262 100644
+--- a/lib/url.c
++++ b/lib/url.c
+@@ -3758,7 +3758,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data,
+ if(result != CURLE_OK)
+ return result;
+
+- if(conn->host.name[0] == '[' && !data->state.this_is_a_follow) {
++ if(conn->host.name[0] == '[') {
+ /* This looks like an IPv6 address literal. See if there is an address
+ scope if there is no location header */
+ char *percent = strchr(conn->host.name, '%');
+--
+1.8.3.1
+
diff --git a/curl.spec b/curl.spec
index 75fe3a8..e3277c1 100644
--- a/curl.spec
+++ b/curl.spec
@@ -1,7 +1,7 @@
Summary: A utility for getting files from remote servers (FTP, HTTP, and others)
Name: curl
Version: 7.29.0
-Release: 18%{?dist}
+Release: 19%{?dist}
License: MIT
Group: Applications/Internet
Source: http://curl.haxx.se/download/%{name}-%{version}.tar.lzma
@@ -61,6 +61,9 @@ Patch18: 0018-curl-7.29.0-517b06d6.patch
# nss: implement non-blocking SSL handshake
Patch19: 0019-curl-7.29.0-8868a226.patch
+# extend URL parser to support IPv6 zone identifiers (#680996)
+Patch20: 0020-curl-7.29.0-9317eced.patch
+
# patch making libcurl multilib ready
Patch101: 0101-curl-7.29.0-multilib.patch
@@ -187,6 +190,7 @@ documentation of the library, too.
%patch17 -p1
%patch18 -p1
%patch19 -p1
+%patch20 -p1
# Fedora patches
%patch101 -p1
@@ -307,6 +311,9 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/aclocal/libcurl.m4
%changelog
+* Sat May 10 2014 Kamil Dudka <kdudka at redhat.com> 7.29.0-19
+- extend URL parser to support IPv6 zone identifiers (#680996)
+
* Fri Apr 25 2014 Kamil Dudka <kdudka at redhat.com> 7.29.0-18
- nss: implement non-blocking SSL handshake
More information about the scm-commits
mailing list