[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