CVE-2026-94266: password leak in CURLINFO_EFFECTIVE_URL
What the report claims
A .netrc password injected at login persists in the handle's URL info and is leaked via CURLINFO_EFFECTIVE_URL after a relative Location redirect.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2026-94266 password leak in CURLINFO_EFFECTIVE_URL
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 code confirms .netrc credentials are written into data->state.uh and never cleared (lib/url.c:1327-1332).
- Redirect processing reuses the same handle (lib/http.c:1244).
- For relative redirects, redirect_url copies the authority including userinfo into the new URL (lib/urlapi.c:1364,1392,1397).
- The resulting URL is stored in data->state.url and exposed via CURLINFO_EFFECTIVE_URL, verbose output, and write-out variables (lib/http.c:1304,1306; lib/getinfo.c:84).
- Absolute redirects do not leak because set_url calls parseurl_and_replace which fully replaces the handle (lib/urlapi.c:1855), but the relative path does not.
- The project policy does not classify this confidentiality flaw as a non-security issue.
How this analysis ran 5 claims · 14 citations verified
Checked against curl permalink d1823ebc17e5 (d1823ebc17e5). Dev 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 detailed report and reproduction. The code confirms the flaw: url_set_data_creds_netrc injects credentials into data->state.uh (lib/url.c:1327-1332), the redirect logic reuses that handle (lib/http.c:1244), and for relative Location: headers, redirect_url copies the userinfo into the new URL (lib/urlapi.c:1364,1392,1397). The result reaches CURLINFO_EFFECTIVE_URL, verbose logs, and %{url_effective} (lib/http.c:1304,1306; lib/getinfo.c:84). This is a valid confidentiality issue. The fix should strip userinfo from the handle before computing the redirect URL, as the Referer builder does (lib/http.c:1219-1221) and the proxy target builder does (lib/http.c:2163-2172).
Inspect all claims and source code 5 claims · 17 code excerpts
Report claims
The function url_set_data_creds_netrc() writes the .netrc user and password directly into data->state.uh without clearing them afterwards.
Read the reasoning
The function url_set_data_creds_netrc explicitly writes the .netrc-sourced user and password into data->state.uh via curl_url_set at lines 1327-1332. There is no subsequent clearing of these fields before the function returns.
The handle data->state.uh is reused when computing the redirect follow‑URL, so the stale userinfo remains in the URL string.
Read the reasoning
Curl_http_follow reparses the redirect target URL into the same data->state.uh handle that already contains the netrc-injected userinfo. The handle is not cloned or cleared before this call.
A server that returns a relative Location: header causes the redirect logic to copy the base URL (including userinfo) into the new URL before appending the path.
Read the reasoning
For a relative Location: like '/newpath', redirect_url computes cutoff at the first '/' after the authority, making prelen span 'scheme://user:pass@host:port'. This prefix is copied verbatim into the new URL before the relative path is appended.
The leaked userinfo ends up in data->state.url and is returned by CURLINFO_EFFECTIVE_URL, curl -w '%{url_effective}', and verbose output.
Read the reasoning
The follow_url containing the netrc credentials is stored in data->state.url and directly returned by CURLINFO_EFFECTIVE_URL. The same string is printed by infof (verbose/debug output) and is available via the %{url_effective} write-out variable.
Using a .netrc with a password and a server that responds with a relative Location:, curl -v -L --netrc-file netrc_test --netrc-optional -w '\nEFFECTIVE_URL=%{url_effective}\n' http://127.0.0.1:25036/first reproduces the leak.
Read the reasoning
The reproduction uses documented options (--netrc-file, --netrc-optional, -L for follow location, -w for write-out) that map to the code paths verified in C1-C4. The server returning a relative Location: triggers the redirect_url path that copies the userinfo.
Read the original report (excerpt)
Source code
| 1248 | static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, |
| 1249 | struct Curl_creds **pcreds) |
| 1250 | { |
| 1251 | struct Curl_creds *ncreds_out = NULL; |
| 1252 | CURLcode result = CURLE_OK; |
| 1253 | |
| 1254 | if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */ |
| C5: data->set.use_netrc enables netrc processing | |
| 1255 | struct Curl_creds *ncreds_in = NULL; |
| 1256 | bool scan_netrc = TRUE; |
| 1257 | NETRCcode ret; |
| 1258 | CURLUcode uc; |
| 1259 | |
| 1260 | if(*pcreds) { |
| 1261 | switch((*pcreds)->source) { |
| 1262 | case CREDS_OPTION: |
| 1263 | /* we never override credentials set via CURLOPT_*, leave. */ |
| 1264 | scan_netrc = FALSE; |
| 1265 | break; |
| 1266 | case CREDS_URL: /* only apply when netrc is not required */ |
| 1267 | if(data->set.use_netrc == CURL_NETRC_REQUIRED) { |
| 1268 | /* We ignore password from URL */ |
| 1269 | ncreds_in = *pcreds; |
| 1270 | } |
| 1271 | else if(!Curl_creds_has_user(*pcreds) || |
| 1272 | !Curl_creds_has_passwd(*pcreds)) { |
| 1273 | /* We use netrc to complete what is missing */ |
| 1274 | ncreds_in = *pcreds; |
| 1275 | } |
| 1276 | else |
| 1277 | scan_netrc = FALSE; |
| 1278 | break; |
| 1279 | default: /* ignore credentials from other sources */ |
| 1280 | break; |
| 1281 | } |
| 1282 | } |
| 1283 | |
| 1284 | if(!scan_netrc) |
| 1285 | goto out; |
| 1286 | |
| 1287 | ret = Curl_netrc_scan(data, &data->state.netrc, |
| 1288 | data->state.origin->hostname, |
| 1289 | Curl_creds_user(ncreds_in), |
| 1290 | CURL_EASY_STR(data, STRING_NETRC_FILE), |
| 1291 | &ncreds_out); |
| 1292 | DEBUGASSERT(!ret || !ncreds_out); |
| 1293 | if(ret == NETRC_OUT_OF_MEMORY) { |
| 1294 | result = CURLE_OUT_OF_MEMORY; |
| 1295 | goto out; |
| 1296 | } |
| 1297 | else if(ret && ((ret == NETRC_NO_MATCH) || |
| 1298 | (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { |
| 1299 | infof(data, "Could not find host %s in the %s file; using defaults", |
| 1300 | data->state.origin->hostname, |
| 1301 | (CURL_EASY_STR(data, STRING_NETRC_FILE) ? |
| 1302 | CURL_EASY_STR(data, STRING_NETRC_FILE) : ".netrc")); |
| 1303 | } |
| 1304 | else if(ret) { |
| 1305 | const char *m = Curl_netrc_strerror(ret); |
| 1306 | failf(data, ".netrc error: %s", m); |
| 1307 | result = CURLE_READ_ERROR; |
| 1308 | goto out; |
| 1309 | } |
| 1310 | else if(ncreds_out) { |
| 1311 | if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) && |
| 1312 | /* if the protocol cannot handle control codes in credentials, make |
| 1313 | sure there are none */ |
| 1314 | (str_has_ctrl(ncreds_out->user) || |
| 1315 | str_has_ctrl(ncreds_out->passwd))) { |
| 1316 | failf(data, "control code detected in .netrc credentials"); |
| 1317 | result = CURLE_READ_ERROR; |
| 1318 | goto out; |
| 1319 | } |
| 1320 | CURL_TRC_M(data, "netrc: using credentials for %s as %s", |
| 1321 | data->state.origin->hostname, ncreds_out->user); |
| 1322 | result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, |
| 1323 | *pcreds, CREDS_NETRC, pcreds); |
| 1324 | if(result) |
| 1325 | goto out; |
| 1326 | /* for updated strings, we update them in the URL */ |
| 1327 | uc = curl_url_set(data->state.uh, CURLUPART_USER, |
| C1: curl_url_set writes netrc user into data->state.uh | |
| 1328 | Curl_creds_user(*pcreds), CURLU_URLENCODE); |
| 1329 | if(!uc) |
| 1330 | uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, |
| C1: curl_url_set writes netrc password into data->state.uh | |
| 1331 | Curl_creds_passwd(*pcreds), |
| 1332 | CURLU_URLENCODE); |
| 1333 | if(uc) |
| C1: No clearing of userinfo after writing credentials; function returns at line 1347 | |
| 1334 | result = Curl_uc_to_curlcode(uc); |
| 1335 | } |
| 1336 | else |
| 1337 | DEBUGASSERT(0); |
| 1338 | } |
| 1339 | |
| 1340 | #ifdef CURLVERBOSE |
| 1341 | Curl_creds_trace(data, data->state.creds, "transfer credentials"); |
| 1342 | #endif |
| 1343 | |
| 1344 | out: |
| 1345 | Curl_creds_unlink(&ncreds_out); |
| 1346 | return result; |
| 1347 | } |
| 1348 | #endif /* CURL_DISABLE_NETRC */ |
| 1349 | |
| 1350 | static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) |
| 1351 | { |
| 1352 | struct Curl_creds *newcreds = NULL; |
| 1353 | CURLcode result = CURLE_OK; |
| 1354 | |
| 1355 | if((CURL_EASY_STR(data, STRING_USERNAME) || |
| 1356 | CURL_EASY_STR(data, STRING_PASSWORD) || |
| 1357 | CURL_EASY_STR(data, STRING_BEARER) || |
| 1175 | CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, |
| 1176 | followtype type) |
| 1177 | { |
| 1178 | bool disallowport = FALSE; |
| 1179 | bool reachedmax = FALSE; |
| 1180 | char *follow_url = NULL; |
| 1181 | CURLUcode uc; |
| 1182 | CURLcode rewind_result; |
| 1183 | bool switch_to_get = FALSE; |
| 1184 | |
| 1185 | DEBUGASSERT(type != FOLLOW_NONE); |
| 1186 | |
| 1187 | if(type != FOLLOW_FAKE) |
| 1188 | data->state.requests++; /* count all real follows */ |
| 1189 | if(type == FOLLOW_REDIR) { |
| 1190 | if((data->set.maxredirs != -1) && |
| 1191 | (data->state.followlocation >= data->set.maxredirs)) { |
| 1192 | reachedmax = TRUE; |
| 1193 | type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected |
| 1194 | to URL */ |
| 1195 | } |
| 1196 | else { |
| 1197 | data->state.followlocation++; /* count redirect-followings, including |
| 1198 | auth reloads */ |
| 1199 | |
| 1200 | if(data->set.http_auto_referer) { |
| 1201 | CURLU *u; |
| 1202 | char *referer = NULL; |
| 1203 | |
| 1204 | /* We are asked to automatically set the previous URL as the referer |
| 1205 | when we get the next URL. We pick the ->url field, which may or may |
| 1206 | not be 100% correct */ |
| 1207 | Curl_bufref_free(&data->state.referer); |
| 1208 | |
| 1209 | /* Make a copy of the URL without credentials and fragment */ |
| 1210 | u = curl_url(); |
| 1211 | if(!u) |
| 1212 | return CURLE_OUT_OF_MEMORY; |
| 1213 | |
| 1214 | uc = curl_url_set(u, CURLUPART_URL, |
| 1215 | Curl_bufref_ptr(&data->state.url), 0); |
| 1216 | if(!uc) |
| 1217 | uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); |
| 1218 | if(!uc) |
| 1219 | uc = curl_url_set(u, CURLUPART_USER, NULL, 0); |
| 1220 | if(!uc) |
| 1221 | uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0); |
| 1222 | if(!uc) |
| 1223 | uc = curl_url_get(u, CURLUPART_URL, &referer, 0); |
| 1224 | |
| 1225 | curl_url_cleanup(u); |
| 1226 | |
| 1227 | if(uc || !referer) |
| 1228 | return CURLE_OUT_OF_MEMORY; |
| 1229 | |
| 1230 | Curl_bufref_set(&data->state.referer, referer, 0, curl_free); |
| 1231 | } |
| 1232 | } |
| 1233 | } |
| 1234 | |
| 1235 | if((type != FOLLOW_RETRY) && |
| 1236 | (data->req.httpcode != 401) && (data->req.httpcode != 407) && |
| 1237 | Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { |
| 1238 | /* If this is not redirect due to a 401 or 407 response and an absolute |
| 1239 | URL: do not allow a custom port number */ |
| 1240 | disallowport = TRUE; |
| 1241 | } |
| 1242 | |
| 1243 | DEBUGASSERT(data->state.uh); |
| C2: DEBUGASSERT(data->state.uh) confirms the handle is reused | |
| 1244 | uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int) |
| C2: curl_url_set called on data->state.uh with CURLUPART_URL for redirect target | |
| 1245 | ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : |
| 1246 | ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | |
| 1247 | CURLU_ALLOW_SPACE | |
| 1248 | (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); |
| 1249 | if(uc) { |
| 1250 | if((uc == CURLUE_OUT_OF_MEMORY) || (type != FOLLOW_FAKE)) { |
| 1251 | failf(data, "The redirect target URL could not be parsed: %s", |
| 1252 | curl_url_strerror(uc)); |
| 1253 | return Curl_uc_to_curlcode(uc); |
| 1254 | } |
| 1255 | |
| 1256 | /* the URL could not be parsed for some reason, but since this is FAKE |
| 1257 | mode, duplicate the field as-is */ |
| 1258 | follow_url = curlx_strdup(newurl); |
| 1259 | if(!follow_url) |
| 1260 | return CURLE_OUT_OF_MEMORY; |
| 1261 | } |
| 1262 | else { |
| 1263 | CURLU *u = curl_url(); |
| 1264 | if(!u) |
| 1265 | return CURLE_OUT_OF_MEMORY; |
| 1266 | uc = curl_url_set(u, CURLUPART_URL, |
| 1267 | Curl_bufref_ptr(&data->state.url), |
| 1268 | CURLU_URLENCODE | CURLU_ALLOW_SPACE); |
| 1269 | if(!uc) |
| 1270 | uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0); |
| 1271 | if(uc) { |
| 1272 | curl_url_cleanup(u); |
| 1273 | return Curl_uc_to_curlcode(uc); |
| 1274 | } |
| 1275 | |
| 1276 | #ifndef CURL_DISABLE_DIGEST_AUTH |
| 1277 | { |
| 1278 | bool same_origin = Curl_url_same_origin(u, data->state.uh); |
| 1279 | curl_url_cleanup(u); |
| 1280 | if(!same_origin) |
| 1281 | Curl_auth_digest_cleanup(&data->state.digest); |
| 1282 | } |
| 1283 | #else |
| 1284 | curl_url_cleanup(u); |
| 1285 | #endif |
| 1286 | } |
| 1287 | DEBUGASSERT(follow_url); |
| 1288 | |
| 1289 | if(type == FOLLOW_FAKE) { |
| 1290 | /* we are only figuring out the new URL if we would have followed locations |
| 1291 | but now we are done so we can get out! */ |
| 1292 | data->info.wouldredirect = follow_url; |
| 1293 | |
| 1294 | if(reachedmax) { |
| 1295 | failf(data, "Maximum (%d) redirects followed", data->set.maxredirs); |
| 1296 | return CURLE_TOO_MANY_REDIRECTS; |
| 1297 | } |
| 1298 | return CURLE_OK; |
| 1299 | } |
| 1300 | |
| 1301 | if(disallowport) |
| 1302 | data->state.allow_port = FALSE; |
| 1303 | |
| 1304 | Curl_bufref_set(&data->state.url, follow_url, 0, curl_free); |
| C4: Curl_bufref_set stores follow_url (with userinfo) into data->state.url | |
| 1305 | rewind_result = Curl_req_soft_reset(&data->req, data); |
| 1306 | infof(data, "Issue another request to this URL: '%s'", follow_url); |
| C4: infof logs the follow_url with credentials in verbose output | |
| 1307 | if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) && |
| 1308 | !data->state.http_ignorecustom && |
| 1309 | CURL_EASY_STR(data, STRING_CUSTOMREQUEST)) { |
| 1310 | data->state.http_ignorecustom = TRUE; |
| 1311 | infof(data, "Drop custom request method for next request"); |
| 1312 | } |
| 1313 | |
| 1314 | /* |
| 1315 | * We get here when the HTTP code is 300-399 (and 401). We need to perform |
| 1316 | * differently based on exactly what return code there was. |
| 1317 | * |
| 1318 | * News from 7.10.6: we can also get here on a 401 or 407, in case we act on |
| 1319 | * an HTTP (proxy-) authentication scheme other than Basic. |
| 1320 | */ |
| 1321 | switch(data->info.httpcode) { |
| 1322 | /* 401 - Act on a WWW-Authenticate, we keep on moving and do the |
| 1323 | Authorization: XXXX header in the HTTP request code snippet */ |
| 1324 | /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the |
| 1325 | Proxy-Authorization: XXXX header in the HTTP request code snippet */ |
| 1326 | /* 300 - Multiple Choices */ |
| 1327 | /* 306 - Not used */ |
| 1328 | /* 307 - Temporary Redirect */ |
| 1329 | default: /* for all above (and the unknown ones) */ |
| 1330 | /* Some codes are explicitly mentioned since I have checked RFC2616 and |
| 1331 | * they seem to be OK to POST to. |
| 1332 | */ |
| 1333 | break; |
| 1334 | case 301: /* Moved Permanently */ |
| 1335 | /* (quote from RFC7231, section 6.4.2) |
| 1273 | return Curl_uc_to_curlcode(uc); |
| 1274 | } |
| 1275 | |
| 1276 | #ifndef CURL_DISABLE_DIGEST_AUTH |
| 1277 | { |
| 1278 | bool same_origin = Curl_url_same_origin(u, data->state.uh); |
| 1279 | curl_url_cleanup(u); |
| 1280 | if(!same_origin) |
| 1281 | Curl_auth_digest_cleanup(&data->state.digest); |
| 1282 | } |
| 1283 | #else |
| 1284 | curl_url_cleanup(u); |
| 1285 | #endif |
| 1286 | } |
| 1287 | DEBUGASSERT(follow_url); |
| 1288 | |
| 1289 | if(type == FOLLOW_FAKE) { |
| 1290 | /* we are only figuring out the new URL if we would have followed locations |
| 1291 | but now we are done so we can get out! */ |
| 1292 | data->info.wouldredirect = follow_url; |
| 1293 | |
| 1294 | if(reachedmax) { |
| 1295 | failf(data, "Maximum (%d) redirects followed", data->set.maxredirs); |
| 1296 | return CURLE_TOO_MANY_REDIRECTS; |
| 1297 | } |
| 1298 | return CURLE_OK; |
| 1299 | } |
| 1300 | |
| 1301 | if(disallowport) |
| 1302 | data->state.allow_port = FALSE; |
| 1303 | |
| 1304 | Curl_bufref_set(&data->state.url, follow_url, 0, curl_free); |
| C4: Curl_bufref_set stores follow_url (with userinfo) into data->state.url | |
| 1305 | rewind_result = Curl_req_soft_reset(&data->req, data); |
| 1306 | infof(data, "Issue another request to this URL: '%s'", follow_url); |
| C4: infof logs the follow_url with credentials in verbose output | |
| 1307 | if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) && |
| 1308 | !data->state.http_ignorecustom && |
| 1309 | CURL_EASY_STR(data, STRING_CUSTOMREQUEST)) { |
| 1310 | data->state.http_ignorecustom = TRUE; |
| 1311 | infof(data, "Drop custom request method for next request"); |
| 1312 | } |
| 1313 | |
| 1314 | /* |
| 1315 | * We get here when the HTTP code is 300-399 (and 401). We need to perform |
| 1316 | * differently based on exactly what return code there was. |
| 1317 | * |
| 1318 | * News from 7.10.6: we can also get here on a 401 or 407, in case we act on |
| 1319 | * an HTTP (proxy-) authentication scheme other than Basic. |
| 1320 | */ |
| 1321 | switch(data->info.httpcode) { |
| 1322 | /* 401 - Act on a WWW-Authenticate, we keep on moving and do the |
| 1323 | Authorization: XXXX header in the HTTP request code snippet */ |
| 1324 | /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the |
| 1325 | Proxy-Authorization: XXXX header in the HTTP request code snippet */ |
| 1326 | /* 300 - Multiple Choices */ |
| 1327 | /* 306 - Not used */ |
| 1328 | /* 307 - Temporary Redirect */ |
| 1329 | default: /* for all above (and the unknown ones) */ |
| 1330 | /* Some codes are explicitly mentioned since I have checked RFC2616 and |
| 1331 | * they seem to be OK to POST to. |
| 1309 | curlx_dyn_free(&host); |
| 1310 | free_urlhandle(u); |
| 1311 | return ures; |
| 1312 | } |
| 1313 | |
| 1314 | /* |
| 1315 | * Parse the URL and, if successful, replace everything in the Curl_URL struct. |
| 1316 | */ |
| 1317 | static CURLUcode parseurl_and_replace(const char *url, CURLU *u, |
| 1318 | unsigned int flags) |
| 1319 | { |
| 1320 | CURLUcode ures; |
| 1321 | CURLU tmpurl; |
| 1322 | memset(&tmpurl, 0, sizeof(tmpurl)); |
| 1323 | ures = parseurl(url, &tmpurl, flags); |
| 1324 | if(!ures) { |
| 1325 | free_urlhandle(u); |
| 1326 | *u = tmpurl; |
| 1327 | } |
| 1328 | return ures; |
| 1329 | } |
| 1330 | |
| 1331 | /* |
| 1332 | * Concatenate a relative URL onto a base URL making it absolute. |
| 1333 | */ |
| 1334 | static CURLUcode redirect_url(const char *base, const char *relurl, |
| 1335 | CURLU *u, unsigned int flags) |
| 1336 | { |
| 1337 | struct dynbuf urlbuf; |
| 1338 | bool host_changed = FALSE; |
| 1339 | const char *useurl = relurl; |
| 1340 | const char *cutoff = NULL; |
| 1341 | size_t prelen; |
| 1342 | CURLUcode uc; |
| 1343 | /* this can get here with a NULL u->scheme only if asked to use the default |
| 1344 | scheme, so allow fallback to that */ |
| 1345 | const char *scheme = u->scheme ? u->scheme : DEFAULT_SCHEME; |
| 1346 | |
| 1347 | /* protsep points to the start of the hostname, after [scheme]:// */ |
| 1348 | const char *protsep = base + strlen(scheme) + 3; |
| 1349 | DEBUGASSERT(base && relurl && u); /* all set here */ |
| 1350 | if(!base) |
| 1351 | return CURLUE_MALFORMED_INPUT; /* should never happen */ |
| 1352 | |
| 1353 | /* handle different relative URL types */ |
| 1354 | switch(relurl[0]) { |
| 1355 | case '/': |
| 1356 | if(relurl[1] == '/') { |
| 1357 | /* protocol-relative URL: //example.com/path */ |
| 1358 | cutoff = protsep; |
| 1359 | useurl = &relurl[2]; |
| 1360 | host_changed = TRUE; |
| 1361 | } |
| 1362 | else |
| 1363 | /* absolute /path */ |
| 1364 | cutoff = strchr(protsep, '/'); |
| C3: cutoff = strchr(protsep, '/') finds first slash after host:port | |
| 1365 | break; |
| 1366 | |
| 1367 | case '#': |
| 1368 | /* fragment-only change */ |
| 1369 | if(u->fragment_present) |
| 1370 | cutoff = strchr(protsep, '#'); |
| 1371 | break; |
| 1372 | |
| 1373 | default: |
| 1374 | /* path or query-only change */ |
| 1375 | if(u->query_present) |
| 1376 | /* remove existing query */ |
| 1377 | cutoff = strchr(protsep, '?'); |
| 1378 | else if(u->fragment_present) |
| 1379 | /* Remove existing fragment */ |
| 1380 | cutoff = strchr(protsep, '#'); |
| 1381 | |
| 1382 | if(relurl[0] != '?') { |
| 1383 | /* append a relative path after the last slash */ |
| 1384 | cutoff = memrchr(protsep, '/', |
| 1385 | cutoff ? (size_t)(cutoff - protsep) : strlen(protsep)); |
| 1386 | if(cutoff) |
| 1387 | cutoff++; /* truncate after last slash */ |
| 1388 | } |
| 1389 | break; |
| 1390 | } |
| 1391 | |
| 1392 | prelen = cutoff ? (size_t)(cutoff - base) : strlen(base); |
| C3: prelen includes scheme://user:pass@host:port | |
| 1393 | |
| 1394 | /* build new URL */ |
| 1395 | curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH); |
| 1396 | |
| 1397 | if(!curlx_dyn_addn(&urlbuf, base, prelen) && |
| C3: curlx_dyn_addn copies base (including userinfo) into new URL buffer | |
| 1398 | !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, |
| 1399 | QUERY_NOT_YET)) { |
| 1400 | uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u, |
| 1401 | flags & ~U_CURLU_PATH_AS_IS); |
| 1402 | } |
| 1403 | else |
| 1404 | uc = CURLUE_OUT_OF_MEMORY; |
| 1405 | |
| 1406 | curlx_dyn_free(&urlbuf); |
| 1407 | return uc; |
| 1408 | } |
| 1409 | |
| 1410 | /* |
| 1411 | */ |
| 1412 | CURLU *curl_url(void) |
| 1413 | { |
| 1414 | return curlx_calloc(1, sizeof(struct Curl_URL)); |
| 1415 | } |
| 1416 | |
| 1417 | void curl_url_cleanup(CURLU *u) |
| 1418 | { |
| 1419 | if(u) { |
| 1420 | free_urlhandle(u); |
| 1421 | curlx_free(u); |
| 1422 | } |
| 1423 | } |
| 1424 | |
| 1425 | #define DUP(dest, src, name) \ |
| 1426 | do { \ |
| 58 | info->proxyauthavail = 0; |
| 59 | info->httpauthavail = 0; |
| 60 | info->proxyauthpicked = 0; |
| 61 | info->httpauthpicked = 0; |
| 62 | info->numconnects = 0; |
| 63 | |
| 64 | curlx_safefree(info->contenttype); |
| 65 | curlx_safefree(info->wouldredirect); |
| 66 | |
| 67 | memset(&info->primary, 0, sizeof(info->primary)); |
| 68 | info->retry_after = 0; |
| 69 | |
| 70 | info->conn_scheme = 0; |
| 71 | info->conn_protocol = 0; |
| 72 | info->used_proxy = 0; |
| 73 | |
| 74 | #ifdef USE_SSL |
| 75 | Curl_ssl_free_certinfo(data); |
| 76 | #endif |
| 77 | } |
| 78 | |
| 79 | static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, |
| 80 | const char **param_charp) |
| 81 | { |
| 82 | switch(info) { |
| 83 | case CURLINFO_EFFECTIVE_URL: { |
| 84 | const char *s = Curl_bufref_ptr(&data->state.url); |
| C4: CURLINFO_EFFECTIVE_URL returns Curl_bufref_ptr(&data->state.url) unsanitized | |
| 85 | *param_charp = s ? s : ""; |
| 86 | } |
| 87 | break; |
| 88 | case CURLINFO_EFFECTIVE_METHOD: { |
| 89 | const char *m = CURL_EASY_STR(data, STRING_CUSTOMREQUEST); |
| 90 | if(!m) { |
| 91 | if(data->set.opt_no_body) |
| 92 | m = "HEAD"; |
| 93 | #ifndef CURL_DISABLE_HTTP |
| 94 | else { |
| 95 | switch(data->state.httpreq) { |
| 96 | case HTTPREQ_POST: |
| 97 | case HTTPREQ_POST_FORM: |
| 98 | case HTTPREQ_POST_MIME: |
| 99 | m = "POST"; |
| 100 | break; |
| 101 | case HTTPREQ_PUT: |
| 102 | m = "PUT"; |
| 103 | break; |
| 104 | default: /* this should never happen */ |
| 105 | case HTTPREQ_GET: |
| 106 | m = "GET"; |
| 107 | break; |
| 108 | case HTTPREQ_HEAD: |
| 109 | m = "HEAD"; |
| 110 | break; |
| 111 | } |
| 2383 | #define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL is preferred |
| C5: CURL_NETRC_OPTIONAL defined |
| 905 | CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) |
| 906 | { |
| 907 | struct Curl_eapi_guard guard; |
| 908 | CURLcode result; |
| 909 | |
| 910 | if(CURL_EAPI_ENTER(&guard, curl, easy_getinfo, &result)) { |
| 911 | struct Curl_easy *data = curl; |
| 912 | va_list arg; |
| 913 | void *paramp; |
| 914 | |
| 915 | va_start(arg, info); |
| 916 | paramp = va_arg(arg, void *); |
| 917 | |
| 918 | result = Curl_getinfo(data, info, paramp); |
| C5: curl_easy_getinfo calls Curl_getinfo for CURLINFO_EFFECTIVE_URL | |
| 919 | |
| 920 | va_end(arg); |
| 921 | } |
| 922 | CURL_EAPI_LEAVE(&guard); |
| 923 | return result; |
| 924 | } |
Show 63 lines (not cited by the judge)
| 2136 | /* The path sent to the proxy is in fact the entire URL, but if the remote |
| 2137 | host is a IDN-name, we must make sure that the request we produce only |
| 2138 | uses the decoded hostname! */ |
| 2139 | |
| 2140 | /* and no fragment part */ |
| 2141 | CURLUcode uc; |
| 2142 | char *url; |
| 2143 | CURLU *h = curl_url_dup(data->state.uh); |
| 2144 | if(!h) |
| 2145 | return CURLE_OUT_OF_MEMORY; |
| 2146 | |
| 2147 | if(!data->state.origin->ipv6 && |
| 2148 | (data->state.origin->user_hostname != data->state.origin->hostname)) { |
| 2149 | uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0); |
| 2150 | if(uc) { |
| 2151 | curl_url_cleanup(h); |
| 2152 | return CURLE_OUT_OF_MEMORY; |
| 2153 | } |
| 2154 | } |
| 2155 | uc = curl_url_set(h, CURLUPART_FRAGMENT, NULL, 0); |
| 2156 | if(uc) { |
| 2157 | curl_url_cleanup(h); |
| 2158 | return CURLE_OUT_OF_MEMORY; |
| 2159 | } |
| 2160 | |
| 2161 | if(data->state.origin->scheme == &Curl_scheme_http) { |
| 2162 | /* when getting HTTP, we do not want the userinfo the URL */ |
| 2163 | uc = curl_url_set(h, CURLUPART_USER, NULL, 0); |
| 2164 | if(uc) { |
| 2165 | curl_url_cleanup(h); |
| 2166 | return CURLE_OUT_OF_MEMORY; |
| 2167 | } |
| 2168 | uc = curl_url_set(h, CURLUPART_PASSWORD, NULL, 0); |
| 2169 | if(uc) { |
| 2170 | curl_url_cleanup(h); |
| 2171 | return CURLE_OUT_OF_MEMORY; |
| 2172 | } |
| 2173 | } |
| 2174 | else if(data->state.creds && (data->state.creds->source != CREDS_URL)) { |
| 2175 | /* credentials not from the URL need to be set */ |
| 2176 | uc = curl_url_set(h, CURLUPART_USER, |
| 2177 | data->state.creds->user, CURLU_URLENCODE); |
| 2178 | if(!uc) |
| 2179 | uc = curl_url_set(h, CURLUPART_PASSWORD, |
| 2180 | data->state.creds->passwd, CURLU_URLENCODE); |
| 2181 | if(uc) { |
| 2182 | curl_url_cleanup(h); |
| 2183 | return Curl_uc_to_curlcode(uc); |
| 2184 | } |
| 2185 | } |
| 2186 | |
| 2187 | /* Extract the URL to use in the request. */ |
| 2188 | uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT); |
| 2189 | if(uc) { |
| 2190 | curl_url_cleanup(h); |
| 2191 | return CURLE_OUT_OF_MEMORY; |
| 2192 | } |
| 2193 | |
| 2194 | curl_url_cleanup(h); |
| 2195 | |
| 2196 | /* target or URL */ |
| 2197 | result = curlx_dyn_add(r, CURL_EASY_STR(data, STRING_TARGET) ? |
| 2198 | CURL_EASY_STR(data, STRING_TARGET) : url); |
Show 1 lines (not cited by the judge)
| 95 | #define CURLU_URLENCODE (1 << 7) /* URL encode on set */ |
Show 8 lines (not cited by the judge)
| 99 | const char *Curl_bufref_ptr(const struct bufref *br) |
| 100 | { |
| 101 | DEBUGASSERT(br); |
| 102 | DEBUGASSERT(br->signature == SIGNATURE); |
| 103 | DEBUGASSERT(br->ptr || !br->len); |
| 104 | |
| 105 | return (const char *)br->ptr; |
| 106 | } |
Show 11 lines (not cited by the judge)
| 72 | void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len, |
| 73 | void (*dtor)(void *)) |
| 74 | { |
| 75 | DEBUGASSERT(ptr || !len); |
| 76 | DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH); |
| 77 | |
| 78 | Curl_bufref_free(br); |
| 79 | br->ptr = (const unsigned char *)ptr; |
| 80 | br->len = len; |
| 81 | br->dtor = dtor; |
| 82 | } |
Show 25 lines (not cited by the judge)
| 104 | CURLcode Curl_creds_merge(const char *user, |
| 105 | const char *passwd, |
| 106 | struct Curl_creds *creds_in, |
| 107 | uint8_t source, |
| 108 | struct Curl_creds **pcreds_out) |
| 109 | { |
| 110 | struct Curl_creds *creds_out = NULL; |
| 111 | CURLcode result; |
| 112 | |
| 113 | if(!creds_in) { |
| 114 | result = Curl_creds_create(user, passwd, NULL, NULL, NULL, |
| 115 | source, &creds_out); |
| 116 | } |
| 117 | else { |
| 118 | result = Curl_creds_create(user ? user : Curl_creds_user(creds_in), |
| 119 | passwd ? passwd : Curl_creds_passwd(creds_in), |
| 120 | Curl_creds_oauth_bearer(creds_in), |
| 121 | Curl_creds_sasl_authzid(creds_in), |
| 122 | Curl_creds_sasl_service(creds_in), |
| 123 | source, &creds_out); |
| 124 | } |
| 125 | Curl_creds_link(pcreds_out, creds_out); |
| 126 | Curl_creds_unlink(&creds_out); |
| 127 | return result; |
| 128 | } |
Show 2 lines (not cited by the judge)
| 81 | #define Curl_creds_user(c) ((c) ? (c)->user : "") |
| 82 | #define Curl_creds_passwd(c) ((c) ? (c)->passwd : "") |
Show 105 lines (not cited by the judge)
| 2070 | CURLUcode curl_url_set(CURLU *u, CURLUPart what, |
| 2071 | const char *part, unsigned int flags) |
| 2072 | { |
| 2073 | char **storep = NULL; |
| 2074 | bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; |
| 2075 | bool plusencode = FALSE; |
| 2076 | bool pathmode = FALSE; |
| 2077 | bool leadingslash = FALSE; |
| 2078 | bool appendquery = FALSE; |
| 2079 | bool equalsencode = FALSE; |
| 2080 | size_t nalloc; |
| 2081 | |
| 2082 | if(!u) |
| 2083 | return CURLUE_BAD_HANDLE; |
| 2084 | if(!part) |
| 2085 | /* setting a part to NULL clears it */ |
| 2086 | return urlset_clear(u, what); |
| 2087 | |
| 2088 | nalloc = strlen(part); |
| 2089 | if(nalloc > CURL_MAX_INPUT_LENGTH) |
| 2090 | /* excessive input length */ |
| 2091 | return CURLUE_MALFORMED_INPUT; |
| 2092 | |
| 2093 | switch(what) { |
| 2094 | case CURLUPART_SCHEME: { |
| 2095 | CURLUcode status = set_url_scheme(u, part, flags); |
| 2096 | if(status) |
| 2097 | return status; |
| 2098 | storep = &u->scheme; |
| 2099 | urlencode = FALSE; /* never */ |
| 2100 | break; |
| 2101 | } |
| 2102 | case CURLUPART_USER: |
| 2103 | storep = &u->user; |
| 2104 | break; |
| 2105 | case CURLUPART_PASSWORD: |
| 2106 | storep = &u->password; |
| 2107 | break; |
| 2108 | case CURLUPART_OPTIONS: |
| 2109 | storep = &u->options; |
| 2110 | break; |
| 2111 | case CURLUPART_HOST: |
| 2112 | storep = &u->host; |
| 2113 | curlx_safefree(u->zoneid); |
| 2114 | break; |
| 2115 | case CURLUPART_ZONEID: |
| 2116 | storep = &u->zoneid; |
| 2117 | break; |
| 2118 | case CURLUPART_PORT: |
| 2119 | return set_url_port(u, part); |
| 2120 | case CURLUPART_PATH: |
| 2121 | pathmode = TRUE; |
| 2122 | leadingslash = TRUE; /* enforce */ |
| 2123 | storep = &u->path; |
| 2124 | break; |
| 2125 | case CURLUPART_QUERY: |
| 2126 | plusencode = urlencode; |
| 2127 | appendquery = (flags & CURLU_APPENDQUERY) ? 1 : 0; |
| 2128 | equalsencode = appendquery; |
| 2129 | storep = &u->query; |
| 2130 | u->query_present = TRUE; |
| 2131 | break; |
| 2132 | case CURLUPART_FRAGMENT: |
| 2133 | storep = &u->fragment; |
| 2134 | u->fragment_present = TRUE; |
| 2135 | break; |
| 2136 | case CURLUPART_URL: |
| 2137 | return set_url(u, part, nalloc, flags); |
| 2138 | default: |
| 2139 | return CURLUE_UNKNOWN_PART; |
| 2140 | } |
| 2141 | DEBUGASSERT(storep); |
| 2142 | { |
| 2143 | const char *newp = NULL; |
| 2144 | struct dynbuf enc; |
| 2145 | CURLUcode status; |
| 2146 | curlx_dyn_init(&enc, (nalloc * 3) + 1 + leadingslash); |
| 2147 | |
| 2148 | if(leadingslash && (part[0] != '/')) { |
| 2149 | CURLcode result = curlx_dyn_addn(&enc, "/", 1); |
| 2150 | if(result) |
| 2151 | return cc2cu(result); |
| 2152 | } |
| 2153 | if(urlencode) |
| 2154 | status = url_encode_part(&enc, part, plusencode, pathmode, equalsencode); |
| 2155 | else |
| 2156 | status = url_uppercasehex_part(&enc, part); |
| 2157 | if(!status) { |
| 2158 | newp = curlx_dyn_ptr(&enc); |
| 2159 | |
| 2160 | if(appendquery && newp) |
| 2161 | return url_append_query(u, &enc); |
| 2162 | else if(what == CURLUPART_HOST) |
| 2163 | status = url_sethost(u, &enc, urlencode, flags); |
| 2164 | } |
| 2165 | if(status) |
| 2166 | return status; |
| 2167 | |
| 2168 | if(what == CURLUPART_PASSWORD) |
| 2169 | curlx_strzero(*storep); |
| 2170 | curlx_free(*storep); |
| 2171 | *storep = (char *)CURL_UNCONST(newp); |
| 2172 | } |
| 2173 | return CURLUE_OK; |
| 2174 | } |
Show 5 lines (not cited by the judge)
| 142 | #define infof(data, ...) \ |
| 143 | do { \ |
| 144 | if(Curl_trc_is_verbose(data)) \ |
| 145 | Curl_infof(data, __VA_ARGS__); \ |
| 146 | } while(0) |
Show 53 lines (not cited by the judge)
| 1822 | static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, |
| 1823 | unsigned int flags) |
| 1824 | { |
| 1825 | /* |
| 1826 | * Allow a new URL to replace the existing (if any) contents. |
| 1827 | * |
| 1828 | * If the existing contents is enough for a URL, allow a relative URL to |
| 1829 | * replace it. |
| 1830 | */ |
| 1831 | CURLUcode uc; |
| 1832 | char *oldurl = NULL; |
| 1833 | |
| 1834 | if(!part_size) { |
| 1835 | /* a blank URL is not a valid URL unless we already have a complete one |
| 1836 | and this is a redirect */ |
| 1837 | uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); |
| 1838 | if(!uc) { |
| 1839 | /* success, meaning the "" is a fine relative URL, and the new URL |
| 1840 | inherits scheme/authority/path/query, but not fragment, from the |
| 1841 | existing URL (RFC 3986 section 5.2.2) */ |
| 1842 | curlx_safefree(u->fragment); |
| 1843 | u->fragment_present = FALSE; |
| 1844 | curlx_free(oldurl); |
| 1845 | return CURLUE_OK; |
| 1846 | } |
| 1847 | if(uc == CURLUE_OUT_OF_MEMORY) |
| 1848 | return uc; |
| 1849 | return CURLUE_MALFORMED_INPUT; |
| 1850 | } |
| 1851 | |
| 1852 | /* if the new URL is absolute replace the existing with the new. */ |
| 1853 | if(Curl_is_absolute_url(url, NULL, 0, |
| 1854 | flags & (CURLU_GUESS_SCHEME | CURLU_DEFAULT_SCHEME))) |
| 1855 | return parseurl_and_replace(url, u, flags); |
| 1856 | |
| 1857 | /* if the old URL is incomplete (we cannot get an absolute URL in |
| 1858 | 'oldurl'), replace the existing with the new. |
| 1859 | Always include "scheme://" to make the URL "complete" */ |
| 1860 | /* Preserve empty query/fragment separators: they affect where relative |
| 1861 | references splice into the base URL. */ |
| 1862 | uc = curl_url_get(u, CURLUPART_URL, &oldurl, |
| 1863 | (flags & ~CURLU_NO_GUESS_SCHEME) | CURLU_GET_EMPTY); |
| 1864 | if(uc == CURLUE_OUT_OF_MEMORY) |
| 1865 | return uc; |
| 1866 | else if(uc) |
| 1867 | return parseurl_and_replace(url, u, flags); |
| 1868 | |
| 1869 | DEBUGASSERT(oldurl); /* it is set here */ |
| 1870 | /* apply the relative part to create a new URL */ |
| 1871 | uc = redirect_url(oldurl, url, u, flags); |
| 1872 | curlx_free(oldurl); |
| 1873 | return uc; |
| 1874 | } |
Show 101 lines (not cited by the judge)
| 1322 | static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, |
| 1323 | struct Curl_creds **pcreds) |
| 1324 | { |
| 1325 | struct Curl_creds *ncreds_out = NULL; |
| 1326 | CURLcode result = CURLE_OK; |
| 1327 | |
| 1328 | if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */ |
| 1329 | struct Curl_creds *ncreds_in = NULL; |
| 1330 | bool scan_netrc = TRUE; |
| 1331 | NETRCcode ret; |
| 1332 | CURLUcode uc; |
| 1333 | |
| 1334 | if(*pcreds) { |
| 1335 | switch((*pcreds)->source) { |
| 1336 | case CREDS_OPTION: |
| 1337 | /* we never override credentials set via CURLOPT_*, leave. */ |
| 1338 | scan_netrc = FALSE; |
| 1339 | break; |
| 1340 | case CREDS_URL: /* only apply when netrc is not required */ |
| 1341 | if(data->set.use_netrc == CURL_NETRC_REQUIRED) { |
| 1342 | /* We ignore password from URL */ |
| 1343 | ncreds_in = *pcreds; |
| 1344 | } |
| 1345 | else if(!Curl_creds_has_user(*pcreds) || |
| 1346 | !Curl_creds_has_passwd(*pcreds)) { |
| 1347 | /* We use netrc to complete what is missing */ |
| 1348 | ncreds_in = *pcreds; |
| 1349 | } |
| 1350 | else |
| 1351 | scan_netrc = FALSE; |
| 1352 | break; |
| 1353 | default: /* ignore credentials from other sources */ |
| 1354 | break; |
| 1355 | } |
| 1356 | } |
| 1357 | |
| 1358 | if(!scan_netrc) |
| 1359 | goto out; |
| 1360 | |
| 1361 | ret = Curl_netrc_scan(data, &data->state.netrc, |
| 1362 | data->state.origin->hostname, |
| 1363 | Curl_creds_user(ncreds_in), |
| 1364 | CURL_EASY_STR(data, STRING_NETRC_FILE), |
| 1365 | &ncreds_out); |
| 1366 | DEBUGASSERT(!ret || !ncreds_out); |
| 1367 | if(ret == NETRC_OUT_OF_MEMORY) { |
| 1368 | result = CURLE_OUT_OF_MEMORY; |
| 1369 | goto out; |
| 1370 | } |
| 1371 | else if(ret && ((ret == NETRC_NO_MATCH) || |
| 1372 | (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { |
| 1373 | infof(data, "Could not find host %s in the %s file; using defaults", |
| 1374 | data->state.origin->hostname, |
| 1375 | (CURL_EASY_STR(data, STRING_NETRC_FILE) ? |
| 1376 | CURL_EASY_STR(data, STRING_NETRC_FILE) : ".netrc")); |
| 1377 | } |
| 1378 | else if(ret) { |
| 1379 | const char *m = Curl_netrc_strerror(ret); |
| 1380 | failf(data, ".netrc error: %s", m); |
| 1381 | result = CURLE_READ_ERROR; |
| 1382 | goto out; |
| 1383 | } |
| 1384 | else if(ncreds_out) { |
| 1385 | if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL)) { |
| 1386 | /* if the protocol cannot handle control codes in credentials, make |
| 1387 | sure there are none */ |
| 1388 | if(str_has_ctrl(ncreds_out->user) || |
| 1389 | str_has_ctrl(ncreds_out->passwd)) { |
| 1390 | failf(data, "control code detected in .netrc credentials"); |
| 1391 | result = CURLE_READ_ERROR; |
| 1392 | goto out; |
| 1393 | } |
| 1394 | } |
| 1395 | CURL_TRC_M(data, "netrc: using credentials for %s as %s", |
| 1396 | data->state.origin->hostname, ncreds_out->user); |
| 1397 | result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, |
| 1398 | *pcreds, CREDS_NETRC, pcreds); |
| 1399 | if(result) |
| 1400 | goto out; |
| 1401 | /* for updated strings, we update them in the URL */ |
| 1402 | uc = curl_url_set(data->state.uh, CURLUPART_USER, |
| 1403 | Curl_creds_user(*pcreds), CURLU_URLENCODE); |
| 1404 | if(!uc) |
| 1405 | uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, |
| 1406 | Curl_creds_passwd(*pcreds), |
| 1407 | CURLU_URLENCODE); |
| 1408 | if(uc) |
| 1409 | result = Curl_uc_to_curlcode(uc); |
| 1410 | } |
| 1411 | else |
| 1412 | DEBUGASSERT(0); |
| 1413 | } |
| 1414 | |
| 1415 | #ifdef CURLVERBOSE |
| 1416 | Curl_creds_trace(data, data->state.creds, "transfer credentials"); |
| 1417 | #endif |
| 1418 | |
| 1419 | out: |
| 1420 | Curl_creds_unlink(&ncreds_out); |
| 1421 | return result; |
| 1422 | } |