/* Provenance: curl/curl tool_header_cb (fixed). * repo: curl/curl * commit: 8236aba58542c5f89f1d41ca09d84579efb05e22 * parent: 26d2755d7c3181e90e46014778941bff53d2309f * commit_url: https://github.com/curl/curl/commit/8236aba58542c5f89f1d41ca09d84579efb05e22 * cve: CVE-2020-8177 * license: curl * function: tool_header_cb * relpath: src/tool_cb_hdr.c * provenance: human * mechanism: other */ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { struct per_transfer *per = userdata; struct HdrCbData *hdrcbdata = &per->hdrcbdata; struct OutStruct *outs = &per->outs; struct OutStruct *heads = &per->heads; struct OutStruct *etag_save = &per->etag_save; const char *str = ptr; const size_t cb = size * nmemb; const char *end = (char *)ptr + cb; long protocol = 0; /* * Once that libcurl has called back tool_header_cb() the returned value * is checked against the amount that was intended to be written, if * it does not match then it fails with CURLE_WRITE_ERROR. So at this * point returning a value different from sz*nmemb indicates failure. */ size_t failure = (size && nmemb) ? 0 : 1; if(!per->config) return failure; #ifdef DEBUGBUILD if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) { warnf(per->config->global, "Header data exceeds single call write " "limit!\n"); return failure; } #endif /* * Write header data when curl option --dump-header (-D) is given. */ if(per->config->headerfile && heads->stream) { size_t rc = fwrite(ptr, size, nmemb, heads->stream); if(rc != cb) return rc; /* flush the stream to send off what we got earlier */ (void)fflush(heads->stream); } /* * Write etag to file when --etag-save option is given. * etag string that we want is enveloped in double quotes */ if(per->config->etag_save_file && etag_save->stream) { /* match only header that start with etag (case insensitive) */ if(curl_strnequal(str, "etag:", 5)) { char *etag_h = NULL; char *first = NULL; char *last = NULL; size_t etag_length = 0; etag_h = ptr; /* point to first occurrence of double quote */ first = memchr(etag_h, '\"', cb); /* * if server side messed with the etag header and doesn't include * double quotes around the etag, kindly exit with a warning */ if(!first) { warnf(per->config->global, "\nReceived header etag is missing double quote/s\n"); return 1; } else { /* discard first double quote */ first++; } /* point to last occurrence of double quote */ last = memchr(first, '\"', cb); if(!last) { warnf(per->config->global, "\nReceived header etag is missing double quote/s\n"); return 1; } /* get length of desired etag */ etag_length = (size_t)last - (size_t)first; fwrite(first, size, etag_length, etag_save->stream); /* terminate with new line */ fputc('\n', etag_save->stream); } (void)fflush(etag_save->stream); } /* * This callback sets the filename where output shall be written when * curl options --remote-name (-O) and --remote-header-name (-J) have * been simultaneously given and additionally server returns an HTTP * Content-Disposition header specifying a filename property. */ curl_easy_getinfo(per->curl, CURLINFO_PROTOCOL, &protocol); if(hdrcbdata->honor_cd_filename && (cb > 20) && checkprefix("Content-disposition:", str) && (protocol & (CURLPROTO_HTTPS|CURLPROTO_HTTP))) { const char *p = str + 20; /* look for the 'filename=' parameter (encoded filenames (*=) are not supported) */ for(;;) { char *filename; size_t len; while(*p && (p < end) && !ISALPHA(*p)) p++; if(p > end - 9) break; if(memcmp(p, "filename=", 9)) { /* no match, find next parameter */ while((p < end) && (*p != ';')) p++; continue; } p += 9; /* this expression below typecasts 'cb' only to avoid warning: signed and unsigned type in conditional expression */ len = (ssize_t)cb - (p - str); filename = parse_filename(p, len); if(filename) { if(outs->stream) { /* indication of problem, get out! */ free(filename); return failure; } outs->is_cd_filename = TRUE; outs->s_isreg = TRUE; outs->fopened = FALSE; outs->filename = filename; outs->alloc_filename = TRUE; hdrcbdata->honor_cd_filename = FALSE; /* done now! */ if(!tool_create_output_file(outs, per->config)) return failure; } break; } if(!outs->stream && !tool_create_output_file(outs, per->config)) return failure; } if(hdrcbdata->config->show_headers && (protocol & (CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_RTSP|CURLPROTO_FILE))) { /* bold headers only for selected protocols */ char *value = NULL; if(!outs->stream && !tool_create_output_file(outs, per->config)) return failure; if(hdrcbdata->global->isatty && hdrcbdata->global->styled_output) value = memchr(ptr, ':', cb); if(value) { size_t namelen = value - ptr; fprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", namelen, ptr); fwrite(&value[1], cb - namelen - 1, 1, outs->stream); } else /* not "handled", just show it */ fwrite(ptr, cb, 1, outs->stream); } return cb; }