summaryrefslogtreecommitdiffstats
path: root/plugins/t
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/t')
0 files changed, 0 insertions, 0 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index ff745c3..34a7da8 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -40,17 +40,17 @@ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \
40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \
41 check_procs check_mysql_query check_apt check_dbi check_curl 41 check_procs check_mysql_query check_apt check_dbi check_curl
42 42
43SUBDIRS = @PICOHTTPPARSER_DIR@ @URIPARSER_DIR@
44
43EXTRA_DIST = t tests 45EXTRA_DIST = t tests
44 46
45PLUGINHDRS = common.h 47PLUGINHDRS = common.h
46 48
47noinst_LIBRARIES = libnpcommon.a libpicohttpparser.a 49noinst_LIBRARIES = libnpcommon.a
48 50
49libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \ 51libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \
50 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h 52 popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h
51 53
52libpicohttpparser_a_SOURCES = picohttpparser.c
53
54BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a 54BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a
55NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS) 55NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS)
56NETLIBS = $(NETOBJS) $(SOCKETLIBS) 56NETLIBS = $(NETOBJS) $(SOCKETLIBS)
@@ -71,9 +71,9 @@ test-debug:
71 71
72check_apt_LDADD = $(BASEOBJS) 72check_apt_LDADD = $(BASEOBJS)
73check_cluster_LDADD = $(BASEOBJS) 73check_cluster_LDADD = $(BASEOBJS)
74check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) 74check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) -Ipicohttpparser -Iuriparser
75check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLINCLUDE) 75check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLINCLUDE) -Ipicohttpparser -Iuriparser
76check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) libpicohttpparser.a 76check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) picohttpparser/libpicohttpparser.a uriparser/liburiparser.a
77check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 77check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
78check_dig_LDADD = $(NETLIBS) 78check_dig_LDADD = $(NETLIBS)
79check_disk_LDADD = $(BASEOBJS) 79check_disk_LDADD = $(BASEOBJS)
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index 5f6905f..72db27a 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -51,17 +51,35 @@ const char *email = "devel@monitoring-plugins.org";
51 51
52#include "picohttpparser.h" 52#include "picohttpparser.h"
53 53
54#include "uriparser/Uri.h"
55
56#include <arpa/inet.h>
57
54#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch)) 58#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
55 59
56#define DEFAULT_BUFFER_SIZE 2048 60#define DEFAULT_BUFFER_SIZE 2048
57#define DEFAULT_SERVER_URL "/" 61#define DEFAULT_SERVER_URL "/"
58#define HTTP_EXPECT "HTTP/1." 62#define HTTP_EXPECT "HTTP/1."
63#define DEFAULT_MAX_REDIRS 15
64#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
59enum { 65enum {
66 MAX_IPV4_HOSTLENGTH = 255,
60 HTTP_PORT = 80, 67 HTTP_PORT = 80,
61 HTTPS_PORT = 443, 68 HTTPS_PORT = 443,
62 MAX_PORT = 65535 69 MAX_PORT = 65535
63}; 70};
64 71
72enum {
73 STICKY_NONE = 0,
74 STICKY_HOST = 1,
75 STICKY_PORT = 2
76};
77
78enum {
79 FOLLOW_HTTP_CURL = 0,
80 FOLLOW_LIBCURL = 1
81};
82
65/* for buffers for header and body */ 83/* for buffers for header and body */
66typedef struct { 84typedef struct {
67 char *buf; 85 char *buf;
@@ -128,6 +146,8 @@ int verbose = 0;
128int show_extended_perfdata = FALSE; 146int show_extended_perfdata = FALSE;
129int min_page_len = 0; 147int min_page_len = 0;
130int max_page_len = 0; 148int max_page_len = 0;
149int redir_depth = 0;
150int max_depth = DEFAULT_MAX_REDIRS;
131char *http_method = NULL; 151char *http_method = NULL;
132char *http_post_data = NULL; 152char *http_post_data = NULL;
133char *http_content_type = NULL; 153char *http_content_type = NULL;
@@ -155,8 +175,12 @@ char string_expect[MAX_INPUT_BUFFER] = "";
155char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; 175char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
156int server_expect_yn = 0; 176int server_expect_yn = 0;
157char user_auth[MAX_INPUT_BUFFER] = ""; 177char user_auth[MAX_INPUT_BUFFER] = "";
178char **http_opt_headers;
179int http_opt_headers_count = 0;
158int display_html = FALSE; 180int display_html = FALSE;
159int onredirect = STATE_OK; 181int onredirect = STATE_OK;
182int followmethod = FOLLOW_HTTP_CURL;
183int followsticky = STICKY_NONE;
160int use_ssl = FALSE; 184int use_ssl = FALSE;
161int use_sni = TRUE; 185int use_sni = TRUE;
162int check_cert = FALSE; 186int check_cert = FALSE;
@@ -181,6 +205,7 @@ curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
181int process_arguments (int, char**); 205int process_arguments (int, char**);
182void handle_curl_option_return_code (CURLcode res, const char* option); 206void handle_curl_option_return_code (CURLcode res, const char* option);
183int check_http (void); 207int check_http (void);
208void redir (curlhelp_write_curlbuf*);
184void print_help (void); 209void print_help (void);
185void print_usage (void); 210void print_usage (void);
186void print_curl_version (void); 211void print_curl_version (void);
@@ -295,6 +320,8 @@ check_http (void)
295{ 320{
296 int result = STATE_OK; 321 int result = STATE_OK;
297 int page_len = 0; 322 int page_len = 0;
323 int i;
324 char *force_host_header = NULL;
298 325
299 /* initialize curl */ 326 /* initialize curl */
300 if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK) 327 if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK)
@@ -371,6 +398,28 @@ check_http (void)
371 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); 398 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
372 header_list = curl_slist_append (header_list, http_header); 399 header_list = curl_slist_append (header_list, http_header);
373 400
401 /* check if Host header is explicitly set in options */
402 if (http_opt_headers_count) {
403 for (i = 0; i < http_opt_headers_count ; i++) {
404 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
405 force_host_header = http_opt_headers[i];
406 }
407 }
408 }
409
410 /* attach additional headers supplied by the user */
411 /* optionally send any other header tag */
412 if (http_opt_headers_count) {
413 for (i = 0; i < http_opt_headers_count ; i++) {
414 if (force_host_header != http_opt_headers[i]) {
415 header_list = curl_slist_append (header_list, http_opt_headers[i]);
416 }
417 }
418 /* This cannot be free'd here because a redirection will then try to access this and segfault */
419 /* Covered in a testcase in tests/check_http.t */
420 /* free(http_opt_headers); */
421 }
422
374 /* set HTTP headers */ 423 /* set HTTP headers */
375 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER"); 424 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER");
376 425
@@ -481,13 +530,29 @@ check_http (void)
481 530
482 /* handle redirections */ 531 /* handle redirections */
483 if (onredirect == STATE_DEPENDENT) { 532 if (onredirect == STATE_DEPENDENT) {
484 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); 533 if( followmethod == FOLLOW_LIBCURL ) {
485 /* TODO: handle the following aspects of redirection 534 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
486 CURLOPT_POSTREDIR: method switch 535
487 CURLINFO_REDIRECT_URL: custom redirect option 536 /* default -1 is infinite, not good, could lead to zombie plugins!
488 CURLOPT_REDIRECT_PROTOCOLS 537 Setting it to one bigger than maximal limit to handle errors nicely below
489 CURLINFO_REDIRECT_COUNT 538 */
490 */ 539 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_MAXREDIRS, max_depth+1), "CURLOPT_MAXREDIRS");
540
541 /* for now allow only http and https (we are a http(s) check plugin in the end) */
542#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
543 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), "CURLOPT_REDIRECT_PROTOCOLS");
544#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) */
545
546 /* TODO: handle the following aspects of redirection, make them
547 * command line options too later:
548 CURLOPT_POSTREDIR: method switch
549 CURLINFO_REDIRECT_URL: custom redirect option
550 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
551 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
552 */
553 } else {
554 // old style redirection is handled below
555 }
491 } 556 }
492 557
493 /* no-body */ 558 /* no-body */
@@ -533,8 +598,8 @@ check_http (void)
533 printf ("**** REQUEST CONTENT ****\n%s\n", http_post_data); 598 printf ("**** REQUEST CONTENT ****\n%s\n", http_post_data);
534 599
535 /* free header and server IP resolve lists, we don't need it anymore */ 600 /* free header and server IP resolve lists, we don't need it anymore */
536 curl_slist_free_all (header_list); 601 curl_slist_free_all (header_list); header_list = NULL;
537 curl_slist_free_all (server_ips); 602 curl_slist_free_all (server_ips); server_ips = NULL;
538 603
539 /* Curl errors, result in critical Nagios state */ 604 /* Curl errors, result in critical Nagios state */
540 if (res != CURLE_OK) { 605 if (res != CURLE_OK) {
@@ -691,18 +756,39 @@ GOT_FIRST_CERT:
691 /* check redirected page if specified */ 756 /* check redirected page if specified */
692 } else if (code >= 300) { 757 } else if (code >= 300) {
693 if (onredirect == STATE_DEPENDENT) { 758 if (onredirect == STATE_DEPENDENT) {
694 code = status_line.http_code; 759 if( followmethod == FOLLOW_LIBCURL ) {
760 code = status_line.http_code;
761 } else {
762 /* old check_http style redirection, if we come
763 * back here, we are in the same status as with
764 * the libcurl method
765 */
766 redir (&header_buf);
767 }
768 } else {
769 /* this is a specific code in the command line to
770 * be returned when a redirection is encoutered
771 */
695 } 772 }
696 result = max_state_alt (onredirect, result); 773 result = max_state_alt (onredirect, result);
697 /* TODO: make sure the last status line has been
698 parsed into the status_line structure
699 */
700 /* all other codes are considered ok */ 774 /* all other codes are considered ok */
701 } else { 775 } else {
702 result = STATE_OK; 776 result = STATE_OK;
703 } 777 }
704 } 778 }
705 779
780 /* libcurl redirection internally, handle error states here */
781 if( followmethod == FOLLOW_LIBCURL ) {
782 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
783 if (verbose >= 2)
784 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
785 if (redir_depth > max_depth) {
786 snprintf (msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl",
787 max_depth);
788 die (STATE_WARNING, "HTTP WARNING - %s", msg);
789 }
790 }
791
706 /* check status codes, set exit status accordingly */ 792 /* check status codes, set exit status accordingly */
707 if( status_line.http_code != code ) { 793 if( status_line.http_code != code ) {
708 die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"), 794 die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"),
@@ -813,6 +899,188 @@ GOT_FIRST_CERT:
813 return result; 899 return result;
814} 900}
815 901
902int
903uri_strcmp (const UriTextRangeA range, const char* s)
904{
905 if (!range.first) return -1;
906 if (range.afterLast - range.first < strlen (s)) return -1;
907 return strncmp (s, range.first, min( range.afterLast - range.first, strlen (s)));
908}
909
910char*
911uri_string (const UriTextRangeA range, char* buf, size_t buflen)
912{
913 if (!range.first) return "(null)";
914 strncpy (buf, range.first, max (buflen, range.afterLast - range.first));
915 buf[max (buflen, range.afterLast - range.first)] = '\0';
916 buf[range.afterLast - range.first] = '\0';
917 return buf;
918}
919
920void
921redir (curlhelp_write_curlbuf* header_buf)
922{
923 char *location = NULL;
924 curlhelp_statusline status_line;
925 struct phr_header headers[255];
926 size_t nof_headers = 255;
927 size_t msglen;
928 char buf[DEFAULT_BUFFER_SIZE];
929 char ipstr[INET_ADDR_MAX_SIZE];
930 int new_port;
931 char *new_host;
932 char *new_url;
933
934 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
935 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
936 headers, &nof_headers, 0);
937
938 location = get_header_value (headers, nof_headers, "location");
939
940 if (verbose >= 2)
941 printf(_("* Seen redirect location %s\n"), location);
942
943 if (++redir_depth > max_depth)
944 die (STATE_WARNING,
945 _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"),
946 max_depth, location, (display_html ? "</A>" : ""));
947
948 UriParserStateA state;
949 UriUriA uri;
950 state.uri = &uri;
951 if (uriParseUriA (&state, location) != URI_SUCCESS) {
952 if (state.errorCode == URI_ERROR_SYNTAX) {
953 die (STATE_UNKNOWN,
954 _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"),
955 location, (display_html ? "</A>" : ""));
956 } else if (state.errorCode == URI_ERROR_MALLOC) {
957 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
958 }
959 }
960
961 if (verbose >= 2) {
962 printf (_("** scheme: %s\n"),
963 uri_string (uri.scheme, buf, DEFAULT_BUFFER_SIZE));
964 printf (_("** host: %s\n"),
965 uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
966 printf (_("** port: %s\n"),
967 uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
968 if (uri.hostData.ip4) {
969 inet_ntop (AF_INET, uri.hostData.ip4->data, ipstr, sizeof (ipstr));
970 printf (_("** IPv4: %s\n"), ipstr);
971 }
972 if (uri.hostData.ip6) {
973 inet_ntop (AF_INET, uri.hostData.ip6->data, ipstr, sizeof (ipstr));
974 printf (_("** IPv6: %s\n"), ipstr);
975 }
976 if (uri.pathHead) {
977 printf (_("** path: "));
978 const UriPathSegmentA* p = uri.pathHead;
979 for (; p; p = p->next) {
980 printf ("/%s", uri_string (p->text, buf, DEFAULT_BUFFER_SIZE));
981 }
982 puts ("");
983 }
984 if (uri.query.first) {
985 printf (_("** query: %s\n"),
986 uri_string (uri.query, buf, DEFAULT_BUFFER_SIZE));
987 }
988 if (uri.fragment.first) {
989 printf (_("** fragment: %s\n"),
990 uri_string (uri.fragment, buf, DEFAULT_BUFFER_SIZE));
991 }
992 }
993
994 use_ssl = !uri_strcmp (uri.scheme, "https");
995
996 /* we do a sloppy test here only, because uriparser would have failed
997 * above, if the port would be invalid, we just check for MAX_PORT
998 */
999 if (uri.portText.first) {
1000 new_port = atoi (uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
1001 } else {
1002 new_port = HTTP_PORT;
1003 if (use_ssl)
1004 new_port = HTTPS_PORT;
1005 }
1006 if (new_port > MAX_PORT)
1007 die (STATE_UNKNOWN,
1008 _("HTTP UNKNOWN - Redirection to port above %d - %s\n"),
1009 MAX_PORT, location, display_html ? "</A>" : "");
1010
1011 /* by RFC 7231 relative URLs in Location should be taken relative to
1012 * the original URL, so wy try to form a new absolute URL here
1013 */
1014 if (!uri.scheme.first && !uri.hostText.first) {
1015 /* TODO: implement */
1016 die (STATE_UNKNOWN, _("HTTP UNKNOWN - non-absolute location, not implemented yet!\n"));
1017 new_host = strdup (host_name ? host_name : server_address);
1018 } else {
1019 new_host = strdup (uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1020 }
1021
1022 new_url = (char *)calloc( 1, DEFAULT_BUFFER_SIZE);
1023 if (uri.pathHead) {
1024 const UriPathSegmentA* p = uri.pathHead;
1025 for (; p; p = p->next) {
1026 strncat (new_url, "/", DEFAULT_BUFFER_SIZE);
1027 strncat (new_url, uri_string (p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE);
1028 }
1029 }
1030
1031 if (server_port==new_port &&
1032 !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1033 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
1034 !strcmp(server_url, new_url))
1035 die (STATE_WARNING,
1036 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1037 use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "</A>" : ""));
1038
1039 /* set new values for redirected request */
1040
1041 free (host_name);
1042 host_name = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1043 free(new_host);
1044
1045 server_port = (unsigned short)new_port;
1046
1047 /* reset virtual port */
1048 virtual_port = server_port;
1049
1050 if (!(followsticky & STICKY_HOST)) {
1051 free (server_address);
1052 server_address = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1053 }
1054 if (!(followsticky & STICKY_PORT)) {
1055 server_port = new_port;
1056 }
1057
1058 free (server_url);
1059 server_url = new_url;
1060
1061 uriFreeUriMembersA (&uri);
1062
1063 if (verbose)
1064 printf (_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http",
1065 host_name ? host_name : server_address, server_port, server_url);
1066
1067 /* TODO: the hash component MUST be taken from the original URL and
1068 * attached to the URL in Location
1069 */
1070
1071 check_http ();
1072}
1073
1074#if 0
1075
1076int main(int argc, char *argv[]) {
1077
1078 for (; i < argc; i++) {
1079
1080 }
1081 printf("\n");
1082#endif
1083
816/* check whether a file exists */ 1084/* check whether a file exists */
817void 1085void
818test_file (char *path) 1086test_file (char *path)
@@ -974,7 +1242,11 @@ process_arguments (int argc, char **argv)
974 snprintf (user_agent, DEFAULT_BUFFER_SIZE, optarg); 1242 snprintf (user_agent, DEFAULT_BUFFER_SIZE, optarg);
975 break; 1243 break;
976 case 'k': /* Additional headers */ 1244 case 'k': /* Additional headers */
977 header_list = curl_slist_append(header_list, optarg); 1245 if (http_opt_headers_count == 0)
1246 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
1247 else
1248 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
1249 http_opt_headers[http_opt_headers_count - 1] = optarg;
978 break; 1250 break;
979 case 'L': /* show html link */ 1251 case 'L': /* show html link */
980 display_html = TRUE; 1252 display_html = TRUE;
@@ -1121,6 +1393,14 @@ process_arguments (int argc, char **argv)
1121 onredirect = STATE_UNKNOWN; 1393 onredirect = STATE_UNKNOWN;
1122 else if (!strcmp (optarg, "follow")) 1394 else if (!strcmp (optarg, "follow"))
1123 onredirect = STATE_DEPENDENT; 1395 onredirect = STATE_DEPENDENT;
1396 else if (!strcmp (optarg, "stickyport"))
1397 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST|STICKY_PORT;
1398 else if (!strcmp (optarg, "sticky"))
1399 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST;
1400 else if (!strcmp (optarg, "follow"))
1401 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE;
1402 else if (!strcmp (optarg, "curl"))
1403 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL;
1124 else usage2 (_("Invalid onredirect option"), optarg); 1404 else usage2 (_("Invalid onredirect option"), optarg);
1125 if (verbose >= 2) 1405 if (verbose >= 2)
1126 printf(_("* Following redirects set to %s\n"), state_text(onredirect)); 1406 printf(_("* Following redirects set to %s\n"), state_text(onredirect));
@@ -1264,7 +1544,6 @@ print_help (void)
1264 print_revision (progname, NP_VERSION); 1544 print_revision (progname, NP_VERSION);
1265 1545
1266 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"); 1546 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1267 printf ("Copyright (c) 2017 Andreas Baumann <mail@andreasbaumann.cc>\n");
1268 printf (COPYRIGHT, copyright, email); 1547 printf (COPYRIGHT, copyright, email);
1269 1548
1270 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); 1549 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
@@ -1365,8 +1644,11 @@ print_help (void)
1365 printf (" %s\n", _("Print additional performance data")); 1644 printf (" %s\n", _("Print additional performance data"));
1366 printf (" %s\n", "-L, --link"); 1645 printf (" %s\n", "-L, --link");
1367 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); 1646 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1368 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow>"); 1647 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1369 printf (" %s\n", _("How to handle redirected pages.")); 1648 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1649 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1650 printf (" %s\n", _("follow uses the old redirection algorithm of check_http."));
1651 printf (" %s\n", _("curl uses CURL_FOLLOWLOCATION built into libcurl."));
1370 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1652 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1371 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); 1653 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1372 1654
@@ -1436,7 +1718,7 @@ print_usage (void)
1436 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname); 1718 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1437 printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>]\n"); 1719 printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>]\n");
1438 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1720 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1439 printf (" [-f <ok|warning|critcal|follow>]\n"); 1721 printf (" [-f <ok|warning|critcal|follow|sticky|stickyport|curl>]\n");
1440 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); 1722 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1441 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1723 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1442 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n"); 1724 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
@@ -1739,7 +2021,7 @@ get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_wri
1739 content_length_s += strspn (content_length_s, " \t"); 2021 content_length_s += strspn (content_length_s, " \t");
1740 content_length = atoi (content_length_s); 2022 content_length = atoi (content_length_s);
1741 if (content_length != body_buf->buflen) { 2023 if (content_length != body_buf->buflen) {
1742 /* TODO: should we warn if the actual and the reported body length doen't match? */ 2024 /* TODO: should we warn if the actual and the reported body length don't match? */
1743 } 2025 }
1744 2026
1745 if (content_length_s) free (content_length_s); 2027 if (content_length_s) free (content_length_s);
diff --git a/plugins/picohttpparser.c b/plugins/picohttpparser.c
deleted file mode 100644
index 6a2d872..0000000
--- a/plugins/picohttpparser.c
+++ /dev/null
@@ -1,620 +0,0 @@
1/*
2 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3 * Shigeo Mitsunari
4 *
5 * The software is licensed under either the MIT License (below) or the Perl
6 * license.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27#include <assert.h>
28#include <stddef.h>
29#include <string.h>
30#ifdef __SSE4_2__
31#ifdef _MSC_VER
32#include <nmmintrin.h>
33#else
34#include <x86intrin.h>
35#endif
36#endif
37#include "picohttpparser.h"
38
39/* $Id: a707070d11d499609f99d09f97535642cec910a8 $ */
40
41#if __GNUC__ >= 3
42#define likely(x) __builtin_expect(!!(x), 1)
43#define unlikely(x) __builtin_expect(!!(x), 0)
44#else
45#define likely(x) (x)
46#define unlikely(x) (x)
47#endif
48
49#ifdef _MSC_VER
50#define ALIGNED(n) _declspec(align(n))
51#else
52#define ALIGNED(n) __attribute__((aligned(n)))
53#endif
54
55#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
56
57#define CHECK_EOF() \
58 if (buf == buf_end) { \
59 *ret = -2; \
60 return NULL; \
61 }
62
63#define EXPECT_CHAR_NO_CHECK(ch) \
64 if (*buf++ != ch) { \
65 *ret = -1; \
66 return NULL; \
67 }
68
69#define EXPECT_CHAR(ch) \
70 CHECK_EOF(); \
71 EXPECT_CHAR_NO_CHECK(ch);
72
73#define ADVANCE_TOKEN(tok, toklen) \
74 do { \
75 const char *tok_start = buf; \
76 static const char ALIGNED(16) ranges2[] = "\000\040\177\177"; \
77 int found2; \
78 buf = findchar_fast(buf, buf_end, ranges2, sizeof(ranges2) - 1, &found2); \
79 if (!found2) { \
80 CHECK_EOF(); \
81 } \
82 while (1) { \
83 if (*buf == ' ') { \
84 break; \
85 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
86 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
87 *ret = -1; \
88 return NULL; \
89 } \
90 } \
91 ++buf; \
92 CHECK_EOF(); \
93 } \
94 tok = tok_start; \
95 toklen = buf - tok_start; \
96 } while (0)
97
98static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
99 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
100 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
101 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
104 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
105 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
106
107static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
108{
109 *found = 0;
110#if __SSE4_2__
111 if (likely(buf_end - buf >= 16)) {
112 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
113
114 size_t left = (buf_end - buf) & ~15;
115 do {
116 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
117 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
118 if (unlikely(r != 16)) {
119 buf += r;
120 *found = 1;
121 break;
122 }
123 buf += 16;
124 left -= 16;
125 } while (likely(left != 0));
126 }
127#else
128 /* suppress unused parameter warning */
129 (void)buf_end;
130 (void)ranges;
131 (void)ranges_size;
132#endif
133 return buf;
134}
135
136static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
137{
138 const char *token_start = buf;
139
140#ifdef __SSE4_2__
141 static const char ranges1[] = "\0\010"
142 /* allow HT */
143 "\012\037"
144 /* allow SP and up to but not including DEL */
145 "\177\177"
146 /* allow chars w. MSB set */
147 ;
148 int found;
149 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
150 if (found)
151 goto FOUND_CTL;
152#else
153 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
154 while (likely(buf_end - buf >= 8)) {
155#define DOIT() \
156 do { \
157 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
158 goto NonPrintable; \
159 ++buf; \
160 } while (0)
161 DOIT();
162 DOIT();
163 DOIT();
164 DOIT();
165 DOIT();
166 DOIT();
167 DOIT();
168 DOIT();
169#undef DOIT
170 continue;
171 NonPrintable:
172 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
173 goto FOUND_CTL;
174 }
175 ++buf;
176 }
177#endif
178 for (;; ++buf) {
179 CHECK_EOF();
180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
182 goto FOUND_CTL;
183 }
184 }
185 }
186FOUND_CTL:
187 if (likely(*buf == '\015')) {
188 ++buf;
189 EXPECT_CHAR('\012');
190 *token_len = buf - 2 - token_start;
191 } else if (*buf == '\012') {
192 *token_len = buf - token_start;
193 ++buf;
194 } else {
195 *ret = -1;
196 return NULL;
197 }
198 *token = token_start;
199
200 return buf;
201}
202
203static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
204{
205 int ret_cnt = 0;
206 buf = last_len < 3 ? buf : buf + last_len - 3;
207
208 while (1) {
209 CHECK_EOF();
210 if (*buf == '\015') {
211 ++buf;
212 CHECK_EOF();
213 EXPECT_CHAR('\012');
214 ++ret_cnt;
215 } else if (*buf == '\012') {
216 ++buf;
217 ++ret_cnt;
218 } else {
219 ++buf;
220 ret_cnt = 0;
221 }
222 if (ret_cnt == 2) {
223 return buf;
224 }
225 }
226
227 *ret = -2;
228 return NULL;
229}
230
231#define PARSE_INT(valp_, mul_) \
232 if (*buf < '0' || '9' < *buf) { \
233 buf++; \
234 *ret = -1; \
235 return NULL; \
236 } \
237 *(valp_) = (mul_) * (*buf++ - '0');
238
239#define PARSE_INT_3(valp_) \
240 do { \
241 int res_ = 0; \
242 PARSE_INT(&res_, 100) \
243 *valp_ = res_; \
244 PARSE_INT(&res_, 10) \
245 *valp_ += res_; \
246 PARSE_INT(&res_, 1) \
247 *valp_ += res_; \
248 } while (0)
249
250/* returned pointer is always within [buf, buf_end), or null */
251static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
252{
253 /* we want at least [HTTP/1.<two chars>] to try to parse */
254 if (buf_end - buf < 9) {
255 *ret = -2;
256 return NULL;
257 }
258 EXPECT_CHAR_NO_CHECK('H');
259 EXPECT_CHAR_NO_CHECK('T');
260 EXPECT_CHAR_NO_CHECK('T');
261 EXPECT_CHAR_NO_CHECK('P');
262 EXPECT_CHAR_NO_CHECK('/');
263 EXPECT_CHAR_NO_CHECK('1');
264 EXPECT_CHAR_NO_CHECK('.');
265 PARSE_INT(minor_version, 1);
266 return buf;
267}
268
269static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
270 size_t max_headers, int *ret)
271{
272 for (;; ++*num_headers) {
273 CHECK_EOF();
274 if (*buf == '\015') {
275 ++buf;
276 EXPECT_CHAR('\012');
277 break;
278 } else if (*buf == '\012') {
279 ++buf;
280 break;
281 }
282 if (*num_headers == max_headers) {
283 *ret = -1;
284 return NULL;
285 }
286 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
287 /* parsing name, but do not discard SP before colon, see
288 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
289 headers[*num_headers].name = buf;
290 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
291 "\"\"" /* 0x22 */
292 "()" /* 0x28,0x29 */
293 ",," /* 0x2c */
294 "//" /* 0x2f */
295 ":@" /* 0x3a-0x40 */
296 "[]" /* 0x5b-0x5d */
297 "{\377"; /* 0x7b-0xff */
298 int found;
299 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
300 if (!found) {
301 CHECK_EOF();
302 }
303 while (1) {
304 if (*buf == ':') {
305 break;
306 } else if (!token_char_map[(unsigned char)*buf]) {
307 *ret = -1;
308 return NULL;
309 }
310 ++buf;
311 CHECK_EOF();
312 }
313 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
314 *ret = -1;
315 return NULL;
316 }
317 ++buf;
318 for (;; ++buf) {
319 CHECK_EOF();
320 if (!(*buf == ' ' || *buf == '\t')) {
321 break;
322 }
323 }
324 } else {
325 headers[*num_headers].name = NULL;
326 headers[*num_headers].name_len = 0;
327 }
328 if ((buf = get_token_to_eol(buf, buf_end, &headers[*num_headers].value, &headers[*num_headers].value_len, ret)) == NULL) {
329 return NULL;
330 }
331 }
332 return buf;
333}
334
335static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
336 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
337 size_t max_headers, int *ret)
338{
339 /* skip first empty line (some clients add CRLF after POST content) */
340 CHECK_EOF();
341 if (*buf == '\015') {
342 ++buf;
343 EXPECT_CHAR('\012');
344 } else if (*buf == '\012') {
345 ++buf;
346 }
347
348 /* parse request line */
349 ADVANCE_TOKEN(*method, *method_len);
350 ++buf;
351 ADVANCE_TOKEN(*path, *path_len);
352 ++buf;
353 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
354 return NULL;
355 }
356 if (*buf == '\015') {
357 ++buf;
358 EXPECT_CHAR('\012');
359 } else if (*buf == '\012') {
360 ++buf;
361 } else {
362 *ret = -1;
363 return NULL;
364 }
365
366 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
367}
368
369int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
370 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
371{
372 const char *buf = buf_start, *buf_end = buf_start + len;
373 size_t max_headers = *num_headers;
374 int r;
375
376 *method = NULL;
377 *method_len = 0;
378 *path = NULL;
379 *path_len = 0;
380 *minor_version = -1;
381 *num_headers = 0;
382
383 /* if last_len != 0, check if the request is complete (a fast countermeasure
384 againt slowloris */
385 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
386 return r;
387 }
388
389 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
390 &r)) == NULL) {
391 return r;
392 }
393
394 return (int)(buf - buf_start);
395}
396
397static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
398 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
399{
400 /* parse "HTTP/1.x" */
401 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
402 return NULL;
403 }
404 /* skip space */
405 if (*buf++ != ' ') {
406 *ret = -1;
407 return NULL;
408 }
409 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
410 if (buf_end - buf < 4) {
411 *ret = -2;
412 return NULL;
413 }
414 PARSE_INT_3(status);
415
416 /* skip space */
417 if (*buf++ != ' ') {
418 *ret = -1;
419 return NULL;
420 }
421 /* get message */
422 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
423 return NULL;
424 }
425
426 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
427}
428
429int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
430 struct phr_header *headers, size_t *num_headers, size_t last_len)
431{
432 const char *buf = buf_start, *buf_end = buf + len;
433 size_t max_headers = *num_headers;
434 int r;
435
436 *minor_version = -1;
437 *status = 0;
438 *msg = NULL;
439 *msg_len = 0;
440 *num_headers = 0;
441
442 /* if last_len != 0, check if the response is complete (a fast countermeasure
443 against slowloris */
444 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
445 return r;
446 }
447
448 if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
449 return r;
450 }
451
452 return (int)(buf - buf_start);
453}
454
455int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
456{
457 const char *buf = buf_start, *buf_end = buf + len;
458 size_t max_headers = *num_headers;
459 int r;
460
461 *num_headers = 0;
462
463 /* if last_len != 0, check if the response is complete (a fast countermeasure
464 against slowloris */
465 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
466 return r;
467 }
468
469 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
470 return r;
471 }
472
473 return (int)(buf - buf_start);
474}
475
476enum {
477 CHUNKED_IN_CHUNK_SIZE,
478 CHUNKED_IN_CHUNK_EXT,
479 CHUNKED_IN_CHUNK_DATA,
480 CHUNKED_IN_CHUNK_CRLF,
481 CHUNKED_IN_TRAILERS_LINE_HEAD,
482 CHUNKED_IN_TRAILERS_LINE_MIDDLE
483};
484
485static int decode_hex(int ch)
486{
487 if ('0' <= ch && ch <= '9') {
488 return ch - '0';
489 } else if ('A' <= ch && ch <= 'F') {
490 return ch - 'A' + 0xa;
491 } else if ('a' <= ch && ch <= 'f') {
492 return ch - 'a' + 0xa;
493 } else {
494 return -1;
495 }
496}
497
498ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
499{
500 size_t dst = 0, src = 0, bufsz = *_bufsz;
501 ssize_t ret = -2; /* incomplete */
502
503 while (1) {
504 switch (decoder->_state) {
505 case CHUNKED_IN_CHUNK_SIZE:
506 for (;; ++src) {
507 int v;
508 if (src == bufsz)
509 goto Exit;
510 if ((v = decode_hex(buf[src])) == -1) {
511 if (decoder->_hex_count == 0) {
512 ret = -1;
513 goto Exit;
514 }
515 break;
516 }
517 if (decoder->_hex_count == sizeof(size_t) * 2) {
518 ret = -1;
519 goto Exit;
520 }
521 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
522 ++decoder->_hex_count;
523 }
524 decoder->_hex_count = 0;
525 decoder->_state = CHUNKED_IN_CHUNK_EXT;
526 /* fallthru */
527 case CHUNKED_IN_CHUNK_EXT:
528 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
529 for (;; ++src) {
530 if (src == bufsz)
531 goto Exit;
532 if (buf[src] == '\012')
533 break;
534 }
535 ++src;
536 if (decoder->bytes_left_in_chunk == 0) {
537 if (decoder->consume_trailer) {
538 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
539 break;
540 } else {
541 goto Complete;
542 }
543 }
544 decoder->_state = CHUNKED_IN_CHUNK_DATA;
545 /* fallthru */
546 case CHUNKED_IN_CHUNK_DATA: {
547 size_t avail = bufsz - src;
548 if (avail < decoder->bytes_left_in_chunk) {
549 if (dst != src)
550 memmove(buf + dst, buf + src, avail);
551 src += avail;
552 dst += avail;
553 decoder->bytes_left_in_chunk -= avail;
554 goto Exit;
555 }
556 if (dst != src)
557 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
558 src += decoder->bytes_left_in_chunk;
559 dst += decoder->bytes_left_in_chunk;
560 decoder->bytes_left_in_chunk = 0;
561 decoder->_state = CHUNKED_IN_CHUNK_CRLF;
562 }
563 /* fallthru */
564 case CHUNKED_IN_CHUNK_CRLF:
565 for (;; ++src) {
566 if (src == bufsz)
567 goto Exit;
568 if (buf[src] != '\015')
569 break;
570 }
571 if (buf[src] != '\012') {
572 ret = -1;
573 goto Exit;
574 }
575 ++src;
576 decoder->_state = CHUNKED_IN_CHUNK_SIZE;
577 break;
578 case CHUNKED_IN_TRAILERS_LINE_HEAD:
579 for (;; ++src) {
580 if (src == bufsz)
581 goto Exit;
582 if (buf[src] != '\015')
583 break;
584 }
585 if (buf[src++] == '\012')
586 goto Complete;
587 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
588 /* fallthru */
589 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
590 for (;; ++src) {
591 if (src == bufsz)
592 goto Exit;
593 if (buf[src] == '\012')
594 break;
595 }
596 ++src;
597 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
598 break;
599 default:
600 assert(!"decoder is corrupt");
601 }
602 }
603
604Complete:
605 ret = bufsz - src;
606Exit:
607 if (dst != src)
608 memmove(buf + dst, buf + src, bufsz - src);
609 *_bufsz = dst;
610 return ret;
611}
612
613int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
614{
615 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
616}
617
618#undef CHECK_EOF
619#undef EXPECT_CHAR
620#undef ADVANCE_TOKEN
diff --git a/plugins/picohttpparser.h b/plugins/picohttpparser.h
deleted file mode 100644
index a8fad71..0000000
--- a/plugins/picohttpparser.h
+++ /dev/null
@@ -1,89 +0,0 @@
1/*
2 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3 * Shigeo Mitsunari
4 *
5 * The software is licensed under either the MIT License (below) or the Perl
6 * license.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27#ifndef picohttpparser_h
28#define picohttpparser_h
29
30#include <sys/types.h>
31
32#ifdef _MSC_VER
33#define ssize_t intptr_t
34#endif
35
36/* $Id: 67fd3ee74103ada60258d8a16e868f483abcca87 $ */
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42/* contains name and value of a header (name == NULL if is a continuing line
43 * of a multiline header */
44struct phr_header {
45 const char *name;
46 size_t name_len;
47 const char *value;
48 size_t value_len;
49};
50
51/* returns number of bytes consumed if successful, -2 if request is partial,
52 * -1 if failed */
53int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
54 int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
55
56/* ditto */
57int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
58 struct phr_header *headers, size_t *num_headers, size_t last_len);
59
60/* ditto */
61int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
62
63/* should be zero-filled before start */
64struct phr_chunked_decoder {
65 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
66 char consume_trailer; /* if trailing headers should be consumed */
67 char _hex_count;
68 char _state;
69};
70
71/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
72 * encoding headers. When the function returns without an error, bufsz is
73 * updated to the length of the decoded data available. Applications should
74 * repeatedly call the function while it returns -2 (incomplete) every time
75 * supplying newly arrived data. If the end of the chunked-encoded data is
76 * found, the function returns a non-negative number indicating the number of
77 * octets left undecoded at the tail of the supplied buffer. Returns -1 on
78 * error.
79 */
80ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
81
82/* returns if the chunked decoder is in middle of chunked data */
83int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
84
85#ifdef __cplusplus
86}
87#endif
88
89#endif