Skip to content

Instantly share code, notes, and snippets.

@HeroicKatora
Last active February 1, 2019 23:07
Show Gist options
  • Save HeroicKatora/279543a2e19e2d22cad6c3562def40a5 to your computer and use it in GitHub Desktop.
Save HeroicKatora/279543a2e19e2d22cad6c3562def40a5 to your computer and use it in GitHub Desktop.
Use curl to generate dumps of HTTP/2 traffic

Custom curl

Unfortunately, curl --trace output decoded http2 which makes it pointless. Http2 over tls also doesn't produce reasonable output with strace and curl, that could otherwise be a match made in heaven. But:

You can modify curl to output the raw h2 buffer data sent and received. A patch for curl-7_63_0 will be placed in this folder, to dump that data into a temp file as nice as possible. The patch file is the other file in this gist. Don't forget to configure curl with support for nghttp2 (when building with CMake that is), and that ssl validation will not work in a configuration without ssl.

$ cd build && cmake -DUSE_NGHTTP2=ON ..

Autodections works fine for me on Linux with OpenSSL

$ ./buildconf && ./configure && make -j

The binary results in /tmp/trinaw-dump-* contain a seqence of tuples, each composed of (size, direction, data[size]) where the data types are (uint64_t, uint8_t and a string of char).

$ ./src/curl --http2 -Lso /dev/null https://youtube.com/ && stat /tmp/trinaw-dump-*

From 4553adcbe8650719ff2e1a08bfa7c0ce064d5527 Mon Sep 17 00:00:00 2001
From: Andreas Molzer <andreas.molzer@gmx.de>
Date: Thu, 31 Jan 2019 02:45:57 +0100
Subject: [PATCH] Add a most simplistic dump of all socket packets
Each block is preceeded by a single little endian header containing
length (uint64_t) and direction (bool).
---
lib/http2.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/lib/http2.c b/lib/http2.c
index 3b8088dff..da82a79c5 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -71,6 +71,18 @@
#define H2BUGF(x) do { } WHILE_FALSE
#endif
+#include "fcntl.h"
+
+static int TRINAW_TAP_CTR;
+static char TRINAW_PATH_BUF[1024];
+static int TRINAW_TAP;
+
+#define TRINAW_SEND 0
+#define TRINAW_RECV 1
+
+static int trinaw_tap_init(void);
+static ssize_t trinaw_tap_write(const char *mem, size_t len, bool recv);
+static int trinaw_close(void);
static ssize_t http2_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len, CURLcode *err);
@@ -391,6 +403,8 @@ static ssize_t send_callback(nghttp2_session *h2,
const uint8_t *data, size_t length, int flags,
void *userp)
{
+ // TRINAW – send tap
+ trinaw_tap_write((const char *)data, length, TRINAW_SEND);
struct connectdata *conn = (struct connectdata *)userp;
struct http_conn *c = &conn->proto.httpc;
ssize_t written;
@@ -1321,6 +1335,8 @@ static int h2_process_pending_input(struct connectdata *conn,
nread = httpc->inbuflen - httpc->nread_inbuf;
inbuf = httpc->inbuf + httpc->nread_inbuf;
+ // TRINAW – receive tap
+ trinaw_tap_write((const char*)inbuf, nread, TRINAW_RECV);
rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
if(rv < 0) {
failf(data,
@@ -1674,6 +1690,8 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
nread));
}
+ // TRINAW – receive tap
+ trinaw_tap_write((const char*)inbuf, nread, TRINAW_RECV);
rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
if(nghttp2_is_fatal((int)rv)) {
@@ -2252,6 +2270,8 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
memcpy(httpc->inbuf, mem, nread);
httpc->inbuflen = nread;
+ // TRINAW – receive tap
+ trinaw_tap_write((const char*)httpc->inbuf, httpc->inbuflen, TRINAW_RECV);
nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
httpc->inbuflen);
@@ -2379,6 +2399,67 @@ bool Curl_h2_http_1_1_error(struct connectdata *conn)
return (httpc->error_code == NGHTTP2_HTTP_1_1_REQUIRED);
}
+int trinaw_tap_init() {
+ int file;
+ struct timespec time;
+
+ if(TRINAW_TAP < 0)
+ return -1;
+ if(TRINAW_TAP > 0)
+ return TRINAW_TAP;
+ clock_gettime(CLOCK_REALTIME, &time);
+ if(0 > snprintf(TRINAW_PATH_BUF, sizeof(TRINAW_PATH_BUF), "/tmp/trinaw-dump-%ld-%ld", time.tv_sec, time.tv_nsec/1000)) {
+ TRINAW_TAP = -1;
+ return -1;
+ }
+ if((file = open(TRINAW_PATH_BUF, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU)) < 0) {
+ TRINAW_TAP = -1;
+ return -1;
+ }
+ TRINAW_TAP = file;
+ return file;
+}
+
+ssize_t trinaw_tap_write(const char *mem, size_t len, bool recv) {
+ int file;
+ size_t len_serialize;
+ char header[9];
+ ssize_t write_now;
+ ssize_t written = 0;
+
+ len_serialize = len;
+ for(int i = 0; i < 8; i++) {
+ header[i] = (len_serialize % 256);
+ len_serialize /= 256;
+ }
+ header[8] = recv;
+
+ if((file = trinaw_tap_init()) < 0) {
+ return 0;
+ }
+
+ if((write_now = write(file, header, 9)) < 9) {
+ return 0;
+ }
+ written += write_now;
+
+ if((write_now = write(file, mem, len)) < 0) {
+ return write_now;
+ }
+
+ return written + write_now;
+}
+
+int trinaw_close() {
+ int file;
+ if((file = trinaw_tap_init()) > 0) {
+ int result = close(file);
+ TRINAW_TAP = 0;
+ return result;
+ }
+ return 0;
+}
+
#else /* !USE_NGHTTP2 */
/* Satisfy external references even if http2 is not compiled in. */
--
2.20.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment