CVE-2019-5435: An integer overflow found in /lib/urlapi.c
What the report claims
An integer overflow in curl's seturl function may cause a heap-based buffer overrun when processing URLs up to 2GB in length on 32-bit systems.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2019-5435 Integer overflows in URL parser
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The code supports the reported flaw. A maintainer should review its impact and decide what to do next.
Why this finding?
- The integer overflow in urllen * 2 + 2 is real and present in the provided code at lib/urlapi.c:645.
- No length check guards the multiplication; the call chain curl_url_set -> parseurl -> seturl is reachable with an absolute URL.
- On 32-bit systems the wrap causes malloc to return a small buffer, and subsequent writes using the original urllen overrun the heap.
- Practical exploitability on 32-bit is limited by address-space constraints (a 2+ GB string is hard to allocate), but the flaw is a classic heap overflow via integer wrap and similar issues (e.g., CVE-2018-14618) have been treated as security vulnerabilities in curl.
How this analysis ran 5 claims · 12 citations verified
Checked against curl master on 2019-04-24 (e7d3f65ab95a). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Review a draft reply to the reporter
Edit and verify before sending.
Thank you for the report. The integer overflow at lib/urlapi.c:645 (urllen * 2 + 2) is real and reachable via curl_url_set with an absolute URL. On 32-bit systems the multiplication wraps, malloc returns an undersized buffer, and later writes overrun the heap. While a 2+ GB URL is difficult to supply on 32-bit due to address-space limits, the flaw is a genuine heap buffer overrun and matches patterns we have fixed before (e.g., CVE-2018-14618). We will address it.
Inspect all claims and source code 5 claims · 9 code excerpts
Report claims
urllen was multiplied by 2 and then passed to malloc, causing integer overflow when url length reaches 2GB on 32-bit OS
Read the reasoning
The code computes urllen * 2 + 2 in size_t and passes it to malloc. On 32-bit systems where size_t is 32-bit unsigned, this multiplication wraps modulo 2^32 when urllen exceeds ~2.1GB, causing malloc to allocate a small buffer instead of the intended large one.
in function seturl in /lib/urlapi.c
Read the reasoning
The function seturl is defined at line 622 in lib/urlapi.c and contains the allocation at line 645.
when a URL of length 2GB is processed on a 32-bit OS
Read the reasoning
No bounds check on the full URL length exists before the multiplication. The trigger condition (2GB URL on 32-bit) is theoretically reachable via the public API curl_url_set with an absolute URL.
may lead to a crash or buffer overrun
Read the reasoning
After the integer overflow, malloc returns a tiny buffer. Subsequent pointer arithmetic (path[urllen + 1]) and writes (hostname[0] = 0) use the original huge urllen value, causing out-of-bounds heap access — a buffer overrun that can crash or corrupt memory.
use curl_url_set with an extreme long URL to trigger parseurl and seturl
Read the reasoning
The call chain curl_url_set -> parseurl -> seturl is present and reachable when the input is an absolute URL. An attacker controlling the URL string passed to curl_url_set can trigger the flaw.
Read the original report
Source code
| 622 | static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) |
| C2: static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) | |
| 623 | { |
| 624 | char *path; |
| 625 | bool path_alloced = FALSE; |
| 626 | char *hostname; |
| 627 | char *query = NULL; |
| 628 | char *fragment = NULL; |
| 629 | CURLUcode result; |
| 630 | bool url_has_scheme = FALSE; |
| 631 | char schemebuf[MAX_SCHEME_LEN]; |
| 632 | char *schemep = NULL; |
| 633 | size_t schemelen = 0; |
| 634 | size_t urllen; |
| C1: urllen declared as size_t | |
| 635 | const struct Curl_handler *h = NULL; |
| 636 | |
| 637 | if(!url) |
| 638 | return CURLUE_MALFORMED_INPUT; |
| 639 | |
| 640 | /************************************************************* |
| 641 | * Parse the URL. |
| 642 | ************************************************************/ |
| 643 | /* allocate scratch area */ |
| 644 | urllen = strlen(url); |
| C1: urllen = strlen(url); returns size_t | |
| C3: urllen = strlen(url); no length check before multiplication | |
| 645 | path = u->scratch = malloc(urllen * 2 + 2); |
| C1: malloc(urllen * 2 + 2); multiplication in size_t arithmetic, wraps on 32-bit when urllen > ~2GB | |
| C4: malloc(urllen * 2 + 2) returns undersized buffer after wrap | |
| 646 | if(!path) |
| 647 | return CURLUE_OUT_OF_MEMORY; |
| 648 | |
| 649 | hostname = &path[urllen + 1]; |
| C4: hostname = &path[urllen + 1]; pointer arithmetic with huge urllen | |
| 650 | hostname[0] = 0; |
| C4: hostname[0] = 0; writes to wrapped pointer location | |
| 651 | |
| 652 | if(Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf))) { |
| 653 | url_has_scheme = TRUE; |
| 654 | schemelen = strlen(schemebuf); |
| 655 | } |
| 656 | |
| 657 | /* handle the file: scheme */ |
| 658 | if(url_has_scheme && strcasecompare(schemebuf, "file")) { |
| 659 | /* path has been allocated large enough to hold this */ |
| 660 | strcpy(path, &url[5]); |
| 661 | |
| 662 | hostname = NULL; /* no host for file: URLs */ |
| 663 | u->scheme = strdup("file"); |
| 664 | if(!u->scheme) |
| 665 | return CURLUE_OUT_OF_MEMORY; |
| 666 | |
| 667 | /* Extra handling URLs with an authority component (i.e. that start with |
| 668 | * "file://") |
| 669 | * |
| 670 | * We allow omitted hostname (e.g. file:/<path>) -- valid according to |
| 671 | * RFC 8089, but not the (current) WHAT-WG URL spec. |
| 672 | */ |
| 673 | if(path[0] == '/' && path[1] == '/') { |
| 674 | /* swallow the two slashes */ |
| 675 | char *ptr = &path[2]; |
| 676 | |
| 677 | /* |
| 678 | * According to RFC 8089, a file: URL can be reliably dereferenced if: |
| 679 | * |
| 680 | * o it has no/blank hostname, or |
| 681 | * |
| 682 | * o the hostname matches "localhost" (case-insensitively), or |
| 683 | * |
| 684 | * o the hostname is a FQDN that resolves to this machine. |
| 685 | * |
| 686 | * For brevity, we only consider URLs with empty, "localhost", or |
| 687 | * "127.0.0.1" hostnames as local. |
| 688 | * |
| 689 | * Additionally, there is an exception for URLs with a Windows drive |
| 690 | * letter in the authority (which was accidentally omitted from RFC 8089 |
| 691 | * Appendix E, but believe me, it was meant to be there. --MK) |
| 692 | */ |
| 693 | if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { |
| 694 | /* the URL includes a host name, it must match "localhost" or |
| 695 | "127.0.0.1" to be valid */ |
| 696 | if(!checkprefix("localhost/", ptr) && |
| 697 | !checkprefix("127.0.0.1/", ptr)) { |
| 698 | /* Invalid file://hostname/, expected localhost or 127.0.0.1 or |
| 699 | none */ |
| 700 | return CURLUE_MALFORMED_INPUT; |
| 701 | } |
| 702 | ptr += 9; /* now points to the slash after the host */ |
| 703 | } |
| 704 | |
| 705 | path = ptr; |
| 706 | } |
| 707 | |
| 708 | #if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) |
| 709 | /* Don't allow Windows drive letters when not in Windows. |
| 710 | * This catches both "file:/c:" and "file:c:" */ |
| 711 | if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || |
| 712 | STARTS_WITH_URL_DRIVE_PREFIX(path)) { |
| 713 | /* File drive letters are only accepted in MSDOS/Windows */ |
| 714 | return CURLUE_MALFORMED_INPUT; |
| 715 | } |
| 716 | #else |
| 717 | /* If the path starts with a slash and a drive letter, ditch the slash */ |
| 718 | if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { |
| 719 | /* This cannot be done with strcpy, as the memory chunks overlap! */ |
| 720 | memmove(path, &path[1], strlen(&path[1]) + 1); |
| 721 | } |
| 722 | #endif |
| 723 | |
| 724 | } |
| 725 | else { |
| 726 | /* clear path */ |
| 727 | const char *p; |
| 728 | const char *hostp; |
| 729 | size_t len; |
| 730 | path[0] = 0; |
| 731 | |
| 732 | if(url_has_scheme) { |
| 733 | int i = 0; |
| 734 | p = &url[schemelen + 1]; |
| 735 | while(p && (*p == '/') && (i < 4)) { |
| 736 | p++; |
| 737 | i++; |
| 738 | } |
| 739 | if((i < 1) || (i>3)) |
| 740 | /* less than one or more than three slashes */ |
| 741 | return CURLUE_MALFORMED_INPUT; |
| 742 | |
| 743 | schemep = schemebuf; |
| 744 | if(!Curl_builtin_scheme(schemep) && |
| 745 | !(flags & CURLU_NON_SUPPORT_SCHEME)) |
| 746 | return CURLUE_UNSUPPORTED_SCHEME; |
| 747 | |
| 748 | if(junkscan(schemep)) |
| 749 | return CURLUE_MALFORMED_INPUT; |
| 750 | } |
| 751 | else { |
| 752 | /* no scheme! */ |
| 753 | |
| 754 | if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) |
| 755 | return CURLUE_MALFORMED_INPUT; |
| 756 | if(flags & CURLU_DEFAULT_SCHEME) |
| 757 | schemep = (char *) DEFAULT_SCHEME; |
| 758 | |
| 759 | /* |
| 760 | * The URL was badly formatted, let's try without scheme specified. |
| 761 | */ |
| 762 | p = url; |
| 763 | } |
| 764 | hostp = p; /* host name starts here */ |
| 765 | |
| 766 | while(*p && !HOSTNAME_END(*p)) /* find end of host name */ |
| 767 | p++; |
| 768 | |
| 769 | len = p - hostp; |
| 770 | if(!len) |
| 771 | return CURLUE_MALFORMED_INPUT; |
| 772 | |
| 773 | memcpy(hostname, hostp, len); |
| 774 | hostname[len] = 0; |
| 775 | |
| 776 | if((flags & CURLU_GUESS_SCHEME) && !schemep) { |
| 777 | /* legacy curl-style guess based on host name */ |
| 778 | if(checkprefix("ftp.", hostname)) |
| 779 | schemep = (char *)"ftp"; |
| 780 | else if(checkprefix("dict.", hostname)) |
| 781 | schemep = (char *)"dict"; |
| 782 | else if(checkprefix("ldap.", hostname)) |
| 1114 | CURLUcode curl_url_set(CURLU *u, CURLUPart what, |
| C5: CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags) | |
| 1115 | const char *part, unsigned int flags) |
| 1116 | { |
| 1117 | char **storep = NULL; |
| 1118 | long port = 0; |
| 1119 | bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; |
| 1120 | bool plusencode = FALSE; |
| 1121 | bool urlskipslash = FALSE; |
| 1122 | bool appendquery = FALSE; |
| 1123 | bool equalsencode = FALSE; |
| 1124 | |
| 1125 | if(!u) |
| 1126 | return CURLUE_BAD_HANDLE; |
| 1127 | if(!part) { |
| 1128 | /* setting a part to NULL clears it */ |
| 1129 | switch(what) { |
| 1130 | case CURLUPART_URL: |
| 1131 | break; |
| 1132 | case CURLUPART_SCHEME: |
| 1133 | storep = &u->scheme; |
| 1134 | break; |
| 1135 | case CURLUPART_USER: |
| 1136 | storep = &u->user; |
| 1137 | break; |
| 1138 | case CURLUPART_PASSWORD: |
| 1139 | storep = &u->password; |
| 1140 | break; |
| 1141 | case CURLUPART_OPTIONS: |
| 1142 | storep = &u->options; |
| 1143 | break; |
| 1144 | case CURLUPART_HOST: |
| 1145 | storep = &u->host; |
| 1146 | break; |
| 1147 | case CURLUPART_PORT: |
| 1148 | u->portnum = 0; |
| 1149 | storep = &u->port; |
| 1150 | break; |
| 1151 | case CURLUPART_PATH: |
| 1152 | storep = &u->path; |
| 1153 | break; |
| 1154 | case CURLUPART_QUERY: |
| 1155 | storep = &u->query; |
| 1156 | break; |
| 1157 | case CURLUPART_FRAGMENT: |
| 1158 | storep = &u->fragment; |
| 1159 | break; |
| 1160 | default: |
| 1161 | return CURLUE_UNKNOWN_PART; |
| 1162 | } |
| 1163 | if(storep && *storep) { |
| 1164 | free(*storep); |
| 1165 | *storep = NULL; |
| 1166 | } |
| 1167 | return CURLUE_OK; |
| 1168 | } |
| 1169 | |
| 1170 | switch(what) { |
| 1171 | case CURLUPART_SCHEME: |
| 1172 | if(!(flags & CURLU_NON_SUPPORT_SCHEME) && |
| 1173 | /* verify that it is a fine scheme */ |
| 1174 | !Curl_builtin_scheme(part)) |
| 1175 | return CURLUE_UNSUPPORTED_SCHEME; |
| 1176 | storep = &u->scheme; |
| 1177 | urlencode = FALSE; /* never */ |
| 1178 | break; |
| 1179 | case CURLUPART_USER: |
| 1180 | storep = &u->user; |
| 1181 | break; |
| 1182 | case CURLUPART_PASSWORD: |
| 1183 | storep = &u->password; |
| 1184 | break; |
| 1185 | case CURLUPART_OPTIONS: |
| 1186 | storep = &u->options; |
| 1187 | break; |
| 1188 | case CURLUPART_HOST: |
| 1189 | storep = &u->host; |
| 1190 | break; |
| 1191 | case CURLUPART_PORT: |
| 1192 | { |
| 1193 | char *endp; |
| 1194 | urlencode = FALSE; /* never */ |
| 1195 | port = strtol(part, &endp, 10); /* Port number must be decimal */ |
| 1196 | if((port <= 0) || (port > 0xffff)) |
| 1197 | return CURLUE_BAD_PORT_NUMBER; |
| 1198 | if(*endp) |
| 1199 | /* weirdly provided number, not good! */ |
| 1200 | return CURLUE_MALFORMED_INPUT; |
| 1201 | storep = &u->port; |
| 1202 | } |
| 1203 | break; |
| 1204 | case CURLUPART_PATH: |
| 1205 | urlskipslash = TRUE; |
| 1206 | storep = &u->path; |
| 1207 | break; |
| 1208 | case CURLUPART_QUERY: |
| 1209 | plusencode = urlencode; |
| 1210 | appendquery = (flags & CURLU_APPENDQUERY)?1:0; |
| 1211 | equalsencode = appendquery; |
| 1212 | storep = &u->query; |
| 1213 | break; |
| 1214 | case CURLUPART_FRAGMENT: |
| 1215 | storep = &u->fragment; |
| 1216 | break; |
| 1217 | case CURLUPART_URL: { |
| 1218 | /* |
| 1219 | * Allow a new URL to replace the existing (if any) contents. |
| 1220 | * |
| 1221 | * If the existing contents is enough for a URL, allow a relative URL to |
| 1222 | * replace it. |
| 1223 | */ |
| 1224 | CURLUcode result; |
| 1225 | char *oldurl; |
| 1226 | char *redired_url; |
| 1227 | CURLU *handle2; |
| 1228 | |
| 1229 | if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN)) { |
| C3: curl_url_set calls Curl_is_absolute_url which only checks scheme length (MAX_SCHEME_LEN), not total URL length | |
| C5: if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN)) { handle2 = curl_url(); result = parseurl(part, handle2, flags); } | |
| 1230 | handle2 = curl_url(); |
| 1231 | if(!handle2) |
| 1232 | return CURLUE_OUT_OF_MEMORY; |
| 1233 | result = parseurl(part, handle2, flags); |
| 1234 | if(!result) |
| 1235 | mv_urlhandle(handle2, u); |
| 1236 | else |
| 1237 | curl_url_cleanup(handle2); |
| 1238 | return result; |
| 1239 | } |
| 1240 | /* extract the full "old" URL to do the redirect on */ |
| 1241 | result = curl_url_get(u, CURLUPART_URL, &oldurl, flags); |
| 1242 | if(result) { |
| 1243 | /* couldn't get the old URL, just use the new! */ |
| 1244 | handle2 = curl_url(); |
| 1245 | if(!handle2) |
| 1246 | return CURLUE_OUT_OF_MEMORY; |
| 1247 | result = parseurl(part, handle2, flags); |
| 1248 | if(!result) |
| 1249 | mv_urlhandle(handle2, u); |
| 1250 | else |
| 1251 | curl_url_cleanup(handle2); |
| 1252 | return result; |
| 1253 | } |
| 1254 | |
| 1255 | /* apply the relative part to create a new URL */ |
| 1256 | redired_url = concat_url(oldurl, part); |
| 1257 | free(oldurl); |
| 1258 | if(!redired_url) |
| 1259 | return CURLUE_OUT_OF_MEMORY; |
| 1260 | |
| 1261 | /* now parse the new URL */ |
| 1262 | handle2 = curl_url(); |
| 1263 | if(!handle2) { |
| 1264 | free(redired_url); |
| 1265 | return CURLUE_OUT_OF_MEMORY; |
| 1266 | } |
| 1267 | result = parseurl(redired_url, handle2, flags); |
| 1268 | free(redired_url); |
| 1269 | if(!result) |
| 1270 | mv_urlhandle(handle2, u); |
| 1271 | else |
| 1272 | curl_url_cleanup(handle2); |
| 1273 | return result; |
| 1274 | } |
| 884 | static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) |
| C5: static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) { result = seturl(url, u, flags); } | |
| 885 | { |
| 886 | CURLUcode result = seturl(url, u, flags); |
| 887 | if(result) { |
| 888 | free_urlhandle(u); |
| 889 | memset(u, 0, sizeof(struct Curl_URL)); |
| 890 | } |
| 891 | return result; |
| 892 | } |
Show 161 lines (not cited by the judge)
| 1114 | CURLUcode curl_url_set(CURLU *u, CURLUPart what, |
| 1115 | const char *part, unsigned int flags) |
| 1116 | { |
| 1117 | char **storep = NULL; |
| 1118 | long port = 0; |
| 1119 | bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; |
| 1120 | bool plusencode = FALSE; |
| 1121 | bool urlskipslash = FALSE; |
| 1122 | bool appendquery = FALSE; |
| 1123 | bool equalsencode = FALSE; |
| 1124 | |
| 1125 | if(!u) |
| 1126 | return CURLUE_BAD_HANDLE; |
| 1127 | if(!part) { |
| 1128 | /* setting a part to NULL clears it */ |
| 1129 | switch(what) { |
| 1130 | case CURLUPART_URL: |
| 1131 | break; |
| 1132 | case CURLUPART_SCHEME: |
| 1133 | storep = &u->scheme; |
| 1134 | break; |
| 1135 | case CURLUPART_USER: |
| 1136 | storep = &u->user; |
| 1137 | break; |
| 1138 | case CURLUPART_PASSWORD: |
| 1139 | storep = &u->password; |
| 1140 | break; |
| 1141 | case CURLUPART_OPTIONS: |
| 1142 | storep = &u->options; |
| 1143 | break; |
| 1144 | case CURLUPART_HOST: |
| 1145 | storep = &u->host; |
| 1146 | break; |
| 1147 | case CURLUPART_PORT: |
| 1148 | storep = &u->port; |
| 1149 | break; |
| 1150 | case CURLUPART_PATH: |
| 1151 | storep = &u->path; |
| 1152 | break; |
| 1153 | case CURLUPART_QUERY: |
| 1154 | storep = &u->query; |
| 1155 | break; |
| 1156 | case CURLUPART_FRAGMENT: |
| 1157 | storep = &u->fragment; |
| 1158 | break; |
| 1159 | default: |
| 1160 | return CURLUE_UNKNOWN_PART; |
| 1161 | } |
| 1162 | if(storep && *storep) { |
| 1163 | free(*storep); |
| 1164 | *storep = NULL; |
| 1165 | } |
| 1166 | return CURLUE_OK; |
| 1167 | } |
| 1168 | |
| 1169 | switch(what) { |
| 1170 | case CURLUPART_SCHEME: |
| 1171 | if(!(flags & CURLU_NON_SUPPORT_SCHEME) && |
| 1172 | /* verify that it is a fine scheme */ |
| 1173 | !Curl_builtin_scheme(part)) |
| 1174 | return CURLUE_UNSUPPORTED_SCHEME; |
| 1175 | storep = &u->scheme; |
| 1176 | urlencode = FALSE; /* never */ |
| 1177 | break; |
| 1178 | case CURLUPART_USER: |
| 1179 | storep = &u->user; |
| 1180 | break; |
| 1181 | case CURLUPART_PASSWORD: |
| 1182 | storep = &u->password; |
| 1183 | break; |
| 1184 | case CURLUPART_OPTIONS: |
| 1185 | storep = &u->options; |
| 1186 | break; |
| 1187 | case CURLUPART_HOST: |
| 1188 | storep = &u->host; |
| 1189 | break; |
| 1190 | case CURLUPART_PORT: |
| 1191 | urlencode = FALSE; /* never */ |
| 1192 | port = strtol(part, NULL, 10); /* Port number must be decimal */ |
| 1193 | if((port <= 0) || (port > 0xffff)) |
| 1194 | return CURLUE_BAD_PORT_NUMBER; |
| 1195 | storep = &u->port; |
| 1196 | break; |
| 1197 | case CURLUPART_PATH: |
| 1198 | urlskipslash = TRUE; |
| 1199 | storep = &u->path; |
| 1200 | break; |
| 1201 | case CURLUPART_QUERY: |
| 1202 | plusencode = urlencode; |
| 1203 | appendquery = (flags & CURLU_APPENDQUERY)?1:0; |
| 1204 | equalsencode = appendquery; |
| 1205 | storep = &u->query; |
| 1206 | break; |
| 1207 | case CURLUPART_FRAGMENT: |
| 1208 | storep = &u->fragment; |
| 1209 | break; |
| 1210 | case CURLUPART_URL: { |
| 1211 | /* |
| 1212 | * Allow a new URL to replace the existing (if any) contents. |
| 1213 | * |
| 1214 | * If the existing contents is enough for a URL, allow a relative URL to |
| 1215 | * replace it. |
| 1216 | */ |
| 1217 | CURLUcode result; |
| 1218 | char *oldurl; |
| 1219 | char *redired_url; |
| 1220 | CURLU *handle2; |
| 1221 | |
| 1222 | if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN)) { |
| 1223 | handle2 = curl_url(); |
| 1224 | if(!handle2) |
| 1225 | return CURLUE_OUT_OF_MEMORY; |
| 1226 | result = parseurl(part, handle2, flags); |
| 1227 | if(!result) |
| 1228 | mv_urlhandle(handle2, u); |
| 1229 | else |
| 1230 | curl_url_cleanup(handle2); |
| 1231 | return result; |
| 1232 | } |
| 1233 | /* extract the full "old" URL to do the redirect on */ |
| 1234 | result = curl_url_get(u, CURLUPART_URL, &oldurl, flags); |
| 1235 | if(result) { |
| 1236 | /* couldn't get the old URL, just use the new! */ |
| 1237 | handle2 = curl_url(); |
| 1238 | if(!handle2) |
| 1239 | return CURLUE_OUT_OF_MEMORY; |
| 1240 | result = parseurl(part, handle2, flags); |
| 1241 | if(!result) |
| 1242 | mv_urlhandle(handle2, u); |
| 1243 | else |
| 1244 | curl_url_cleanup(handle2); |
| 1245 | return result; |
| 1246 | } |
| 1247 | |
| 1248 | /* apply the relative part to create a new URL */ |
| 1249 | redired_url = concat_url(oldurl, part); |
| 1250 | free(oldurl); |
| 1251 | if(!redired_url) |
| 1252 | return CURLUE_OUT_OF_MEMORY; |
| 1253 | |
| 1254 | /* now parse the new URL */ |
| 1255 | handle2 = curl_url(); |
| 1256 | if(!handle2) { |
| 1257 | free(redired_url); |
| 1258 | return CURLUE_OUT_OF_MEMORY; |
| 1259 | } |
| 1260 | result = parseurl(redired_url, handle2, flags); |
| 1261 | free(redired_url); |
| 1262 | if(!result) |
| 1263 | mv_urlhandle(handle2, u); |
| 1264 | else |
| 1265 | curl_url_cleanup(handle2); |
| 1266 | return result; |
| 1267 | } |
| 1268 | default: |
| 1269 | return CURLUE_UNKNOWN_PART; |
| 1270 | } |
| 1271 | if(storep) { |
| 1272 | const char *newp = part; |
| 1273 | size_t nalloc = strlen(part); |
| 1274 |
Show 26 lines (not cited by the judge)
| 232 | bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen) |
| 233 | { |
| 234 | size_t i; |
| 235 | #ifdef WIN32 |
| 236 | if(STARTS_WITH_DRIVE_PREFIX(url)) |
| 237 | return FALSE; |
| 238 | #endif |
| 239 | for(i = 0; i < buflen && url[i]; ++i) { |
| 240 | char s = url[i]; |
| 241 | if((s == ':') && (url[i + 1] == '/')) { |
| 242 | if(buf) |
| 243 | buf[i] = 0; |
| 244 | return TRUE; |
| 245 | } |
| 246 | /* RFC 3986 3.1 explains: |
| 247 | scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) |
| 248 | */ |
| 249 | else if(ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') ) { |
| 250 | if(buf) |
| 251 | buf[i] = (char)TOLOWER(s); |
| 252 | } |
| 253 | else |
| 254 | break; |
| 255 | } |
| 256 | return FALSE; |
| 257 | } |
Show 4 lines (not cited by the judge)
| 896 | CURLU *curl_url(void) |
| 897 | { |
| 898 | return calloc(sizeof(struct Curl_URL), 1); |
| 899 | } |
Show 7 lines (not cited by the judge)
| 901 | void curl_url_cleanup(CURLU *u) |
| 902 | { |
| 903 | if(u) { |
| 904 | free_urlhandle(u); |
| 905 | free(u); |
| 906 | } |
| 907 | } |
Show 13 lines (not cited by the judge)
| 70 | static void free_urlhandle(struct Curl_URL *u) |
| 71 | { |
| 72 | free(u->scheme); |
| 73 | free(u->user); |
| 74 | free(u->password); |
| 75 | free(u->options); |
| 76 | free(u->host); |
| 77 | free(u->port); |
| 78 | free(u->path); |
| 79 | free(u->query); |
| 80 | free(u->fragment); |
| 81 | free(u->scratch); |
| 82 | } |
Show 7 lines (not cited by the judge)
| 86 | static void mv_urlhandle(struct Curl_URL *from, |
| 87 | struct Curl_URL *to) |
| 88 | { |
| 89 | free_urlhandle(to); |
| 90 | *to = *from; |
| 91 | free(from); |
| 92 | } |