diff options
Diffstat (limited to 'web/attachments/427519-http-parser.bundle')
-rw-r--r-- | web/attachments/427519-http-parser.bundle | 1396 |
1 files changed, 1396 insertions, 0 deletions
diff --git a/web/attachments/427519-http-parser.bundle b/web/attachments/427519-http-parser.bundle new file mode 100644 index 0000000..74ab9e4 --- /dev/null +++ b/web/attachments/427519-http-parser.bundle | |||
@@ -0,0 +1,1396 @@ | |||
1 | From 256d8d15acf98ee405f79b75a52692531d173f49 Mon Sep 17 00:00:00 2001 | ||
2 | From: Ferenc Wagner <wferi@niif.hu> | ||
3 | Date: Wed, 2 Nov 2011 10:03:38 +0100 | ||
4 | Subject: [PATCH 0/9] check_http: add support for chunked encoding via http-parser | ||
5 | |||
6 | Hi, | ||
7 | |||
8 | Here is an attempt to add support for chunked encoding in check_http | ||
9 | by switching to the third-party HTTP parser from Joyent hosted at | ||
10 | https://github.com/joyent/http-parser. The switch makes much of the | ||
11 | in-house ad-hoc parsing code redundant, and induces some changes in | ||
12 | the semantics of the -e switch, but that should be regarded as a step | ||
13 | forward, since the role of that switch was to verify HTTP correctness | ||
14 | (at least in part) and now we have a much better mechanism for that: | ||
15 | the parser itself. The pagesize limits (-m switch) also gained a | ||
16 | clearer semantics: they check the content size now, as that may be | ||
17 | available even when -N is used, and does not depend on the transfer | ||
18 | encoding. | ||
19 | |||
20 | Ferenc Wagner (9): | ||
21 | check_http: do not print page length if body was not read | ||
22 | Add lib/http-parser submodule and adjust Makefile.am to use it in | ||
23 | check_http | ||
24 | check_http: move status code and redirect location parsing to | ||
25 | http-parser | ||
26 | check_http: whitespace change to fix up indentation by removing dummy | ||
27 | block | ||
28 | check_http: constify and rename the location argument for redir() | ||
29 | check_http: make list of parsed headers configurable | ||
30 | check_http: use header parsing machinery in check_document_dates() | ||
31 | check_http: also read the response content via http-parse | ||
32 | check_http: check content length instead of response length | ||
33 | |||
34 | .gitmodules | 3 + | ||
35 | lib/Makefile.am | 10 +- | ||
36 | lib/http-parser | 1 + | ||
37 | plugins/Makefile.am | 9 +- | ||
38 | plugins/check_http.c | 596 ++++++++++++++++++++------------------------------ | ||
39 | 5 files changed, 256 insertions(+), 363 deletions(-) | ||
40 | create mode 100644 .gitmodules | ||
41 | create mode 160000 lib/http-parser | ||
42 | |||
43 | -- | ||
44 | 1.7.2.5 | ||
45 | |||
46 | From abc1d1c539376cb5531a9686e581ff62acf421e5 Mon Sep 17 00:00:00 2001 | ||
47 | From: Ferenc Wagner <wferi@niif.hu> | ||
48 | Date: Mon, 31 Oct 2011 15:10:54 +0100 | ||
49 | Subject: [PATCH 1/9] check_http: do not print page length if body was not read | ||
50 | |||
51 | |||
52 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
53 | --- | ||
54 | plugins/check_http.c | 2 +- | ||
55 | 1 files changed, 1 insertions(+), 1 deletions(-) | ||
56 | |||
57 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
58 | index 433c28e..6df9d23 100644 | ||
59 | --- a/plugins/check_http.c | ||
60 | +++ b/plugins/check_http.c | ||
61 | @@ -923,7 +923,7 @@ check_http (void) | ||
62 | /* leave full_page untouched so we can free it later */ | ||
63 | page = full_page; | ||
64 | |||
65 | - if (verbose) | ||
66 | + if (verbose && !no_body) | ||
67 | printf ("%s://%s:%d%s is %d characters\n", | ||
68 | use_ssl ? "https" : "http", server_address, | ||
69 | server_port, server_url, (int)pagesize); | ||
70 | -- | ||
71 | 1.7.2.5 | ||
72 | |||
73 | |||
74 | From 27536d84b73489308f129ff5623ffd1445dfc5fb Mon Sep 17 00:00:00 2001 | ||
75 | From: Ferenc Wagner <wferi@niif.hu> | ||
76 | Date: Mon, 31 Oct 2011 15:14:38 +0100 | ||
77 | Subject: [PATCH 2/9] Add lib/http-parser submodule and adjust Makefile.am to use it in check_http | ||
78 | |||
79 | |||
80 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
81 | --- | ||
82 | .gitmodules | 3 +++ | ||
83 | lib/Makefile.am | 10 +++++++++- | ||
84 | lib/http-parser | 1 + | ||
85 | plugins/Makefile.am | 9 +++++---- | ||
86 | 4 files changed, 18 insertions(+), 5 deletions(-) | ||
87 | create mode 100644 .gitmodules | ||
88 | create mode 160000 lib/http-parser | ||
89 | |||
90 | diff --git a/.gitmodules b/.gitmodules | ||
91 | new file mode 100644 | ||
92 | index 0000000..5270743 | ||
93 | --- /dev/null | ||
94 | +++ b/.gitmodules | ||
95 | @@ -0,0 +1,3 @@ | ||
96 | +[submodule "lib/http-parser"] | ||
97 | + path = lib/http-parser | ||
98 | + url = git://github.com/joyent/http-parser.git | ||
99 | diff --git a/lib/Makefile.am b/lib/Makefile.am | ||
100 | index 99fa591..561b7cf 100644 | ||
101 | --- a/lib/Makefile.am | ||
102 | +++ b/lib/Makefile.am | ||
103 | @@ -2,7 +2,7 @@ | ||
104 | |||
105 | SUBDIRS = . tests | ||
106 | |||
107 | -noinst_LIBRARIES = libnagiosplug.a | ||
108 | +noinst_LIBRARIES = libnagiosplug.a http-parser/libhttp_parser.a | ||
109 | |||
110 | AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" | ||
111 | |||
112 | @@ -18,3 +18,11 @@ INCLUDES = -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/p | ||
113 | test test-debug: | ||
114 | cd tests && make $@ | ||
115 | |||
116 | +http-parser/libhttp_parser.a: | ||
117 | + cd http-parser && $(MAKE) package | ||
118 | + | ||
119 | +mostlyclean-local: | ||
120 | + cd http-parser && $(MAKE) clean | ||
121 | + | ||
122 | +check-local: | ||
123 | + cd http-parser && $(MAKE) test | ||
124 | diff --git a/lib/http-parser b/lib/http-parser | ||
125 | new file mode 160000 | ||
126 | index 0000000..f1d48aa | ||
127 | --- /dev/null | ||
128 | +++ b/lib/http-parser | ||
129 | @@ -0,0 +1 @@ | ||
130 | +Subproject commit f1d48aa31c932f80a64122a75a87bc909b4073f9 | ||
131 | diff --git a/plugins/Makefile.am b/plugins/Makefile.am | ||
132 | index 36a28b0..2df105e 100644 | ||
133 | --- a/plugins/Makefile.am | ||
134 | +++ b/plugins/Makefile.am | ||
135 | @@ -13,7 +13,7 @@ AM_CFLAGS = -DNP_VERSION='"$(NP_VERSION)"' | ||
136 | |||
137 | VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t | ||
138 | |||
139 | -INCLUDES = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl @LDAPINCLUDE@ @PGINCLUDE@ @SSLINCLUDE@ | ||
140 | +INCLUDES = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/lib/http-parser -I$(top_srcdir)/gl -I$(top_srcdir)/intl @LDAPINCLUDE@ @PGINCLUDE@ @SSLINCLUDE@ | ||
141 | |||
142 | localedir = $(datadir)/locale | ||
143 | # gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this | ||
144 | @@ -48,6 +48,7 @@ BASEOBJS = utils.o ../lib/libnagiosplug.a ../gl/libgnu.a | ||
145 | NETOBJS = netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) | ||
146 | SSLOBJS = sslutils.o | ||
147 | NETLIBS = $(NETOBJS) $(SOCKETLIBS) | ||
148 | +HTTPPARSER = ../lib/http-parser/libhttp_parser.a | ||
149 | |||
150 | TESTS_ENVIRONMENT = perl -I $(top_builddir) -I $(top_srcdir) | ||
151 | |||
152 | @@ -70,7 +71,7 @@ check_dns_LDADD = $(NETLIBS) runcmd.o | ||
153 | check_dummy_LDADD = $(BASEOBJS) | ||
154 | check_fping_LDADD = $(NETLIBS) popen.o | ||
155 | check_game_LDADD = $(BASEOBJS) runcmd.o | ||
156 | -check_http_LDADD = $(SSLOBJS) $(NETLIBS) $(SSLLIBS) | ||
157 | +check_http_LDADD = $(SSLOBJS) $(NETLIBS) $(SSLLIBS) $(HTTPPARSER) | ||
158 | check_hpjd_LDADD = $(NETLIBS) popen.o | ||
159 | check_ldap_LDADD = $(NETLIBS) $(LDAPLIBS) | ||
160 | check_load_LDADD = $(BASEOBJS) popen.o | ||
161 | @@ -115,7 +116,7 @@ check_dns_DEPENDENCIES = check_dns.c $(NETOBJS) runcmd.o $(DEPLIBS) | ||
162 | check_dummy_DEPENDENCIES = check_dummy.c $(DEPLIBS) | ||
163 | check_fping_DEPENDENCIES = check_fping.c $(NETOBJS) popen.o $(DEPLIBS) | ||
164 | check_game_DEPENDENCIES = check_game.c $(DEPLIBS) runcmd.o | ||
165 | -check_http_DEPENDENCIES = check_http.c $(SSLOBJS) $(NETOBJS) $(DEPLIBS) | ||
166 | +check_http_DEPENDENCIES = check_http.c $(SSLOBJS) $(NETOBJS) $(DEPLIBS) $(HTTPPARSER) | ||
167 | check_hpjd_DEPENDENCIES = check_hpjd.c $(NETOBJS) popen.o $(DEPLIBS) | ||
168 | check_ide_smart_DEPENDENCIES = check_ide_smart.c $(BASEOBJS) $(DEPLIBS) | ||
169 | check_ldap_DEPENDENCIES = check_ldap.c $(NETOBJS) $(DEPLIBS) | ||
170 | @@ -170,7 +171,7 @@ install-exec-hook: | ||
171 | cd $(DESTDIR)$(libexecdir) && \ | ||
172 | for i in $(check_tcp_programs) ; do rm -f $$i; ln -s check_tcp $$i ; done ;\ | ||
173 | if [ -x check_ldap ] ; then rm -f check_ldaps ; ln -s check_ldap check_ldaps ; fi | ||
174 | - | ||
175 | + | ||
176 | clean-local: | ||
177 | rm -f $(check_tcp_programs) | ||
178 | rm -f NP-VERSION-FILE | ||
179 | -- | ||
180 | 1.7.2.5 | ||
181 | |||
182 | |||
183 | From e8b35f17bf23515dc43233ebbde1c0ccb11d5ecb Mon Sep 17 00:00:00 2001 | ||
184 | From: Ferenc Wagner <wferi@niif.hu> | ||
185 | Date: Mon, 31 Oct 2011 15:20:48 +0100 | ||
186 | Subject: [PATCH 3/9] check_http: move status code and redirect location parsing to http-parser | ||
187 | |||
188 | This changes the semantics of the -e switch, as http-parser doesn't make | ||
189 | the status string available. But it does substantial HTTP verification, | ||
190 | so much of the original functionality will be regained once HTTP parsing | ||
191 | errors become CRITICAL. | ||
192 | |||
193 | This introduces some new gettext strings. | ||
194 | |||
195 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
196 | --- | ||
197 | plugins/check_http.c | 264 +++++++++++++++++++++++++------------------------- | ||
198 | 1 files changed, 131 insertions(+), 133 deletions(-) | ||
199 | |||
200 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
201 | index 6df9d23..ada7a95 100644 | ||
202 | --- a/plugins/check_http.c | ||
203 | +++ b/plugins/check_http.c | ||
204 | @@ -41,6 +41,7 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; | ||
205 | #include "netutils.h" | ||
206 | #include "utils.h" | ||
207 | #include "base64.h" | ||
208 | +#include "http_parser.h" | ||
209 | #include <ctype.h> | ||
210 | |||
211 | #define INPUT_DELIMITER ";" | ||
212 | @@ -48,7 +49,15 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; | ||
213 | #define STICKY_HOST 1 | ||
214 | #define STICKY_PORT 2 | ||
215 | |||
216 | -#define HTTP_EXPECT "HTTP/1." | ||
217 | +struct parser_data_t { | ||
218 | + enum http_errno prev_callback; /* use HPE_CB_ macros for identifying callbacks */ | ||
219 | + char *current_header; | ||
220 | + size_t current_header_length; | ||
221 | + int parsing_location; | ||
222 | + char *location; | ||
223 | + size_t location_length; | ||
224 | +}; | ||
225 | + | ||
226 | enum { | ||
227 | MAX_IPV4_HOSTLENGTH = 255, | ||
228 | HTTP_PORT = 80, | ||
229 | @@ -97,8 +106,7 @@ char *host_name; | ||
230 | char *server_url; | ||
231 | char *user_agent; | ||
232 | int server_url_length; | ||
233 | -int server_expect_yn = 0; | ||
234 | -char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; | ||
235 | +int status_expect = -1; | ||
236 | char string_expect[MAX_INPUT_BUFFER] = ""; | ||
237 | char output_string_search[30] = ""; | ||
238 | char *warning_thresholds = NULL; | ||
239 | @@ -126,7 +134,7 @@ char buffer[MAX_INPUT_BUFFER]; | ||
240 | |||
241 | int process_arguments (int, char **); | ||
242 | int check_http (void); | ||
243 | -void redir (char *pos, char *status_line); | ||
244 | +void redir (char *pos); | ||
245 | int server_type_check(const char *type); | ||
246 | int server_port_check(int ssl_flag); | ||
247 | char *perfd_time (double microsec); | ||
248 | @@ -367,10 +375,8 @@ process_arguments (int argc, char **argv) | ||
249 | strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1); | ||
250 | string_expect[MAX_INPUT_BUFFER - 1] = 0; | ||
251 | break; | ||
252 | - case 'e': /* string or substring */ | ||
253 | - strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1); | ||
254 | - server_expect[MAX_INPUT_BUFFER - 1] = 0; | ||
255 | - server_expect_yn = 1; | ||
256 | + case 'e': /* expected HTTP response code */ | ||
257 | + status_expect = atoi (optarg); | ||
258 | break; | ||
259 | case 'T': /* Content-type */ | ||
260 | asprintf (&http_content_type, "%s", optarg); | ||
261 | @@ -589,26 +595,6 @@ parse_time_string (const char *string) | ||
262 | } | ||
263 | } | ||
264 | |||
265 | -/* Checks if the server 'reply' is one of the expected 'statuscodes' */ | ||
266 | -static int | ||
267 | -expected_statuscode (const char *reply, const char *statuscodes) | ||
268 | -{ | ||
269 | - char *expected, *code; | ||
270 | - int result = 0; | ||
271 | - | ||
272 | - if ((expected = strdup (statuscodes)) == NULL) | ||
273 | - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); | ||
274 | - | ||
275 | - for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ",")) | ||
276 | - if (strstr (reply, code) != NULL) { | ||
277 | - result = 1; | ||
278 | - break; | ||
279 | - } | ||
280 | - | ||
281 | - free (expected); | ||
282 | - return result; | ||
283 | -} | ||
284 | - | ||
285 | static int | ||
286 | check_document_dates (const char *headers, char **msg) | ||
287 | { | ||
288 | @@ -772,16 +758,78 @@ prepend_slash (char *path) | ||
289 | return newpath; | ||
290 | } | ||
291 | |||
292 | +/* | ||
293 | +Returns 0 on success, 1 if allocation fails. | ||
294 | +Works on binary data, but also zero-terminates the result | ||
295 | +for easier string handling. | ||
296 | +*/ | ||
297 | +int | ||
298 | +append (char **orig, size_t *orig_len, const char *extra, size_t len) | ||
299 | +{ | ||
300 | + *orig = realloc (*orig, *orig_len + len + 1); | ||
301 | + if (!*orig) return 1; | ||
302 | + memcpy (*orig + *orig_len, extra, len); | ||
303 | + *orig_len += len; | ||
304 | + (*orig)[*orig_len] = 0; | ||
305 | + return 0; | ||
306 | +} | ||
307 | + | ||
308 | +int | ||
309 | +header_field_callback (http_parser *parser, const char *at, size_t length) | ||
310 | +{ | ||
311 | + struct parser_data_t *data = parser->data; | ||
312 | + | ||
313 | + switch (data->prev_callback) { | ||
314 | + case HPE_CB_header_value: | ||
315 | + data->current_header_length = 0; | ||
316 | + /* fall through */ | ||
317 | + case 0: | ||
318 | + data->prev_callback = HPE_CB_header_field; | ||
319 | + /* fall through */ | ||
320 | + case HPE_CB_header_field: | ||
321 | + return append (&data->current_header, &data->current_header_length, at, length); | ||
322 | + default: | ||
323 | + return 1; | ||
324 | + } | ||
325 | +} | ||
326 | + | ||
327 | +int | ||
328 | +header_value_callback (http_parser *parser, const char *at, size_t length) | ||
329 | +{ | ||
330 | + struct parser_data_t *data = parser->data; | ||
331 | + | ||
332 | + switch (data->prev_callback) { | ||
333 | + case HPE_CB_header_field: | ||
334 | + data->parsing_location = !strcasecmp (data->current_header, "location"); | ||
335 | + data->prev_callback = HPE_CB_header_value; | ||
336 | + /* fall through */ | ||
337 | + case HPE_CB_header_value: | ||
338 | + return data->parsing_location && | ||
339 | + append (&data->location, &data->location_length, at, length); | ||
340 | + default: | ||
341 | + return 1; | ||
342 | + } | ||
343 | +} | ||
344 | + | ||
345 | +int | ||
346 | +headers_complete_callback (http_parser *parser) | ||
347 | +{ | ||
348 | + struct parser_data_t *data = parser->data; | ||
349 | + | ||
350 | + data->prev_callback = HPE_CB_headers_complete; | ||
351 | + | ||
352 | + if (no_body) /* Terminate parsing (thus reading) if the -N option is set */ | ||
353 | + return 2; /* 1 means don't expect body in this callback */ | ||
354 | + return 0; | ||
355 | +} | ||
356 | + | ||
357 | int | ||
358 | check_http (void) | ||
359 | { | ||
360 | char *msg; | ||
361 | - char *status_line; | ||
362 | - char *status_code; | ||
363 | char *header; | ||
364 | char *page; | ||
365 | char *auth; | ||
366 | - int http_status; | ||
367 | int i = 0; | ||
368 | size_t pagesize = 0; | ||
369 | char *full_page; | ||
370 | @@ -792,6 +840,9 @@ check_http (void) | ||
371 | double elapsed_time; | ||
372 | int page_len = 0; | ||
373 | int result = STATE_OK; | ||
374 | + http_parser_settings settings; | ||
375 | + http_parser parser; | ||
376 | + struct parser_data_t parser_data; | ||
377 | |||
378 | /* try to connect to the host at the given port number */ | ||
379 | if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) | ||
380 | @@ -869,9 +920,29 @@ check_http (void) | ||
381 | if (verbose) printf ("%s\n", buf); | ||
382 | my_send (buf, strlen (buf)); | ||
383 | |||
384 | + /* Initialize the HTTP parser */ | ||
385 | + http_parser_init (&parser, HTTP_RESPONSE); | ||
386 | + memset (&parser_data, 0, sizeof parser_data); | ||
387 | + parser.data = &parser_data; | ||
388 | + memset (&settings, 0, sizeof settings); | ||
389 | + settings.on_header_field = header_field_callback; | ||
390 | + settings.on_header_value = header_value_callback; | ||
391 | + settings.on_headers_complete = headers_complete_callback; | ||
392 | + | ||
393 | /* fetch the page */ | ||
394 | full_page = strdup(""); | ||
395 | - while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) { | ||
396 | + while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) >= 0) { | ||
397 | + int nparsed = http_parser_execute(&parser, &settings, buffer, i); | ||
398 | + if (nparsed != i) { | ||
399 | + enum http_errno code = HTTP_PARSER_ERRNO (&parser); | ||
400 | + if (code == HPE_CB_headers_complete) { /* the -N check fired */ | ||
401 | + /* break; FIXME this would break the current header code */ | ||
402 | + } else { | ||
403 | + printf ("http_parser_execute returned %d instead of %i: %s\n", | ||
404 | + nparsed, i, http_errno_description (code)); | ||
405 | + } | ||
406 | + } | ||
407 | + if (i == 0) break; | ||
408 | buffer[i] = '\0'; | ||
409 | asprintf (&full_page_new, "%s%s", full_page, buffer); | ||
410 | free (full_page); | ||
411 | @@ -928,15 +999,9 @@ check_http (void) | ||
412 | use_ssl ? "https" : "http", server_address, | ||
413 | server_port, server_url, (int)pagesize); | ||
414 | |||
415 | - /* find status line and null-terminate it */ | ||
416 | - status_line = page; | ||
417 | + /* skip status line */ | ||
418 | page += (size_t) strcspn (page, "\r\n"); | ||
419 | - pos = page; | ||
420 | page += (size_t) strspn (page, "\r\n"); | ||
421 | - status_line[strcspn(status_line, "\r\n")] = 0; | ||
422 | - strip (status_line); | ||
423 | - if (verbose) | ||
424 | - printf ("STATUS: %s\n", status_line); | ||
425 | |||
426 | /* find header info and null-terminate it */ | ||
427 | header = page; | ||
428 | @@ -955,68 +1020,38 @@ check_http (void) | ||
429 | printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, | ||
430 | (no_body ? " [[ skipped ]]" : page)); | ||
431 | |||
432 | - /* make sure the status line matches the response we are looking for */ | ||
433 | - if (!expected_statuscode (status_line, server_expect)) { | ||
434 | - if (server_port == HTTP_PORT) | ||
435 | - asprintf (&msg, | ||
436 | - _("Invalid HTTP response received from host: %s\n"), | ||
437 | - status_line); | ||
438 | - else | ||
439 | - asprintf (&msg, | ||
440 | - _("Invalid HTTP response received from host on port %d: %s\n"), | ||
441 | - server_port, status_line); | ||
442 | - die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg); | ||
443 | - } | ||
444 | - | ||
445 | - /* Bypass normal status line check if server_expect was set by user and not default */ | ||
446 | - /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */ | ||
447 | - if ( server_expect_yn ) { | ||
448 | - asprintf (&msg, | ||
449 | - _("Status line output matched \"%s\" - "), server_expect); | ||
450 | - if (verbose) | ||
451 | - printf ("%s\n",msg); | ||
452 | - } | ||
453 | - else { | ||
454 | - /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ | ||
455 | - /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */ | ||
456 | - /* Status-Code = 3 DIGITS */ | ||
457 | - | ||
458 | - status_code = strchr (status_line, ' ') + sizeof (char); | ||
459 | - if (strspn (status_code, "1234567890") != 3) | ||
460 | - die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line); | ||
461 | - | ||
462 | - http_status = atoi (status_code); | ||
463 | - | ||
464 | - /* check the return code */ | ||
465 | - | ||
466 | - if (http_status >= 600 || http_status < 100) { | ||
467 | - die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line); | ||
468 | + if (status_expect != -1) { | ||
469 | + if (parser.status_code == status_expect) | ||
470 | + asprintf (&msg, _("Got expected code %d - "), status_expect); | ||
471 | + else { | ||
472 | + asprintf (&msg, _("Invalid HTTP status received from host: %d\n"), | ||
473 | + parser.status_code); | ||
474 | + die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg); | ||
475 | + } | ||
476 | + } else { | ||
477 | + if (parser.status_code >= 600 || parser.status_code < 100) { | ||
478 | + die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d)\n"), parser.status_code); | ||
479 | } | ||
480 | /* server errors result in a critical state */ | ||
481 | - else if (http_status >= 500) { | ||
482 | - asprintf (&msg, _("%s - "), status_line); | ||
483 | + else if (parser.status_code >= 500) { | ||
484 | result = STATE_CRITICAL; | ||
485 | } | ||
486 | /* client errors result in a warning state */ | ||
487 | - else if (http_status >= 400) { | ||
488 | - asprintf (&msg, _("%s - "), status_line); | ||
489 | + else if (parser.status_code >= 400) { | ||
490 | result = max_state_alt(STATE_WARNING, result); | ||
491 | } | ||
492 | /* check redirected page if specified */ | ||
493 | - else if (http_status >= 300) { | ||
494 | - | ||
495 | - if (onredirect == STATE_DEPENDENT) | ||
496 | - redir (header, status_line); | ||
497 | + else if (parser.status_code >= 300) { | ||
498 | + if (onredirect == STATE_DEPENDENT) { | ||
499 | + if (parser_data.location) | ||
500 | + redir (parser_data.location); | ||
501 | + else die (STATE_CRITICAL, _("Redirect without Location header\n")); | ||
502 | + } | ||
503 | else | ||
504 | result = max_state_alt(onredirect, result); | ||
505 | - asprintf (&msg, _("%s - "), status_line); | ||
506 | - } /* end if (http_status >= 300) */ | ||
507 | - else { | ||
508 | - /* Print OK status anyway */ | ||
509 | - asprintf (&msg, _("%s - "), status_line); | ||
510 | } | ||
511 | - | ||
512 | - } /* end else (server_expect_yn) */ | ||
513 | + asprintf (&msg, _("%d - "), parser.status_code); | ||
514 | + } | ||
515 | |||
516 | /* reset the alarm - must be called *after* redir or we'll never die on redirects! */ | ||
517 | alarm (0); | ||
518 | @@ -1111,11 +1146,10 @@ check_http (void) | ||
519 | #define HD5 URI_PATH | ||
520 | |||
521 | void | ||
522 | -redir (char *pos, char *status_line) | ||
523 | +redir (char *pos) | ||
524 | { | ||
525 | int i = 0; | ||
526 | char *x; | ||
527 | - char xx[2]; | ||
528 | char type[6]; | ||
529 | char *addr; | ||
530 | char *url; | ||
531 | @@ -1124,41 +1158,11 @@ redir (char *pos, char *status_line) | ||
532 | if (addr == NULL) | ||
533 | die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n")); | ||
534 | |||
535 | - url = malloc (strcspn (pos, "\r\n")); | ||
536 | + url = malloc (strlen (pos) + 1); | ||
537 | if (url == NULL) | ||
538 | die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); | ||
539 | |||
540 | - while (pos) { | ||
541 | - sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i); | ||
542 | - if (i == 0) { | ||
543 | - pos += (size_t) strcspn (pos, "\r\n"); | ||
544 | - pos += (size_t) strspn (pos, "\r\n"); | ||
545 | - if (strlen(pos) == 0) | ||
546 | - die (STATE_UNKNOWN, | ||
547 | - _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"), | ||
548 | - status_line, (display_html ? "</A>" : "")); | ||
549 | - continue; | ||
550 | - } | ||
551 | - | ||
552 | - pos += i; | ||
553 | - pos += strspn (pos, " \t"); | ||
554 | - | ||
555 | - /* | ||
556 | - * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by | ||
557 | - * preceding each extra line with at least one SP or HT.'' | ||
558 | - */ | ||
559 | - for (; (i = strspn (pos, "\r\n")); pos += i) { | ||
560 | - pos += i; | ||
561 | - if (!(i = strspn (pos, " \t"))) { | ||
562 | - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"), | ||
563 | - display_html ? "</A>" : ""); | ||
564 | - } | ||
565 | - } | ||
566 | - | ||
567 | - url = realloc (url, strcspn (pos, "\r\n") + 1); | ||
568 | - if (url == NULL) | ||
569 | - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); | ||
570 | - | ||
571 | + { /* dummy block to reduce patch diff */ | ||
572 | /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ | ||
573 | if (sscanf (pos, HD1, type, addr, &i, url) == 4) { | ||
574 | url = prepend_slash (url); | ||
575 | @@ -1203,10 +1207,7 @@ redir (char *pos, char *status_line) | ||
576 | _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), | ||
577 | pos, (display_html ? "</A>" : "")); | ||
578 | } | ||
579 | - | ||
580 | - break; | ||
581 | - | ||
582 | - } /* end while (pos) */ | ||
583 | + } | ||
584 | |||
585 | if (++redir_depth > max_depth) | ||
586 | die (STATE_WARNING, | ||
587 | @@ -1332,11 +1333,8 @@ print_help (void) | ||
588 | printf (" %s\n", _("(when this option is used the URL is not checked.)\n")); | ||
589 | #endif | ||
590 | |||
591 | - printf (" %s\n", "-e, --expect=STRING"); | ||
592 | - printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); | ||
593 | - printf (" %s", _("the first (status) line of the server response (default: ")); | ||
594 | - printf ("%s)\n", HTTP_EXPECT); | ||
595 | - printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); | ||
596 | + printf (" %s\n", "-e, --expect=INTEGER"); | ||
597 | + printf (" %s\n", _("Expected HTTP status code, overriding the default logic")); | ||
598 | printf (" %s\n", "-s, --string=STRING"); | ||
599 | printf (" %s\n", _("String to expect in the content")); | ||
600 | printf (" %s\n", "-u, --url=PATH"); | ||
601 | -- | ||
602 | 1.7.2.5 | ||
603 | |||
604 | |||
605 | From a35b05d935f975478c90360ab56fb54d2d82e9c6 Mon Sep 17 00:00:00 2001 | ||
606 | From: Ferenc Wagner <wferi@niif.hu> | ||
607 | Date: Wed, 2 Nov 2011 09:52:19 +0100 | ||
608 | Subject: [PATCH 4/9] check_http: whitespace change to fix up indentation by removing dummy block | ||
609 | |||
610 | |||
611 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
612 | --- | ||
613 | plugins/check_http.c | 76 ++++++++++++++++++++++++------------------------- | ||
614 | 1 files changed, 37 insertions(+), 39 deletions(-) | ||
615 | |||
616 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
617 | index ada7a95..3966ab4 100644 | ||
618 | --- a/plugins/check_http.c | ||
619 | +++ b/plugins/check_http.c | ||
620 | @@ -1162,51 +1162,49 @@ redir (char *pos) | ||
621 | if (url == NULL) | ||
622 | die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); | ||
623 | |||
624 | - { /* dummy block to reduce patch diff */ | ||
625 | - /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ | ||
626 | - if (sscanf (pos, HD1, type, addr, &i, url) == 4) { | ||
627 | - url = prepend_slash (url); | ||
628 | - use_ssl = server_type_check (type); | ||
629 | - } | ||
630 | + /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ | ||
631 | + if (sscanf (pos, HD1, type, addr, &i, url) == 4) { | ||
632 | + url = prepend_slash (url); | ||
633 | + use_ssl = server_type_check (type); | ||
634 | + } | ||
635 | |||
636 | - /* URI_HTTP URI_HOST URI_PATH */ | ||
637 | - else if (sscanf (pos, HD2, type, addr, url) == 3 ) { | ||
638 | - url = prepend_slash (url); | ||
639 | - use_ssl = server_type_check (type); | ||
640 | - i = server_port_check (use_ssl); | ||
641 | - } | ||
642 | + /* URI_HTTP URI_HOST URI_PATH */ | ||
643 | + else if (sscanf (pos, HD2, type, addr, url) == 3 ) { | ||
644 | + url = prepend_slash (url); | ||
645 | + use_ssl = server_type_check (type); | ||
646 | + i = server_port_check (use_ssl); | ||
647 | + } | ||
648 | |||
649 | - /* URI_HTTP URI_HOST URI_PORT */ | ||
650 | - else if (sscanf (pos, HD3, type, addr, &i) == 3) { | ||
651 | - strcpy (url, HTTP_URL); | ||
652 | - use_ssl = server_type_check (type); | ||
653 | - } | ||
654 | + /* URI_HTTP URI_HOST URI_PORT */ | ||
655 | + else if (sscanf (pos, HD3, type, addr, &i) == 3) { | ||
656 | + strcpy (url, HTTP_URL); | ||
657 | + use_ssl = server_type_check (type); | ||
658 | + } | ||
659 | |||
660 | - /* URI_HTTP URI_HOST */ | ||
661 | - else if (sscanf (pos, HD4, type, addr) == 2) { | ||
662 | - strcpy (url, HTTP_URL); | ||
663 | - use_ssl = server_type_check (type); | ||
664 | - i = server_port_check (use_ssl); | ||
665 | - } | ||
666 | + /* URI_HTTP URI_HOST */ | ||
667 | + else if (sscanf (pos, HD4, type, addr) == 2) { | ||
668 | + strcpy (url, HTTP_URL); | ||
669 | + use_ssl = server_type_check (type); | ||
670 | + i = server_port_check (use_ssl); | ||
671 | + } | ||
672 | |||
673 | - /* URI_PATH */ | ||
674 | - else if (sscanf (pos, HD5, url) == 1) { | ||
675 | - /* relative url */ | ||
676 | - if ((url[0] != '/')) { | ||
677 | - if ((x = strrchr(server_url, '/'))) | ||
678 | - *x = '\0'; | ||
679 | - asprintf (&url, "%s/%s", server_url, url); | ||
680 | - } | ||
681 | - i = server_port; | ||
682 | - strcpy (type, server_type); | ||
683 | - strcpy (addr, host_name ? host_name : server_address); | ||
684 | + /* URI_PATH */ | ||
685 | + else if (sscanf (pos, HD5, url) == 1) { | ||
686 | + /* relative url */ | ||
687 | + if ((url[0] != '/')) { | ||
688 | + if ((x = strrchr(server_url, '/'))) | ||
689 | + *x = '\0'; | ||
690 | + asprintf (&url, "%s/%s", server_url, url); | ||
691 | } | ||
692 | + i = server_port; | ||
693 | + strcpy (type, server_type); | ||
694 | + strcpy (addr, host_name ? host_name : server_address); | ||
695 | + } | ||
696 | |||
697 | - else { | ||
698 | - die (STATE_UNKNOWN, | ||
699 | - _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), | ||
700 | - pos, (display_html ? "</A>" : "")); | ||
701 | - } | ||
702 | + else { | ||
703 | + die (STATE_UNKNOWN, | ||
704 | + _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), | ||
705 | + pos, (display_html ? "</A>" : "")); | ||
706 | } | ||
707 | |||
708 | if (++redir_depth > max_depth) | ||
709 | -- | ||
710 | 1.7.2.5 | ||
711 | |||
712 | |||
713 | From 3f410b149eb7cd112466bbb147525c1d497adaa2 Mon Sep 17 00:00:00 2001 | ||
714 | From: Ferenc Wagner <wferi@niif.hu> | ||
715 | Date: Mon, 31 Oct 2011 17:45:49 +0100 | ||
716 | Subject: [PATCH 5/9] check_http: constify and rename the location argument for redir() | ||
717 | |||
718 | |||
719 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
720 | --- | ||
721 | plugins/check_http.c | 18 +++++++++--------- | ||
722 | 1 files changed, 9 insertions(+), 9 deletions(-) | ||
723 | |||
724 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
725 | index 3966ab4..c4586d5 100644 | ||
726 | --- a/plugins/check_http.c | ||
727 | +++ b/plugins/check_http.c | ||
728 | @@ -134,7 +134,7 @@ char buffer[MAX_INPUT_BUFFER]; | ||
729 | |||
730 | int process_arguments (int, char **); | ||
731 | int check_http (void); | ||
732 | -void redir (char *pos); | ||
733 | +void redir (const char *location); | ||
734 | int server_type_check(const char *type); | ||
735 | int server_port_check(int ssl_flag); | ||
736 | char *perfd_time (double microsec); | ||
737 | @@ -1146,7 +1146,7 @@ check_http (void) | ||
738 | #define HD5 URI_PATH | ||
739 | |||
740 | void | ||
741 | -redir (char *pos) | ||
742 | +redir (const char *location) | ||
743 | { | ||
744 | int i = 0; | ||
745 | char *x; | ||
746 | @@ -1158,38 +1158,38 @@ redir (char *pos) | ||
747 | if (addr == NULL) | ||
748 | die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n")); | ||
749 | |||
750 | - url = malloc (strlen (pos) + 1); | ||
751 | + url = malloc (strlen (location) + 1); | ||
752 | if (url == NULL) | ||
753 | die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); | ||
754 | |||
755 | /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ | ||
756 | - if (sscanf (pos, HD1, type, addr, &i, url) == 4) { | ||
757 | + if (sscanf (location, HD1, type, addr, &i, url) == 4) { | ||
758 | url = prepend_slash (url); | ||
759 | use_ssl = server_type_check (type); | ||
760 | } | ||
761 | |||
762 | /* URI_HTTP URI_HOST URI_PATH */ | ||
763 | - else if (sscanf (pos, HD2, type, addr, url) == 3 ) { | ||
764 | + else if (sscanf (location, HD2, type, addr, url) == 3 ) { | ||
765 | url = prepend_slash (url); | ||
766 | use_ssl = server_type_check (type); | ||
767 | i = server_port_check (use_ssl); | ||
768 | } | ||
769 | |||
770 | /* URI_HTTP URI_HOST URI_PORT */ | ||
771 | - else if (sscanf (pos, HD3, type, addr, &i) == 3) { | ||
772 | + else if (sscanf (location, HD3, type, addr, &i) == 3) { | ||
773 | strcpy (url, HTTP_URL); | ||
774 | use_ssl = server_type_check (type); | ||
775 | } | ||
776 | |||
777 | /* URI_HTTP URI_HOST */ | ||
778 | - else if (sscanf (pos, HD4, type, addr) == 2) { | ||
779 | + else if (sscanf (location, HD4, type, addr) == 2) { | ||
780 | strcpy (url, HTTP_URL); | ||
781 | use_ssl = server_type_check (type); | ||
782 | i = server_port_check (use_ssl); | ||
783 | } | ||
784 | |||
785 | /* URI_PATH */ | ||
786 | - else if (sscanf (pos, HD5, url) == 1) { | ||
787 | + else if (sscanf (location, HD5, url) == 1) { | ||
788 | /* relative url */ | ||
789 | if ((url[0] != '/')) { | ||
790 | if ((x = strrchr(server_url, '/'))) | ||
791 | @@ -1204,7 +1204,7 @@ redir (char *pos) | ||
792 | else { | ||
793 | die (STATE_UNKNOWN, | ||
794 | _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), | ||
795 | - pos, (display_html ? "</A>" : "")); | ||
796 | + location, (display_html ? "</A>" : "")); | ||
797 | } | ||
798 | |||
799 | if (++redir_depth > max_depth) | ||
800 | -- | ||
801 | 1.7.2.5 | ||
802 | |||
803 | |||
804 | From d1b806941bf6bf94754e86e832a11eabc003a656 Mon Sep 17 00:00:00 2001 | ||
805 | From: Ferenc Wagner <wferi@niif.hu> | ||
806 | Date: Mon, 31 Oct 2011 17:47:47 +0100 | ||
807 | Subject: [PATCH 6/9] check_http: make list of parsed headers configurable | ||
808 | |||
809 | |||
810 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
811 | --- | ||
812 | plugins/check_http.c | 58 +++++++++++++++++++++++++++++++++++++++---------- | ||
813 | 1 files changed, 46 insertions(+), 12 deletions(-) | ||
814 | |||
815 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
816 | index c4586d5..5466e06 100644 | ||
817 | --- a/plugins/check_http.c | ||
818 | +++ b/plugins/check_http.c | ||
819 | @@ -49,13 +49,17 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; | ||
820 | #define STICKY_HOST 1 | ||
821 | #define STICKY_PORT 2 | ||
822 | |||
823 | +struct header_data_t { | ||
824 | + const char *field; | ||
825 | + char *value; | ||
826 | +}; | ||
827 | + | ||
828 | struct parser_data_t { | ||
829 | enum http_errno prev_callback; /* use HPE_CB_ macros for identifying callbacks */ | ||
830 | - char *current_header; | ||
831 | - size_t current_header_length; | ||
832 | - int parsing_location; | ||
833 | - char *location; | ||
834 | - size_t location_length; | ||
835 | + char *current_field; | ||
836 | + size_t current_length; | ||
837 | + struct header_data_t *current_header; | ||
838 | + struct header_data_t *headers; | ||
839 | }; | ||
840 | |||
841 | enum { | ||
842 | @@ -758,6 +762,17 @@ prepend_slash (char *path) | ||
843 | return newpath; | ||
844 | } | ||
845 | |||
846 | +const char * | ||
847 | +get_value (const struct header_data_t *header, const char *field) | ||
848 | +{ | ||
849 | + while (header->field) { | ||
850 | + if (!strcasecmp (header->field, field)) | ||
851 | + return header->value; | ||
852 | + header++; | ||
853 | + } | ||
854 | + return 0; | ||
855 | +} | ||
856 | + | ||
857 | /* | ||
858 | Returns 0 on success, 1 if allocation fails. | ||
859 | Works on binary data, but also zero-terminates the result | ||
860 | @@ -781,13 +796,13 @@ header_field_callback (http_parser *parser, const char *at, size_t length) | ||
861 | |||
862 | switch (data->prev_callback) { | ||
863 | case HPE_CB_header_value: | ||
864 | - data->current_header_length = 0; | ||
865 | + data->current_length = 0; | ||
866 | /* fall through */ | ||
867 | case 0: | ||
868 | data->prev_callback = HPE_CB_header_field; | ||
869 | /* fall through */ | ||
870 | case HPE_CB_header_field: | ||
871 | - return append (&data->current_header, &data->current_header_length, at, length); | ||
872 | + return append (&data->current_field, &data->current_length, at, length); | ||
873 | default: | ||
874 | return 1; | ||
875 | } | ||
876 | @@ -797,15 +812,23 @@ int | ||
877 | header_value_callback (http_parser *parser, const char *at, size_t length) | ||
878 | { | ||
879 | struct parser_data_t *data = parser->data; | ||
880 | + struct header_data_t *header; | ||
881 | |||
882 | switch (data->prev_callback) { | ||
883 | case HPE_CB_header_field: | ||
884 | - data->parsing_location = !strcasecmp (data->current_header, "location"); | ||
885 | + data->current_header = 0; | ||
886 | + data->current_length = 0; | ||
887 | + for (header = data->headers; header->field; header++) | ||
888 | + if (!strcasecmp (data->current_field, header->field)) { | ||
889 | + data->current_header = header; | ||
890 | + break; | ||
891 | + } | ||
892 | data->prev_callback = HPE_CB_header_value; | ||
893 | /* fall through */ | ||
894 | case HPE_CB_header_value: | ||
895 | - return data->parsing_location && | ||
896 | - append (&data->location, &data->location_length, at, length); | ||
897 | + return data->current_header && | ||
898 | + append (&(data->current_header->value), | ||
899 | + &data->current_length, at, length); | ||
900 | default: | ||
901 | return 1; | ||
902 | } | ||
903 | @@ -816,6 +839,13 @@ headers_complete_callback (http_parser *parser) | ||
904 | { | ||
905 | struct parser_data_t *data = parser->data; | ||
906 | |||
907 | + if (verbose) { | ||
908 | + const struct header_data_t *header; | ||
909 | + printf ("Parsed headers:\n"); | ||
910 | + for (header = data->headers; header->field; header++) | ||
911 | + printf ("%s: %s\n", header->field, header->value); | ||
912 | + } | ||
913 | + | ||
914 | data->prev_callback = HPE_CB_headers_complete; | ||
915 | |||
916 | if (no_body) /* Terminate parsing (thus reading) if the -N option is set */ | ||
917 | @@ -843,6 +873,8 @@ check_http (void) | ||
918 | http_parser_settings settings; | ||
919 | http_parser parser; | ||
920 | struct parser_data_t parser_data; | ||
921 | + struct header_data_t interesting_headers[] = | ||
922 | + {{"location", 0}, {"date", 0}, {"last-modified", 0}, {0, 0}}; | ||
923 | |||
924 | /* try to connect to the host at the given port number */ | ||
925 | if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) | ||
926 | @@ -923,6 +955,7 @@ check_http (void) | ||
927 | /* Initialize the HTTP parser */ | ||
928 | http_parser_init (&parser, HTTP_RESPONSE); | ||
929 | memset (&parser_data, 0, sizeof parser_data); | ||
930 | + parser_data.headers = interesting_headers; | ||
931 | parser.data = &parser_data; | ||
932 | memset (&settings, 0, sizeof settings); | ||
933 | settings.on_header_field = header_field_callback; | ||
934 | @@ -1043,8 +1076,9 @@ check_http (void) | ||
935 | /* check redirected page if specified */ | ||
936 | else if (parser.status_code >= 300) { | ||
937 | if (onredirect == STATE_DEPENDENT) { | ||
938 | - if (parser_data.location) | ||
939 | - redir (parser_data.location); | ||
940 | + const char *location = get_value (parser_data.headers, "location"); | ||
941 | + if (location) | ||
942 | + redir (location); | ||
943 | else die (STATE_CRITICAL, _("Redirect without Location header\n")); | ||
944 | } | ||
945 | else | ||
946 | -- | ||
947 | 1.7.2.5 | ||
948 | |||
949 | |||
950 | From 62e8868bb24c2161ccb7748dba82c6a90618aac3 Mon Sep 17 00:00:00 2001 | ||
951 | From: Ferenc Wagner <wferi@niif.hu> | ||
952 | Date: Mon, 31 Oct 2011 17:57:11 +0100 | ||
953 | Subject: [PATCH 7/9] check_http: use header parsing machinery in check_document_dates() | ||
954 | |||
955 | |||
956 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
957 | --- | ||
958 | plugins/check_http.c | 61 +++---------------------------------------------- | ||
959 | 1 files changed, 4 insertions(+), 57 deletions(-) | ||
960 | |||
961 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
962 | index 5466e06..423bb22 100644 | ||
963 | --- a/plugins/check_http.c | ||
964 | +++ b/plugins/check_http.c | ||
965 | @@ -600,63 +600,10 @@ parse_time_string (const char *string) | ||
966 | } | ||
967 | |||
968 | static int | ||
969 | -check_document_dates (const char *headers, char **msg) | ||
970 | +check_document_dates (const char *server_date, const char *document_date, char **msg) | ||
971 | { | ||
972 | - const char *s; | ||
973 | - char *server_date = 0; | ||
974 | - char *document_date = 0; | ||
975 | int date_result = STATE_OK; | ||
976 | |||
977 | - s = headers; | ||
978 | - while (*s) { | ||
979 | - const char *field = s; | ||
980 | - const char *value = 0; | ||
981 | - | ||
982 | - /* Find the end of the header field */ | ||
983 | - while (*s && !isspace(*s) && *s != ':') | ||
984 | - s++; | ||
985 | - | ||
986 | - /* Remember the header value, if any. */ | ||
987 | - if (*s == ':') | ||
988 | - value = ++s; | ||
989 | - | ||
990 | - /* Skip to the end of the header, including continuation lines. */ | ||
991 | - while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) | ||
992 | - s++; | ||
993 | - | ||
994 | - /* Avoid stepping over end-of-string marker */ | ||
995 | - if (*s) | ||
996 | - s++; | ||
997 | - | ||
998 | - /* Process this header. */ | ||
999 | - if (value && value > field+2) { | ||
1000 | - char *ff = (char *) malloc (value-field); | ||
1001 | - char *ss = ff; | ||
1002 | - while (field < value-1) | ||
1003 | - *ss++ = tolower(*field++); | ||
1004 | - *ss++ = 0; | ||
1005 | - | ||
1006 | - if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) { | ||
1007 | - const char *e; | ||
1008 | - while (*value && isspace (*value)) | ||
1009 | - value++; | ||
1010 | - for (e = value; *e && *e != '\r' && *e != '\n'; e++) | ||
1011 | - ; | ||
1012 | - ss = (char *) malloc (e - value + 1); | ||
1013 | - strncpy (ss, value, e - value); | ||
1014 | - ss[e - value] = 0; | ||
1015 | - if (!strcmp (ff, "date")) { | ||
1016 | - if (server_date) free (server_date); | ||
1017 | - server_date = ss; | ||
1018 | - } else { | ||
1019 | - if (document_date) free (document_date); | ||
1020 | - document_date = ss; | ||
1021 | - } | ||
1022 | - } | ||
1023 | - free (ff); | ||
1024 | - } | ||
1025 | - } | ||
1026 | - | ||
1027 | /* Done parsing the body. Now check the dates we (hopefully) parsed. */ | ||
1028 | if (!server_date || !*server_date) { | ||
1029 | asprintf (msg, _("%sServer date unknown, "), *msg); | ||
1030 | @@ -687,8 +634,6 @@ check_document_dates (const char *headers, char **msg) | ||
1031 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
1032 | } | ||
1033 | } | ||
1034 | - free (server_date); | ||
1035 | - free (document_date); | ||
1036 | } | ||
1037 | return date_result; | ||
1038 | } | ||
1039 | @@ -1091,7 +1036,9 @@ check_http (void) | ||
1040 | alarm (0); | ||
1041 | |||
1042 | if (maximum_age >= 0) { | ||
1043 | - result = max_state_alt(check_document_dates(header, &msg), result); | ||
1044 | + result = max_state_alt(check_document_dates(get_value (parser_data.headers, "date"), | ||
1045 | + get_value (parser_data.headers, "last-modified"), &msg), | ||
1046 | + result); | ||
1047 | } | ||
1048 | |||
1049 | /* Page and Header content checks go here */ | ||
1050 | -- | ||
1051 | 1.7.2.5 | ||
1052 | |||
1053 | |||
1054 | From efe4e949ae4ba64f333f402d0f4b54f07b18164c Mon Sep 17 00:00:00 2001 | ||
1055 | From: Ferenc Wagner <wferi@niif.hu> | ||
1056 | Date: Tue, 1 Nov 2011 16:17:00 +0100 | ||
1057 | Subject: [PATCH 8/9] check_http: also read the response content via http-parse | ||
1058 | |||
1059 | This gets rid of good a bunch of code, and also gives free decoding of | ||
1060 | Transfer-Encoding: chunked, which all HTTP/1.1 clients must support. | ||
1061 | |||
1062 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
1063 | --- | ||
1064 | plugins/check_http.c | 86 ++++++++++++------------------------------------- | ||
1065 | 1 files changed, 21 insertions(+), 65 deletions(-) | ||
1066 | |||
1067 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
1068 | index 423bb22..deea38c 100644 | ||
1069 | --- a/plugins/check_http.c | ||
1070 | +++ b/plugins/check_http.c | ||
1071 | @@ -60,6 +60,7 @@ struct parser_data_t { | ||
1072 | size_t current_length; | ||
1073 | struct header_data_t *current_header; | ||
1074 | struct header_data_t *headers; | ||
1075 | + char *content; | ||
1076 | }; | ||
1077 | |||
1078 | enum { | ||
1079 | @@ -488,26 +489,6 @@ process_arguments (int argc, char **argv) | ||
1080 | return TRUE; | ||
1081 | } | ||
1082 | |||
1083 | - | ||
1084 | - | ||
1085 | -/* Returns 1 if we're done processing the document body; 0 to keep going */ | ||
1086 | -static int | ||
1087 | -document_headers_done (char *full_page) | ||
1088 | -{ | ||
1089 | - const char *body; | ||
1090 | - | ||
1091 | - for (body = full_page; *body; body++) { | ||
1092 | - if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3)) | ||
1093 | - break; | ||
1094 | - } | ||
1095 | - | ||
1096 | - if (!*body) | ||
1097 | - return 0; /* haven't read end of headers yet */ | ||
1098 | - | ||
1099 | - full_page[body - full_page] = 0; | ||
1100 | - return 1; | ||
1101 | -} | ||
1102 | - | ||
1103 | static time_t | ||
1104 | parse_time_string (const char *string) | ||
1105 | { | ||
1106 | @@ -792,6 +773,7 @@ headers_complete_callback (http_parser *parser) | ||
1107 | } | ||
1108 | |||
1109 | data->prev_callback = HPE_CB_headers_complete; | ||
1110 | + data->current_length = 0; /* prepare for the body */ | ||
1111 | |||
1112 | if (no_body) /* Terminate parsing (thus reading) if the -N option is set */ | ||
1113 | return 2; /* 1 means don't expect body in this callback */ | ||
1114 | @@ -799,16 +781,20 @@ headers_complete_callback (http_parser *parser) | ||
1115 | } | ||
1116 | |||
1117 | int | ||
1118 | +body_callback (http_parser *parser, const char *at, size_t length) | ||
1119 | +{ | ||
1120 | + struct parser_data_t *data = parser->data; | ||
1121 | + | ||
1122 | + return append (&data->content, &data->current_length, at, length); | ||
1123 | +} | ||
1124 | + | ||
1125 | +int | ||
1126 | check_http (void) | ||
1127 | { | ||
1128 | char *msg; | ||
1129 | - char *header; | ||
1130 | - char *page; | ||
1131 | char *auth; | ||
1132 | int i = 0; | ||
1133 | size_t pagesize = 0; | ||
1134 | - char *full_page; | ||
1135 | - char *full_page_new; | ||
1136 | char *buf; | ||
1137 | char *pos; | ||
1138 | long microsec; | ||
1139 | @@ -906,31 +892,19 @@ check_http (void) | ||
1140 | settings.on_header_field = header_field_callback; | ||
1141 | settings.on_header_value = header_value_callback; | ||
1142 | settings.on_headers_complete = headers_complete_callback; | ||
1143 | + settings.on_body = body_callback; | ||
1144 | |||
1145 | /* fetch the page */ | ||
1146 | - full_page = strdup(""); | ||
1147 | while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) >= 0) { | ||
1148 | int nparsed = http_parser_execute(&parser, &settings, buffer, i); | ||
1149 | + pagesize += i; | ||
1150 | if (nparsed != i) { | ||
1151 | enum http_errno code = HTTP_PARSER_ERRNO (&parser); | ||
1152 | - if (code == HPE_CB_headers_complete) { /* the -N check fired */ | ||
1153 | - /* break; FIXME this would break the current header code */ | ||
1154 | - } else { | ||
1155 | - printf ("http_parser_execute returned %d instead of %i: %s\n", | ||
1156 | - nparsed, i, http_errno_description (code)); | ||
1157 | - } | ||
1158 | + if (code == HPE_CB_headers_complete) break; /* the -N check fired */ | ||
1159 | + else die (STATE_CRITICAL, _("HTTP CRITICAL - error parsing response: %s"), | ||
1160 | + http_errno_description (code)); | ||
1161 | } | ||
1162 | if (i == 0) break; | ||
1163 | - buffer[i] = '\0'; | ||
1164 | - asprintf (&full_page_new, "%s%s", full_page, buffer); | ||
1165 | - free (full_page); | ||
1166 | - full_page = full_page_new; | ||
1167 | - pagesize += i; | ||
1168 | - | ||
1169 | - if (no_body && document_headers_done (full_page)) { | ||
1170 | - i = 0; | ||
1171 | - break; | ||
1172 | - } | ||
1173 | } | ||
1174 | |||
1175 | if (i < 0 && errno != ECONNRESET) { | ||
1176 | @@ -969,34 +943,16 @@ check_http (void) | ||
1177 | microsec = deltime (tv); | ||
1178 | elapsed_time = (double)microsec / 1.0e6; | ||
1179 | |||
1180 | - /* leave full_page untouched so we can free it later */ | ||
1181 | - page = full_page; | ||
1182 | - | ||
1183 | if (verbose && !no_body) | ||
1184 | printf ("%s://%s:%d%s is %d characters\n", | ||
1185 | use_ssl ? "https" : "http", server_address, | ||
1186 | server_port, server_url, (int)pagesize); | ||
1187 | |||
1188 | - /* skip status line */ | ||
1189 | - page += (size_t) strcspn (page, "\r\n"); | ||
1190 | - page += (size_t) strspn (page, "\r\n"); | ||
1191 | - | ||
1192 | - /* find header info and null-terminate it */ | ||
1193 | - header = page; | ||
1194 | - while (strcspn (page, "\r\n") > 0) { | ||
1195 | - page += (size_t) strcspn (page, "\r\n"); | ||
1196 | - pos = page; | ||
1197 | - if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) || | ||
1198 | - (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2)) | ||
1199 | - page += (size_t) 2; | ||
1200 | - else | ||
1201 | - page += (size_t) 1; | ||
1202 | + if (verbose && !no_body) { | ||
1203 | + puts ("**** CONTENT ****"); | ||
1204 | + fwrite (parser_data.content, parser_data.current_length, 1, stdout); | ||
1205 | + putchar ('\n'); | ||
1206 | } | ||
1207 | - page += (size_t) strspn (page, "\r\n"); | ||
1208 | - header[pos - header] = 0; | ||
1209 | - if (verbose) | ||
1210 | - printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, | ||
1211 | - (no_body ? " [[ skipped ]]" : page)); | ||
1212 | |||
1213 | if (status_expect != -1) { | ||
1214 | if (parser.status_code == status_expect) | ||
1215 | @@ -1044,7 +1000,7 @@ check_http (void) | ||
1216 | /* Page and Header content checks go here */ | ||
1217 | |||
1218 | if (strlen (string_expect)) { | ||
1219 | - if (!strstr (page, string_expect)) { | ||
1220 | + if (!strstr (parser_data.content, string_expect)) { | ||
1221 | strncpy(&output_string_search[0],string_expect,sizeof(output_string_search)); | ||
1222 | if(output_string_search[sizeof(output_string_search)-1]!='\0') { | ||
1223 | bcopy("...",&output_string_search[sizeof(output_string_search)-4],4); | ||
1224 | @@ -1055,7 +1011,7 @@ check_http (void) | ||
1225 | } | ||
1226 | |||
1227 | if (strlen (regexp)) { | ||
1228 | - errcode = regexec (&preg, page, REGS, pmatch, 0); | ||
1229 | + errcode = regexec (&preg, parser_data.content, REGS, pmatch, 0); | ||
1230 | if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) { | ||
1231 | /* OK - No-op to avoid changing the logic around it */ | ||
1232 | result = max_state_alt(STATE_OK, result); | ||
1233 | -- | ||
1234 | 1.7.2.5 | ||
1235 | |||
1236 | |||
1237 | From 256d8d15acf98ee405f79b75a52692531d173f49 Mon Sep 17 00:00:00 2001 | ||
1238 | From: Ferenc Wagner <wferi@niif.hu> | ||
1239 | Date: Wed, 2 Nov 2011 07:57:30 +0100 | ||
1240 | Subject: [PATCH 9/9] check_http: check content length instead of response length | ||
1241 | |||
1242 | The content length does not depend on the transfer encoding and may | ||
1243 | be possible to derive without fetching the entire body (cf. -N option). | ||
1244 | |||
1245 | Signed-off-by: Ferenc Wagner <wferi@niif.hu> | ||
1246 | --- | ||
1247 | plugins/check_http.c | 93 +++++++++++--------------------------------------- | ||
1248 | 1 files changed, 20 insertions(+), 73 deletions(-) | ||
1249 | |||
1250 | diff --git a/plugins/check_http.c b/plugins/check_http.c | ||
1251 | index deea38c..27f5cd3 100644 | ||
1252 | --- a/plugins/check_http.c | ||
1253 | +++ b/plugins/check_http.c | ||
1254 | @@ -619,59 +619,6 @@ check_document_dates (const char *server_date, const char *document_date, char * | ||
1255 | return date_result; | ||
1256 | } | ||
1257 | |||
1258 | -int | ||
1259 | -get_content_length (const char *headers) | ||
1260 | -{ | ||
1261 | - const char *s; | ||
1262 | - int content_length = 0; | ||
1263 | - | ||
1264 | - s = headers; | ||
1265 | - while (*s) { | ||
1266 | - const char *field = s; | ||
1267 | - const char *value = 0; | ||
1268 | - | ||
1269 | - /* Find the end of the header field */ | ||
1270 | - while (*s && !isspace(*s) && *s != ':') | ||
1271 | - s++; | ||
1272 | - | ||
1273 | - /* Remember the header value, if any. */ | ||
1274 | - if (*s == ':') | ||
1275 | - value = ++s; | ||
1276 | - | ||
1277 | - /* Skip to the end of the header, including continuation lines. */ | ||
1278 | - while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) | ||
1279 | - s++; | ||
1280 | - | ||
1281 | - /* Avoid stepping over end-of-string marker */ | ||
1282 | - if (*s) | ||
1283 | - s++; | ||
1284 | - | ||
1285 | - /* Process this header. */ | ||
1286 | - if (value && value > field+2) { | ||
1287 | - char *ff = (char *) malloc (value-field); | ||
1288 | - char *ss = ff; | ||
1289 | - while (field < value-1) | ||
1290 | - *ss++ = tolower(*field++); | ||
1291 | - *ss++ = 0; | ||
1292 | - | ||
1293 | - if (!strcmp (ff, "content-length")) { | ||
1294 | - const char *e; | ||
1295 | - while (*value && isspace (*value)) | ||
1296 | - value++; | ||
1297 | - for (e = value; *e && *e != '\r' && *e != '\n'; e++) | ||
1298 | - ; | ||
1299 | - ss = (char *) malloc (e - value + 1); | ||
1300 | - strncpy (ss, value, e - value); | ||
1301 | - ss[e - value] = 0; | ||
1302 | - content_length = atoi(ss); | ||
1303 | - free (ss); | ||
1304 | - } | ||
1305 | - free (ff); | ||
1306 | - } | ||
1307 | - } | ||
1308 | - return (content_length); | ||
1309 | -} | ||
1310 | - | ||
1311 | char * | ||
1312 | prepend_slash (char *path) | ||
1313 | { | ||
1314 | @@ -799,13 +746,13 @@ check_http (void) | ||
1315 | char *pos; | ||
1316 | long microsec; | ||
1317 | double elapsed_time; | ||
1318 | - int page_len = 0; | ||
1319 | + int page_len = -1; | ||
1320 | int result = STATE_OK; | ||
1321 | http_parser_settings settings; | ||
1322 | http_parser parser; | ||
1323 | struct parser_data_t parser_data; | ||
1324 | struct header_data_t interesting_headers[] = | ||
1325 | - {{"location", 0}, {"date", 0}, {"last-modified", 0}, {0, 0}}; | ||
1326 | + {{"location", 0}, {"date", 0}, {"last-modified", 0}, {"content-length", 0}, {0, 0}}; | ||
1327 | |||
1328 | /* try to connect to the host at the given port number */ | ||
1329 | if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) | ||
1330 | @@ -1031,21 +978,21 @@ check_http (void) | ||
1331 | } | ||
1332 | } | ||
1333 | |||
1334 | - /* make sure the page is of an appropriate size */ | ||
1335 | - /* page_len = get_content_length(header); */ | ||
1336 | - /* FIXME: Will this work with -N ? IMHO we should use | ||
1337 | - * get_content_length(header) and always check if it's different than the | ||
1338 | - * returned pagesize | ||
1339 | - */ | ||
1340 | - /* FIXME: IIRC pagesize returns headers - shouldn't we make | ||
1341 | - * it == get_content_length(header) ?? | ||
1342 | - */ | ||
1343 | - page_len = pagesize; | ||
1344 | - if ((max_page_len > 0) && (page_len > max_page_len)) { | ||
1345 | - asprintf (&msg, _("%spage size %d too large, "), msg, page_len); | ||
1346 | - result = max_state_alt(STATE_WARNING, result); | ||
1347 | - } else if ((min_page_len > 0) && (page_len < min_page_len)) { | ||
1348 | - asprintf (&msg, _("%spage size %d too small, "), msg, page_len); | ||
1349 | + /* make sure the content is of an appropriate size */ | ||
1350 | + if (no_body) { | ||
1351 | + const char *content_length = get_value (parser_data.headers, "content-length"); | ||
1352 | + if (content_length) page_len = atol (content_length); | ||
1353 | + } else page_len = parser_data.current_length; | ||
1354 | + if (page_len != -1) { | ||
1355 | + if ((max_page_len > 0) && (page_len > max_page_len)) { | ||
1356 | + asprintf (&msg, _("%spage size %d too large, "), msg, page_len); | ||
1357 | + result = max_state_alt(STATE_WARNING, result); | ||
1358 | + } else if ((min_page_len > 0) && (page_len < min_page_len)) { | ||
1359 | + asprintf (&msg, _("%spage size %d too small, "), msg, page_len); | ||
1360 | + result = max_state_alt(STATE_WARNING, result); | ||
1361 | + } | ||
1362 | + } else if (max_page_len > 0 || min_page_len > 0) { | ||
1363 | + asprintf (&msg, _("%spage size unknown, "), msg); | ||
1364 | result = max_state_alt(STATE_WARNING, result); | ||
1365 | } | ||
1366 | |||
1367 | @@ -1058,7 +1005,7 @@ check_http (void) | ||
1368 | /* check elapsed time */ | ||
1369 | asprintf (&msg, | ||
1370 | _("%s - %d bytes in %.3f second response time %s|%s %s"), | ||
1371 | - msg, page_len, elapsed_time, | ||
1372 | + msg, pagesize, elapsed_time, | ||
1373 | (display_html ? "</A>" : ""), | ||
1374 | perfd_time (elapsed_time), perfd_size (page_len)); | ||
1375 | |||
1376 | @@ -1310,7 +1257,7 @@ print_help (void) | ||
1377 | printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); | ||
1378 | printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); | ||
1379 | printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); | ||
1380 | - printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); | ||
1381 | + printf (" %s\n", _("Minimum content size required (bytes) : Maximum content size required (bytes)")); | ||
1382 | |||
1383 | printf (UT_WARN_CRIT); | ||
1384 | |||
1385 | @@ -1361,7 +1308,7 @@ print_usage (void) | ||
1386 | printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-a auth]\n"); | ||
1387 | printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n"); | ||
1388 | printf (" [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); | ||
1389 | - printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); | ||
1390 | + printf (" [-P string] [-m <min_cnt_size>:<max_cnt_size>] [-4|-6] [-N] [-M <age>]\n"); | ||
1391 | printf (" [-A string] [-k string] [-S] [--sni] [-C <age>] [-T <content-type>]\n"); | ||
1392 | printf (" [-j method]\n"); | ||
1393 | } | ||
1394 | -- | ||
1395 | 1.7.2.5 | ||
1396 | |||