Skip to content

Commit e142fe2

Browse files
Dave Pachecory
authored andcommitted
DTrace probes: support X-Forwarded-For
INTRO-385
1 parent b6a742d commit e142fe2

4 files changed

Lines changed: 136 additions & 18 deletions

File tree

src/node.d

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,30 +64,96 @@ translator node_connection_t <node_dtrace_connection_t *nc> {
6464
&((node_dtrace_connection64_t *)nc)->buffered, sizeof (int32_t));
6565
};
6666

67+
typedef struct {
68+
uint32_t version;
69+
} node_dtrace_http_request_t;
70+
6771
typedef struct {
6872
uint32_t url;
6973
uint32_t method;
70-
} node_dtrace_http_request_t;
74+
} node_dtrace_http_request_v0_t;
75+
76+
typedef struct {
77+
uint32_t version;
78+
uint32_t url;
79+
uint32_t method;
80+
uint32_t forwardedFor;
81+
} node_dtrace_http_request_v1_t;
7182

7283
typedef struct {
7384
uint64_t url;
7485
uint64_t method;
75-
} node_dtrace_http_request64_t;
86+
} node_dtrace_http_request64_v0_t;
87+
88+
typedef struct {
89+
uint32_t version;
90+
uint32_t pad;
91+
uint64_t url;
92+
uint64_t method;
93+
uint64_t forwardedFor;
94+
} node_dtrace_http_request64_v1_t;
7695

7796
typedef struct {
7897
string url;
7998
string method;
99+
string forwardedFor;
80100
} node_http_request_t;
81101

102+
/*
103+
* This translator is even filthier than usual owing to our attempts to
104+
* maintain backwards compatibility. Previous versions of node used an
105+
* http_request struct that had fields for "url" and "method". The current
106+
* version also provides a "forwardedFor" field. To distinguish the binary
107+
* representations of these structs, the new version also prepends a "version"
108+
* member (where the old one has a "url" pointer). So each field that we're
109+
* translating below first switches on the value of this "version" field: if
110+
* it's larger than 4096, we know we must be looking at the "url" pointer of
111+
* the older structure version. Otherwise, we must be looking at the new
112+
* version. Besides this, we have the usual switch based on the userland
113+
* process data model. This would all be simpler with macros, but those aren't
114+
* available in delivered D library files since that would make DTrace
115+
* dependent on cpp, which isn't always available.
116+
*/
82117
translator node_http_request_t <node_dtrace_http_request_t *nd> {
83-
url = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
84-
copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nd->url,
85-
sizeof (int32_t))) :
86-
copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
87-
&((node_dtrace_http_request64_t *)nd)->url, sizeof (int64_t)));
88-
method = curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
89-
copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)&nd->method,
90-
sizeof (int32_t))) :
91-
copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
92-
&((node_dtrace_http_request64_t *)nd)->method, sizeof (int64_t)));
118+
url = (*(uint32_t *)copyin((uintptr_t)&nd->version, sizeof (uint32_t))) >= 4096 ?
119+
(curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
120+
copyinstr(*(uint32_t *)copyin((uintptr_t)
121+
&((node_dtrace_http_request_v0_t *)nd)->url,
122+
sizeof (uint32_t))) :
123+
copyinstr(*(uint64_t *)copyin((uintptr_t)
124+
&((node_dtrace_http_request64_v0_t *)nd)->url,
125+
sizeof (uint64_t)))) :
126+
(curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
127+
copyinstr(*(uint32_t *)copyin((uintptr_t)
128+
&((node_dtrace_http_request_v1_t *)nd)->url,
129+
sizeof (uint32_t))) :
130+
copyinstr(*(uint64_t *)copyin((uintptr_t)
131+
&((node_dtrace_http_request64_v1_t *)nd)->url,
132+
sizeof (uint64_t))));
133+
134+
method = (*(uint32_t *)copyin((uintptr_t)&nd->version, sizeof (uint32_t))) >= 4096 ?
135+
(curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
136+
copyinstr(*(uint32_t *)copyin((uintptr_t)
137+
&((node_dtrace_http_request_v0_t *)nd)->method,
138+
sizeof (uint32_t))) :
139+
copyinstr(*(uint64_t *)copyin((uintptr_t)
140+
&((node_dtrace_http_request64_v0_t *)nd)->method,
141+
sizeof (uint64_t)))) :
142+
(curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
143+
copyinstr(*(uint32_t *)copyin((uintptr_t)
144+
&((node_dtrace_http_request_v1_t *)nd)->method,
145+
sizeof (uint32_t))) :
146+
copyinstr(*(uint64_t *)copyin((uintptr_t)
147+
&((node_dtrace_http_request64_v1_t *)nd)->method,
148+
sizeof (uint64_t))));
149+
150+
forwardedFor = (*(uint32_t *)
151+
copyin((uintptr_t)&nd->version, sizeof (uint32_t))) >= 4096 ? "" :
152+
(curpsinfo->pr_dmodel == PR_MODEL_ILP32 ?
153+
copyinstr(*(uint32_t *)copyin((uintptr_t)
154+
&((node_dtrace_http_request_v1_t *)nd)->forwardedFor,
155+
sizeof (uint32_t))) :
156+
copyinstr(*(uint64_t *)copyin((uintptr_t)
157+
&((node_dtrace_http_request64_v1_t *)nd)->forwardedFor,
158+
sizeof (uint64_t))));
93159
};

src/node_dtrace.cc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

2222
#include <node_dtrace.h>
23+
#include <strings.h>
2324

2425
#ifdef HAVE_DTRACE
2526
#include "node_provider.h"
@@ -180,18 +181,33 @@ Handle<Value> DTRACE_NET_SOCKET_WRITE(const Arguments& args) {
180181
}
181182

182183
Handle<Value> DTRACE_HTTP_SERVER_REQUEST(const Arguments& args) {
183-
node_dtrace_http_request_t req;
184+
node_dtrace_http_server_request_t req;
184185

185186
if (!NODE_HTTP_SERVER_REQUEST_ENABLED())
186187
return Undefined();
187188

188189
HandleScope scope;
189190

190191
Local<Object> arg0 = Local<Object>::Cast(args[0]);
192+
Local<Object> headers;
191193

194+
bzero(&req, sizeof(req));
195+
req._un.version = 1;
192196
SLURP_STRING(arg0, url, &req.url);
193197
SLURP_STRING(arg0, method, &req.method);
194198

199+
SLURP_OBJECT(arg0, headers, &headers);
200+
201+
if (!(headers)->IsObject())
202+
return (ThrowException(Exception::Error(String::New("expected "
203+
"object for request to contain string member headers"))));
204+
205+
Local<Value> strfwdfor = headers->Get(String::New("x-forwarded-for"));
206+
String::Utf8Value fwdfor(strfwdfor->ToString());
207+
208+
if (!strfwdfor->IsString() || (req.forwardedFor = *fwdfor) == NULL)
209+
req.forwardedFor = "";
210+
195211
SLURP_CONNECTION(args[1], conn);
196212

197213
NODE_HTTP_SERVER_REQUEST(&req, &conn);
@@ -211,7 +227,7 @@ Handle<Value> DTRACE_HTTP_SERVER_RESPONSE(const Arguments& args) {
211227
}
212228

213229
Handle<Value> DTRACE_HTTP_CLIENT_REQUEST(const Arguments& args) {
214-
node_dtrace_http_request_t req;
230+
node_dtrace_http_client_request_t req;
215231
char *header;
216232

217233
if (!NODE_HTTP_CLIENT_REQUEST_ENABLED())

src/node_dtrace.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@
2727

2828
extern "C" {
2929

30+
/*
31+
* The following structures are passed directly to DTrace when probes are fired.
32+
* Translators in node.d translate these structures into the corresponding D
33+
* structures, taking care of dealing with the user process data model (32-bit
34+
* or 64-bit) and structure versions (see node_dtrace_http_server_request_t
35+
* below).
36+
*/
37+
3038
typedef struct {
3139
int32_t fd;
3240
int32_t port;
@@ -37,7 +45,31 @@ typedef struct {
3745
typedef struct {
3846
char *url;
3947
char *method;
40-
} node_dtrace_http_request_t;
48+
} node_dtrace_http_client_request_t;
49+
50+
/*
51+
* The original version of this structure contained only a url and method, just
52+
* like the client request above. To add the new forwardedFor field, the
53+
* structure layout was changed to begin with an integer version. The
54+
* translator knows whether it's looking at an old- or new-version structure
55+
* based on whether the version field's value is a reasonable pointer (i.e.
56+
* address greater than 4K). No doubt this is filthy, but there's not much else
57+
* we can do, and it works reliably.
58+
*
59+
* This version of the structure also contains padding that should be zeroed out
60+
* by the consumer so that future versions of the translator can simply check if
61+
* a field is present by checking it against NULL.
62+
*/
63+
typedef struct {
64+
union {
65+
uint32_t version;
66+
uintptr_t unused; /* for compat. with old 64-bit struct */
67+
} _un;
68+
char *url;
69+
char *method;
70+
char *forwardedFor;
71+
char *_pad[8];
72+
} node_dtrace_http_server_request_t;
4173

4274
}
4375

src/node_provider.d

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ typedef struct {
4141

4242
typedef struct {
4343
int dummy;
44-
} node_dtrace_http_request_t;
44+
} node_dtrace_http_server_request_t;
45+
46+
typedef struct {
47+
int dummy;
48+
} node_dtrace_http_client_request_t;
4549

4650
typedef struct {
4751
int dummy;
@@ -56,12 +60,12 @@ provider node {
5660
(node_connection_t *c, int b);
5761
probe net__socket__write(node_dtrace_connection_t *c, int b) :
5862
(node_connection_t *c, int b);
59-
probe http__server__request(node_dtrace_http_request_t *h,
63+
probe http__server__request(node_dtrace_http_server_request_t *h,
6064
node_dtrace_connection_t *c) :
6165
(node_http_request_t *h, node_connection_t *c);
6266
probe http__server__response(node_dtrace_connection_t *c) :
6367
(node_connection_t *c);
64-
probe http__client__request(node_dtrace_http_request_t *h,
68+
probe http__client__request(node_dtrace_http_client_request_t *h,
6569
node_dtrace_connection_t *c) :
6670
(node_http_request_t *h, node_connection_t *c);
6771
probe http__client__response(node_dtrace_connection_t *c) :

0 commit comments

Comments
 (0)