+2020-03-24 Frank Ch. Eigler <fche@redhat.com>
+
+ * debuginfod.h, libdebuginfod.map: New functions for _add_url_header.
+ * debuginfod-client.c (struct debuginfod_client): Add headers fields.
+ (debuginfod_add_http_header): New client api to add outgoing headers.
+ (add_default_headers): Renamed from add_extra_headers, skip if flag.
+ (debuginfod_query_server): Pass accumulated headers to libcurl.
+ (debuginfod_end): Clean accumulated headers.
+ (debuginfod_find_*): Add default headers at this point.
+ * debuginfod.cxx (handle_buildid): Add conn pointer. Use it to relay
+ incoming UA and XFF headers to federated upstream debuginfods.
+
2020-03-26 Frank Ch. Eigler <fche@redhat.com>
* debuginfod.cxx (handler_cb): Export two families of metrics for
/* Stores current/last url, if any. */
char* url;
+ /* Accumulates outgoing http header names/values. */
+ int user_agent_set_p; /* affects add_default_headers */
+ struct curl_slist *headers;
+
/* Can contain all other context, like cache_path, server_urls,
timeout or other info gotten from environment variables, the
handle data, etc. So those don't have to be reparsed and
static void
-add_extra_headers(CURL *handle)
+add_default_headers(debuginfod_client *client)
{
+ if (client->user_agent_set_p)
+ return;
+
/* Compute a User-Agent: string to send. The more accurately this
describes this host, the likelier that the debuginfod servers
might be able to locate debuginfo for us. */
}
char *ua = NULL;
- rc = asprintf(& ua, "%s/%s,%s,%s/%s",
+ rc = asprintf(& ua, "User-Agent: %s/%s,%s,%s/%s",
PACKAGE_NAME, PACKAGE_VERSION,
utspart ?: "",
id ?: "",
ua = NULL;
if (ua)
- curl_easy_setopt(handle, CURLOPT_USERAGENT, (void*) ua); /* implicit strdup */
+ (void) debuginfod_add_http_header (client, ua);
free (ua);
free (id);
free (c->url);
c->url = NULL;
+ add_default_headers(c);
+
/* Is there any server we can query? If not, don't do any work,
just return with ENOSYS. Don't even access the cache. */
urls_envvar = getenv(server_urls_envvar);
#endif
curl_easy_setopt(data[i].handle, CURLOPT_AUTOREFERER, (long) 1);
curl_easy_setopt(data[i].handle, CURLOPT_ACCEPT_ENCODING, "");
- add_extra_headers(data[i].handle);
+ curl_easy_setopt(data[i].handle, CURLOPT_HTTPHEADER, c->headers);
curl_multi_add_handle(curlm, data[i].handle);
server_url = strtok_r(NULL, url_delim, &strtok_saveptr);
void
debuginfod_end (debuginfod_client *client)
{
+ if (client == NULL)
+ return;
+
+ curl_slist_free_all (client->headers);
free (client->url);
free (client);
}
}
+/* Add an outgoing HTTP header. */
+int debuginfod_add_http_header (debuginfod_client *client, const char* header)
+{
+ struct curl_slist *temp = curl_slist_append (client->headers, header);
+ if (temp == NULL)
+ return -ENOMEM;
+
+ /* Track if User-Agent: is being set. If so, signal not to add the
+ default one. */
+ if (strncmp (header, "User-Agent:", 11) == 0)
+ client->user_agent_set_p = 1;
+
+ client->headers = temp;
+ return 0;
+}
+
+
void
debuginfod_set_progressfn(debuginfod_client *client,
debuginfod_progressfn_t fn)
}
-static struct MHD_Response* handle_buildid (const string& buildid /* unsafe */,
- const string& artifacttype /* unsafe */,
- const string& suffix /* unsafe */,
- int *result_fd
- )
+static struct MHD_Response*
+handle_buildid (MHD_Connection* conn,
+ const string& buildid /* unsafe */,
+ const string& artifacttype /* unsafe */,
+ const string& suffix /* unsafe */,
+ int *result_fd)
{
// validate artifacttype
string atype_code;
{
debuginfod_set_progressfn (client, & debuginfod_find_progress);
+ if (conn)
+ {
+ // Transcribe incoming User-Agent:
+ string ua = MHD_lookup_connection_value (conn, MHD_HEADER_KIND, "User-Agent") ?: "";
+ string ua_complete = string("User-Agent: ") + ua;
+ debuginfod_add_http_header (client, ua_complete.c_str());
+
+ // Compute larger XFF:, for avoiding info loss during
+ // federation, and for future cyclicity detection.
+ string xff = MHD_lookup_connection_value (conn, MHD_HEADER_KIND, "X-Forwarded-For") ?: "";
+ if (xff != "")
+ xff += string(", "); // comma separated list
+
+ // Compute the client's numeric IP address only - so can't merge with conninfo()
+ const union MHD_ConnectionInfo *u = MHD_get_connection_info (conn,
+ MHD_CONNECTION_INFO_CLIENT_ADDRESS);
+ struct sockaddr *so = u ? u->client_addr : 0;
+ char hostname[256] = ""; // RFC1035
+ if (so && so->sa_family == AF_INET)
+ (void) getnameinfo (so, sizeof (struct sockaddr_in), hostname, sizeof (hostname), NULL, 0,
+ NI_NUMERICHOST);
+ else if (so && so->sa_family == AF_INET6)
+ (void) getnameinfo (so, sizeof (struct sockaddr_in6), hostname, sizeof (hostname), NULL, 0,
+ NI_NUMERICHOST);
+
+ string xff_complete = string("X-Forwarded-For: ")+xff+string(hostname);
+ debuginfod_add_http_header (client, xff_complete.c_str());
+ }
+
if (artifacttype == "debuginfo")
fd = debuginfod_find_debuginfo (client,
(const unsigned char*) buildid.c_str(),
}
inc_metric("http_requests_total", "type", artifacttype);
-
// get the resulting fd so we can report its size
int fd;
- r = handle_buildid(buildid, artifacttype, suffix, &fd);
+ r = handle_buildid(connection, buildid, artifacttype, suffix, &fd);
if (r)
{
struct stat fs;
struct MHD_Response *r = 0;
try
{
- r = handle_buildid (buildid, "debuginfo", "", &alt_fd);
+ r = handle_buildid (0, buildid, "debuginfo", "", &alt_fd);
}
catch (const reportable_exception& e)
{
/* Get the current or last active URL, if known. */
const char* debuginfod_get_url (debuginfod_client *client);
+/* Add an outgoing HTTP request "Header: Value". Copies string. */
+int debuginfod_add_http_header (debuginfod_client *client, const char* header);
+
/* Release debuginfod client connection context handle. */
void debuginfod_end (debuginfod_client *client);
debuginfod_set_user_data;
debuginfod_get_user_data;
debuginfod_get_url;
+ debuginfod_add_http_header;
};
+2020-03-24 Frank Ch. Eigler <fche@redhat.com>
+
+ * debuginfod_add_http_header.3: New function, documented ...
+ * debuginfod_find_debuginfo.3: ... here.
+ * Makefile.am (notrans_dist_*_man3): Add it.
+
2020-03-26 Frank Ch. Eigler <fche@redhat.com>
* debuginfod.8 (-R): Note zstd compression complications
if DEBUGINFOD
notrans_dist_man8_MANS += debuginfod.8
+notrans_dist_man3_MANS += debuginfod_add_http_header.3
notrans_dist_man3_MANS += debuginfod_begin.3
notrans_dist_man3_MANS += debuginfod_end.3
notrans_dist_man3_MANS += debuginfod_find_debuginfo.3
--- /dev/null
+.so man3/debuginfod_find_debuginfo.3
.BI " void *" data ");"
.BI "void* debuginfod_get_user_data(debuginfod_client *" client ");"
.BI "const char* debuginfod_get_url(debuginfod_client *" client ");"
+.BI "int debuginfod_add_http_header(debuginfod_client *" client ","
+.BI " const char* " header ");"
.SH DESCRIPTION
or freed. The caller should copy it if it is needed beyond the release
of the client object.
+.SS "HTTP HEADER"
+
+Before a lookup function is initiated, a client application may
+add HTTP request headers to future downloads.
+.BR \%debuginfod_add_http_header ()
+may be called with strings of the form
+.BR \%"Header:\~value" .
+These strings are copied by the library. A zero return value
+indicates success, but out-of-memory conditions may result in
+a non-zero \fI-ENOMEM\fP.
+
+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.
+
.SH "CACHE"
If the query is successful, the \fBdebuginfod_find_*\fP() functions save
the target file to a local cache. The location of the cache is controlled
+2020-03-24 Frank Ch. Eigler <fche@redhat.com>
+
+ * run-debuginfod-find.sh: Test relay of UA and XFF headers across
+ federating debuginfods.
+
2020-03-26 Frank Ch. Eigler <fche@redhat.com>
* run-debuginfod-find.sh: Look for debuginfod's new
fi
}
-env LD_LIBRARY_PATH=$ldpath DEBUGINFOD_URLS= ${abs_builddir}/../debuginfod/debuginfod $VERBOSE -F -R -d $DB -p $PORT1 -t0 -g0 --fdcache-fds 1 --fdcache-mbs 2 -Z .tar.xz -Z .tar.bz2=bzcat R F Z L &
+env LD_LIBRARY_PATH=$ldpath DEBUGINFOD_URLS= ${abs_builddir}/../debuginfod/debuginfod $VERBOSE -F -R -d $DB -p $PORT1 -t0 -g0 --fdcache-fds 1 --fdcache-mbs 2 -Z .tar.xz -Z .tar.bz2=bzcat -v R F Z L > vlog4 2>&1 &
PID1=$!
+tempfiles vlog4
# Server must become ready
wait_ready $PORT1 'ready' 1
export DEBUGINFOD_URLS=http://127.0.0.1:$PORT1/ # or without trailing /
rm -rf $DEBUGINFOD_CACHE_PATH
testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID
+# send a request to stress XFF and User-Agent federation relay;
+# we'll grep for the two patterns in vlog4
+curl -s -H 'User-Agent: TESTCURL' -H 'X-Forwarded-For: TESTXFF' $DEBUGINFOD_URLS/buildid/deaddeadbeef00000000/debuginfo -o /dev/null || true
+
+grep UA:TESTCURL vlog4
+grep XFF:TESTXFF vlog4
+
+
# confirm that first server can't resolve symlinked info in L/ but second can
BUILDID=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \
-a L/foo | grep 'Build ID' | cut -d ' ' -f 7`