From bbc2ca6d553f0ce3e670303ac9a3c764cf10d779 Mon Sep 17 00:00:00 2001 From: Noah Sanci Date: Wed, 15 Jun 2022 10:07:29 -0400 Subject: [PATCH] PR28284 - Debuginfod header functionality implemented Debuginfod and debuginfod clients are now equipped to send and receive http headers prefixed with X-DEBUGINFOD and print them in verbose mode for more context Signed-off-by: Noah Sanci --- debuginfod/ChangeLog | 14 ++++++++++ debuginfod/debuginfod-client.c | 14 ++++++++-- debuginfod/debuginfod-find.c | 3 ++ debuginfod/debuginfod.cxx | 18 ++++++++++++ debuginfod/debuginfod.h.in | 4 +++ debuginfod/libdebuginfod.map | 3 ++ doc/ChangeLog | 5 ++++ doc/debuginfod_find_debuginfo.3 | 13 +++++++++ doc/debuginfod_get_headers.3 | 2 ++ tests/ChangeLog | 6 ++++ tests/run-debuginfod-response-headers.sh | 48 ++++++++++++++++++++++++++++---- 11 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 doc/debuginfod_get_headers.3 diff --git a/debuginfod/ChangeLog b/debuginfod/ChangeLog index c692a38..8c843f3 100644 --- a/debuginfod/ChangeLog +++ b/debuginfod/ChangeLog @@ -1,3 +1,17 @@ +2022-07-15 Noah Sanci + + * debuginfod-client.c (header_callback): Handle headers without + X-DEBUGINFOD prefix. + (debuginfod_query_server): Removed verbose printing headers when + undesired. + (debuginfod_get_headers): Created. + * debuginfod-find.c (main): Verboes printing headers. + * debuginfod.cxx (handle_buildid): Add headers prefixed with + X-DEBUGINFOD from federated servers to this server's response + headers. + * debuginfod.h.in (debuginfod_get_headers): Created. + * libdebuginfod.map: New elfutils version added. + 2022-09-02 Aaron Merey * debuginfod.cxx (parse_opt): If '-C' is given with no arg, do not diff --git a/debuginfod/debuginfod-client.c b/debuginfod/debuginfod-client.c index b7b65af..a3565f5 100644 --- a/debuginfod/debuginfod-client.c +++ b/debuginfod/debuginfod-client.c @@ -501,6 +501,12 @@ header_callback (char * buffer, size_t size, size_t numitems, void * userdata) { if (size != 1) return 0; + // X-DEBUGINFOD is 11 characters long. + // Some basic checks to ensure the headers received are of the expected format + if ( strncmp(buffer, "X-DEBUGINFOD", 11) || buffer[numitems-1] != '\n' + || (buffer == strstr(buffer, ":")) ){ + return numitems; + } /* Temporary buffer for realloc */ char *temp = NULL; struct handle_data *data = (struct handle_data *) userdata; @@ -1111,8 +1117,6 @@ debuginfod_query_server (debuginfod_client *c, if (c->winning_headers == NULL) { c->winning_headers = data[committed_to].response_data; - if (vfd >= 0 && c->winning_headers != NULL) - dprintf(vfd, "\n%s", c->winning_headers); data[committed_to].response_data = NULL; data[committed_to].response_data_size = 0; } @@ -1542,6 +1546,12 @@ debuginfod_get_url(debuginfod_client *client) return client->url; } +const char * +debuginfod_get_headers(debuginfod_client *client) +{ + return client->winning_headers; +} + void debuginfod_end (debuginfod_client *client) { diff --git a/debuginfod/debuginfod-find.c b/debuginfod/debuginfod-find.c index f60b546..fb1f294 100644 --- a/debuginfod/debuginfod-find.c +++ b/debuginfod/debuginfod-find.c @@ -215,6 +215,9 @@ main(int argc, char** argv) if (verbose) { + const char* headers = debuginfod_get_headers(client); + if (headers) + fprintf(stderr, "Headers:\n%s", headers); const char* url = debuginfod_get_url (client); if (url != NULL) fprintf(stderr, "Downloaded from %s\n", url); diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx index 8680c04..27b671d 100644 --- a/debuginfod/debuginfod.cxx +++ b/debuginfod/debuginfod.cxx @@ -2093,6 +2093,24 @@ and will not query the upstream servers"); { add_mhd_response_header (r, "Content-Type", "application/octet-stream"); + const char * hdrs = debuginfod_get_headers(client); + string header_dup; + if (hdrs) + header_dup = string(hdrs); + size_t pos = 0; + // Clean winning headers to add all X-DEBUGINFOD lines to the package we'll send + while( (pos = header_dup.find("X-DEBUGINFOD")) != string::npos) + { + // Focus on where X-DEBUGINFOD- begins + header_dup = header_dup.substr(pos); + size_t newline = header_dup.find('\n'); + if (newline == string::npos) + break; + add_mhd_response_header(r, header_dup.substr(0,header_dup.find(':')).c_str(), + header_dup.substr(header_dup.find(':')).c_str()); + header_dup = header_dup.substr(newline); + } + add_mhd_last_modified (r, s.st_mtime); if (verbose > 1) obatched(clog) << "serving file from upstream debuginfod/cache" << endl; diff --git a/debuginfod/debuginfod.h.in b/debuginfod/debuginfod.h.in index c358df4..6ae8b91 100644 --- a/debuginfod/debuginfod.h.in +++ b/debuginfod/debuginfod.h.in @@ -93,6 +93,10 @@ void* debuginfod_get_user_data (debuginfod_client *client); /* Get the current or last active URL, if known. */ const char* debuginfod_get_url (debuginfod_client *client); +/* Returns all headers sent to this client which were prefixed + * with X-DEBUGINFOD */ +const char* debuginfod_get_headers(debuginfod_client *client); + /* Add an outgoing HTTP request "Header: Value". Copies string. */ int debuginfod_add_http_header (debuginfod_client *client, const char* header); diff --git a/debuginfod/libdebuginfod.map b/debuginfod/libdebuginfod.map index 7d2f588..f95b5b9 100644 --- a/debuginfod/libdebuginfod.map +++ b/debuginfod/libdebuginfod.map @@ -18,3 +18,6 @@ ELFUTILS_0.179 { ELFUTILS_0.183 { debuginfod_set_verbose_fd; } ELFUTILS_0.179; +ELFUTILS_0.189 { + debuginfod_get_headers; +} ELFUTILS_0.183; diff --git a/doc/ChangeLog b/doc/ChangeLog index 3fff1a8..c2a01a0 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,8 @@ +2022-07-15 Noah Sanci + * debuginfod_find_debuginfo.3: Explained debuginfod_get_headers + usage. + * debuginfod_get_headers.3: Created. + 2022-09-02 Aaron Merey * debuginfod.8 (-C): Update description. diff --git a/doc/debuginfod_find_debuginfo.3 b/doc/debuginfod_find_debuginfo.3 index 30cef3c..984fda1 100644 --- a/doc/debuginfod_find_debuginfo.3 +++ b/doc/debuginfod_find_debuginfo.3 @@ -58,6 +58,7 @@ OPTIONAL FUNCTIONS .BI "const char* debuginfod_get_url(debuginfod_client *" client ");" .BI "int debuginfod_add_http_header(debuginfod_client *" client "," .BI " const char* " header ");" +.BI "const char* debuginfod_get_headers(debuginfod_client *" client ");" .SH DESCRIPTION @@ -198,6 +199,18 @@ By default, the library adds a descriptive \fIUser-Agent:\fP header to outgoing requests. If the client application adds a header with the same name, this default is suppressed. +.BR \%debuginfod_get_headers () +may be called with a debuginfod client. This function will return the +http response headers prefixed with +.BR X-DEBUGINFOD +received from the first handle to get a response from a debuginfod server. +Note that all other http headers aren't stored in the libcurl header +callback function since they aren't of as much interest. The caller should +copy the returned string if it is needed beyond the release of the client object. +The returned string may be NULL if no headers are prefixed with +.BR X-DEBUGINFOD +\. + .SH "MACROS" .SS "DEBUGINFOD_SONAME" diff --git a/doc/debuginfod_get_headers.3 b/doc/debuginfod_get_headers.3 new file mode 100644 index 0000000..1db5598 --- /dev/null +++ b/doc/debuginfod_get_headers.3 @@ -0,0 +1,2 @@ +.so man3/debuginfod_find_debuginfo.3 + diff --git a/tests/ChangeLog b/tests/ChangeLog index d2952cc..659bfa1 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2022-07-15 Noah Sanci + + * run-debuginfod-response-headers.sh: Added test + to ensure that federated servers pass headers down to + queried server. + 2022-08-04 Sergei Trofimovich * low_high_pc.c (handle_die): Drop redundant 'lx' suffix. diff --git a/tests/run-debuginfod-response-headers.sh b/tests/run-debuginfod-response-headers.sh index 62c4388..e5698cc 100755 --- a/tests/run-debuginfod-response-headers.sh +++ b/tests/run-debuginfod-response-headers.sh @@ -73,17 +73,21 @@ rm -rf $DEBUGINFOD_CACHE_PATH env DEBUGINFOD_URLS="http://127.0.0.1:"$PORT1 LD_LIBRARY_PATH=$ldpath ${abs_top_builddir}/debuginfod/debuginfod-find\ -vvv executable F/prog > vlog-find$PORT1.1 2>&1 tempfiles vlog-find$PORT1.1 -grep 'Content-Length: ' vlog-find$PORT1.1 -grep 'X-DEBUGINFOD-FILE: ' vlog-find$PORT1.1 -grep 'X-DEBUGINFOD-SIZE: ' vlog-find$PORT1.1 +errfiles vlog-find$PORT1.1 +cat vlog-find$PORT1.1 +grep 'Headers:' vlog-find$PORT1.1 +grep 'X-DEBUGINFOD-FILE: prog' vlog-find$PORT1.1 +grep 'X-DEBUGINFOD-SIZE: ' vlog-find$PORT1.1 # Check to see if an executable file located in an archive prints the file's description and archive env DEBUGINFOD_URLS="http://127.0.0.1:"$PORT1 LD_LIBRARY_PATH=$ldpath ${abs_top_builddir}/debuginfod/debuginfod-find\ -vvv executable c36708a78618d597dee15d0dc989f093ca5f9120 > vlog-find$PORT1.2 2>&1 tempfiles vlog-find$PORT1.2 -grep 'Content-Length: ' vlog-find$PORT1.2 -grep 'X-DEBUGINFOD-FILE: ' vlog-find$PORT1.2 -grep 'X-DEBUGINFOD-SIZE: ' vlog-find$PORT1.2 +errfiles vlog-find$PORT1.2 +cat vlog-find$PORT1.2 +grep 'Headers:' vlog-find$PORT1.2 +grep 'X-DEBUGINFOD-FILE: ' vlog-find$PORT1.2 +grep 'X-DEBUGINFOD-SIZE: ' vlog-find$PORT1.2 grep 'X-DEBUGINFOD-ARCHIVE: ' vlog-find$PORT1.2 # Check that X-DEBUGINFOD-SIZE matches the size of each file @@ -94,6 +98,38 @@ do test $st_size -eq $x_debuginfod_size done +rm -rf $DEBUGINFOD_CACHE_PATH +BUILDID=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ + -a F/prog | grep 'Build ID' | cut -d ' ' -f 7` +netcat_dir="buildid/$BUILDID/" +mkdir -p ${PWD}/$netcat_dir +cp F/prog ${PWD}/$netcat_dir/executable +tempfiles F/prog + +# Netcat dies after answering the request +nc -l -p $PORT2 -c 'echo -e "HTTP/1.1 200 OK\nX-DEBUGINFOD-SIZE: ba:d_size\nX-DEBUGINFOD-\rFILE:\=\+ \r213\n\n $(date)"' & < ${PWD}/$netcat_dir"executable" & +# Wait until the netcat port is in use. Otherwise debuginfod-find can query +# before netcat is ready. +SECONDS=0 +nc_start=$SECONDS +while [ ! $(lsof -i -P -n | grep LISTEN | grep "nc.*$PORT2") ] +do + # If it takes longer than 5 seconds for netcat to start up, then fail + duration=$(( SECONDS - nc_start )) + if [ $SECONDS -gt 5 ] + then + err + fi +done + +env DEBUGINFOD_URLS="http://127.0.0.1:"$PORT2 LD_LIBRARY_PATH=$ldpath ${abs_top_builddir}/debuginfod/debuginfod-find\ + -vvv executable $BUILDID > vlog-find$PORT2 2>&1 +errfiles vlog-find$PORT2 +tempfiles vlog-find$PORT2 +cat vlog-find$PORT2 | grep "X-DEBUGINFOD-" +rm -f "$netcat_dir"executable +rmdir -p $netcat_dir + kill $PID1 wait $PID1 PID1=0 -- 2.7.4