diff --git a/plugins/check_http.c b/plugins/check_http.c index 433c28e..a71cfe6 100644 --- a/plugins/check_http.c +++ b/plugins/check_http.c @@ -41,7 +41,9 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; #include "netutils.h" #include "utils.h" #include "base64.h" +#include "zlib.h" #include +#include #define INPUT_DELIMITER ";" #define STICKY_NONE 0 @@ -114,6 +116,8 @@ int followsticky = STICKY_NONE; int use_ssl = FALSE; int use_sni = FALSE; int verbose = FALSE; +int decompress = FALSE; +int chunked = FALSE; int sd; int min_page_len = 0; int max_page_len = 0; @@ -134,6 +138,14 @@ char *perfd_size (int page_len); void print_help (void); void print_usage (void); +int page_content_decompress(const char *in_buf,int in_size, + char **data,int *out_size,int type); +int find_header_info(const char* header, + char* content,const char* keyword); +int get_content_encoding(const char* header); +int get_transfer_encoding(const char* header); +char* decodechunked(char * chunked, unsigned int *inputlen); + int main (int argc, char **argv) { @@ -214,6 +226,8 @@ process_arguments (int argc, char **argv) {"invert-regex", no_argument, NULL, INVERT_REGEX}, {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, + {"decompress",no_argument,0,'d'}, + {"chunked",no_argument,0,'X'}, {0, 0, 0, 0} }; @@ -234,7 +248,7 @@ process_arguments (int argc, char **argv) } while (1) { - c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts, &option); + c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:e:p:s:R:r:u:f:C:nlLSm:M:NdX", longopts, &option); if (c == -1 || c == EOF) break; @@ -406,6 +420,12 @@ process_arguments (int argc, char **argv) case 'v': /* verbose */ verbose = TRUE; break; + case 'd': /* decompress */ + decompress = TRUE; + break; + case 'X': /*chunked*/ + chunked = TRUE; + break; case 'm': /* min_page_length */ { char *tmp; @@ -793,6 +813,9 @@ check_http (void) int page_len = 0; int result = STATE_OK; + char *uncompress_page; + int uncompress_page_size; + /* try to connect to the host at the given port number */ if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n")); @@ -870,18 +893,15 @@ check_http (void) my_send (buf, strlen (buf)); /* fetch the page */ - full_page = strdup(""); + full_page =(char*)malloc(sizeof(char)*4096); while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) { - buffer[i] = '\0'; - asprintf (&full_page_new, "%s%s", full_page, buffer); - free (full_page); - full_page = full_page_new; + full_page = realloc(full_page, pagesize + i); + memcpy(full_page + pagesize, buffer, i); pagesize += i; - - if (no_body && document_headers_done (full_page)) { - i = 0; - break; - } + if (no_body && document_headers_done (full_page)) { + i = 0; + break; + } } if (i < 0 && errno != ECONNRESET) { @@ -951,6 +971,28 @@ check_http (void) } page += (size_t) strspn (page, "\r\n"); header[pos - header] = 0; + + /*deal chunked data*/ + if(get_transfer_encoding(header)==1 && chunked) { + pagesize = pagesize - (page-header); + page = decodechunked(page,&pagesize); + } + else { + pagesize = get_content_length(header); + } + + /*decompress the page content*/ + int content_encoding = get_content_encoding(header); + if(decompress && (content_encoding == 1 || content_encoding == 2)) { + result = page_content_decompress(page,pagesize,&uncompress_page, + &uncompress_page_size,content_encoding); + + if(result == 0) { + page = uncompress_page; + pagesize = uncompress_page_size; + } + } + if (verbose) printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, (no_body ? " [[ skipped ]]" : page)); @@ -1384,6 +1426,8 @@ print_help (void) printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf (UT_VERBOSE); + printf (UT_DECOMPRESS); + printf (UT_CHUNKED); printf ("\n"); printf ("%s\n", _("Notes:")); @@ -1432,3 +1476,167 @@ print_usage (void) printf (" [-A string] [-k string] [-S] [--sni] [-C ] [-T ]\n"); printf (" [-j method]\n"); } + +/* HTTP gzip or deflate decompress; + * type = 1 represent gzip format + * type = 2 represent deflate format */ +int page_content_decompress(const char *in_buf,int in_size, + char **out_buf_ptr, int *out_size,int type) +{ + z_stream stream = {0}; /* decompression stream */ + char *out_buf = NULL; + int out_buf_bytes = 0; + char tmp_buf[4096]; + int result; + int new_bytes; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + stream.next_in = (void*) in_buf; + stream.avail_in = in_size; + stream.next_out = tmp_buf; + stream.avail_out = sizeof tmp_buf; + + switch (type) { + case 1: /*gzip*/ + if(inflateInit2(&stream,MAX_WBITS+32) != Z_OK) + return -1; + break; + case 2: /*deflate*/ + if(inflateInit2(&stream,-MAX_WBITS) != Z_OK) + return -1; + } + + do { + result = inflate(&stream,Z_NO_FLUSH); + switch (result) { + case Z_BUF_ERROR: + if(stream.avail_in == 0) + goto DONE; /*zlib bug */ + case Z_ERRNO: + case Z_NEED_DICT: + case Z_MEM_ERROR: + case Z_DATA_ERROR: + inflateEnd(&stream); + free(out_buf); + return -1; + } + if(stream.avail_out < sizeof tmp_buf) { + new_bytes = sizeof tmp_buf - stream.avail_out; + out_buf = realloc(out_buf,out_buf_bytes + new_bytes); + memcpy (out_buf + out_buf_bytes,tmp_buf,new_bytes); + out_buf_bytes += new_bytes; + stream.next_out = tmp_buf; + stream.avail_out = sizeof tmp_buf; + } + else { + inflateEnd(&stream); + free(out_buf); + return -1; + } + } + while(result != Z_STREAM_END); + +DONE: + + if(inflateEnd(&stream) != Z_OK) { + free(out_buf); + return -1; + } + + *out_size = out_buf_bytes; + out_buf = realloc(out_buf,out_buf_bytes + 1); + out_buf[out_buf_bytes] = 0; + *out_buf_ptr = out_buf; + + return 0; +} + +/*find content from header data by keyword*/ +int find_header_info(const char* header,char* content,const char* keyword) { + char* start = strstr(header,keyword); + if(start != 0) { + start = start + strcspn(start,": "); + start += strspn(start,": "); + char* end = start + strcspn(start," \r\n"); + if( content != NULL) { + memcpy(content,start,end-start); + } + return 1; + } + else{ + return 0; + } +} + +/*find Content-Encoding from the header info; + *if Content-Encoding equal 'gzip' return 1,or return 0. + */ +int get_content_encoding(const char* header) { + int result = 0; + int ret = 0; + char* content_encoding = (char*)malloc(sizeof(char)*10); + result = find_header_info(header,content_encoding,"Content-Encoding"); + + if(result && strncmp(content_encoding,"gzip",4)==0) { + ret = 1; + } + else if(result && strncmp(content_encoding,"deflate",7)==0) { + ret = 2; + } + else { + ret = 0; + } + if(content_encoding != NULL) { + free(content_encoding); + } + return ret; +} + +/*find Transfer-Encoding from the header info; + * if Content-Encoding equal 'chunked' return 1,or return 0. + */ +int get_transfer_encoding(const char* header) { + int result = 0; + int ret = 0; + char *transfer_encoding = (char*)malloc(sizeof(char)*10); + result = find_header_info(header,transfer_encoding,"Transfer-Encoding"); + + if(result && strncmp(transfer_encoding,"chunked",7)==0){ + ret = 1; + } + else { + ret = 0; + } + + if(transfer_encoding != NULL) { + free(transfer_encoding); + } + return ret; +} + +/* Returns NULL on invalid input */ +char* decodechunked(char * chunked, unsigned int *inputlen) { + char *orig = chunked, *dest = chunked; + unsigned long chunklen; + while((chunklen = strtoul(orig, &orig, 16))) { + /* process one more chunk: */ + /* skip chunk-extension part */ + while(*orig && (*orig != '\r')) + orig++; + /* skip '\r\n' after chunk length */ + orig += 2; + if(( chunklen > (chunked + *inputlen - orig))) + /* insane chunk length. Well... */ + return NULL; + memmove(dest, orig, chunklen); + dest += chunklen; + orig += chunklen; + /* and go to the next chunk */ + } + *dest = '\0'; + *inputlen = dest - chunked; + + return chunked; +} diff --git a/plugins/utils.h b/plugins/utils.h index 3c3f189..78a74aa 100644 --- a/plugins/utils.h +++ b/plugins/utils.h @@ -204,4 +204,12 @@ The nagios plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\n\ copies of the plugins under the terms of the GNU General Public License.\n\ For more information about these matters, see the file named COPYING.\n") +#define UT_DECOMPRESS _("\ + -d, --decompress\n\ + Decompress the contents of page which were compressed by gzip or deflate.\n") + +#define UT_CHUNKED _("\ + -X, --chunked\n\ + Deal with the data which was transferred by chunked encoding.") + #endif /* NP_UTILS_H */