From 2a9812ee43d64d11be96544f84501686541da86b Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Thu, 19 Jan 2017 10:23:00 +0100 Subject: added most options from nagios-curl-plugin diff --git a/plugins/check_curl.c b/plugins/check_curl.c index be024fe..e4230dc 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -40,6 +40,20 @@ const char *email = "devel@monitoring-plugins.org"; #include "common.h" #include "utils.h" +#ifdef HAVE_SYS_TYPES_H +#include +#else +#define unsigned int size_t +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + #ifndef LIBCURL_PROTOCOL_HTTP #error libcurl compiled without HTTP support, compiling check_curl plugin makes not much sense #endif @@ -47,50 +61,343 @@ const char *email = "devel@monitoring-plugins.org"; #include "curl/curl.h" #include "curl/easy.h" -int verbose = FALSE; +#define DEFAULT_BUFFER_SIZE 2048 +#define DEFAULT_SERVER_URL "/" +#define DEFAULT_HTTP_PORT 80 +#define DEFAULT_HTTPS_PORT 443 +#define MAX_PORT 65535 + +/* for buffers for header and body */ +typedef struct { + char *buf; + size_t buflen; + size_t bufsize; +} curlhelp_curlbuf; + +/* for parsing the HTTP status line */ +typedef struct { + int http_major; /* major version of the protocol, always 1 (HTTP/0.9 + * never reached the big internet most likely) */ + int http_minor; /* minor version of the protocol, usually 0 or 1 */ + int http_code; /* HTTP return code as in RFC 2145 */ + int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see + * http://support.microsoft.com/kb/318380/en-us */ + char *msg; /* the human readable message */ + char *first_line; /* a copy of the first line */ +} curlhelp_statusline; + +char *server_address; +char *host_name; +char *server_url = DEFAULT_SERVER_URL; +unsigned short server_port = DEFAULT_HTTP_PORT; +char *warning_thresholds = NULL; +char *critical_thresholds = NULL; +thresholds *thlds; +char user_agent[DEFAULT_BUFFER_SIZE]; +int verbose = 0; CURL *curl; +struct curl_slist *header_list = NULL; +curlhelp_curlbuf body_buf; +curlhelp_curlbuf header_buf; +curlhelp_statusline status_line; +char http_header[DEFAULT_BUFFER_SIZE]; +long code; +long socket_timeout = DEFAULT_SOCKET_TIMEOUT; +double total_time; +char errbuf[CURL_ERROR_SIZE+1]; +CURLcode res; +char url[DEFAULT_BUFFER_SIZE]; +char msg[DEFAULT_BUFFER_SIZE]; +char perfstring[DEFAULT_BUFFER_SIZE]; +char user_auth[MAX_INPUT_BUFFER] = ""; +int onredirect = STATE_OK; +int use_ssl = FALSE; +int use_sni = TRUE; +int check_cert = FALSE; +int ssl_version = CURL_SSLVERSION_DEFAULT; +char *client_cert = NULL; +char *client_privkey = NULL; -int process_arguments (int, char **); +int process_arguments (int, char**); void print_help (void); void print_usage (void); void print_curl_version (void); +int curlhelp_initbuffer (curlhelp_curlbuf*); +int curlhelp_buffer_callback (void*, size_t , size_t , void*); +void curlhelp_freebuffer (curlhelp_curlbuf*); + +int curlhelp_parse_statusline (char*, curlhelp_statusline *); +void curlhelp_free_statusline (curlhelp_statusline *); + +void remove_newlines (char *); +void test_file (char *); int main (int argc, char **argv) { - int result = STATE_UNKNOWN; + int result = STATE_OK; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); /* Parse extra opts if any */ - argv=np_extra_opts (&argc, argv, progname); + argv = np_extra_opts (&argc, argv, progname); + + /* set defaults */ + snprintf( user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s)", + progname, NP_VERSION, VERSION); + /* parse arguments */ if (process_arguments (argc, argv) == ERROR) usage4 (_("Could not parse arguments")); - + + /* initialize curl */ if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK) die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); if ((curl = curl_easy_init()) == NULL) die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); + if (verbose >= 3) + curl_easy_setopt (curl, CURLOPT_VERBOSE, TRUE); + + /* initialize buffer for body of the answer */ + if (curlhelp_initbuffer(&body_buf) < 0) + die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); + curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_callback); + curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)&body_buf); + + /* initialize buffer for header of the answer */ + if (curlhelp_initbuffer( &header_buf ) < 0) + die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n" ); + curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_callback); + curl_easy_setopt (curl, CURLOPT_WRITEHEADER, (void *)&header_buf); + + /* set the error buffer */ + curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf); + + /* set timeouts */ + curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, socket_timeout); + curl_easy_setopt (curl, CURLOPT_TIMEOUT, socket_timeout); + + /* compose URL */ + snprintf (url, DEFAULT_BUFFER_SIZE, "%s://%s%s", use_ssl ? "https" : "http", + server_address, server_url); + curl_easy_setopt (curl, CURLOPT_URL, url); + + /* set port */ + curl_easy_setopt (curl, CURLOPT_PORT, server_port); + + /* compose HTTP headers */ + snprintf (http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name); + header_list = curl_slist_append (header_list, http_header); + curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ); + + /* set SSL version, warn about unsecure or unsupported versions */ + if (use_ssl) { + curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version); + } + + /* client certificate and key to present to server (SSL) */ + if (client_cert) + curl_easy_setopt (curl, CURLOPT_SSLCERT, client_cert); + if (client_privkey) + curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey); + + /* per default if we have a CA verify both the peer and the + * hostname in the certificate, can be switched off later */ + curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 2); + curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2); + + /* backward-compatible behaviour, be tolerant in checks */ + if (!check_cert) { + //TODO: depending on more options have aspects we want + //to be tolerant about + //curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1 ); + curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0); + } + + /* set default or user-given user agent identification */ + curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent); + + /* authentication */ + if (strcmp(user_auth, "")) + curl_easy_setopt (curl, CURLOPT_USERPWD, user_auth); + + /* TODO: parameter auth method, bitfield of following methods: + * CURLAUTH_BASIC (default) + * CURLAUTH_DIGEST + * CURLAUTH_DIGEST_IE + * CURLAUTH_NEGOTIATE + * CURLAUTH_NTLM + * CURLAUTH_NTLM_WB + * + * convenience tokens for typical sets of methods: + * CURLAUTH_ANYSAFE: most secure, without BASIC + * or CURLAUTH_ANY: most secure, even BASIC if necessary + * + * curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ); + */ + + /* TODO: --cacert: CA certificate file to verify SSL connection against (SSL) */ + //~ if( args_info.cacert_given ) { + //~ curl_easy_setopt( curl, CURLOPT_CAINFO, args_info.cacert_arg ); + //~ } + + /* TODO: old option -s: check if the excepted string matches */ + //~ if( args_info.string_given ) { + //~ if( strstr( body_buf.buf, args_info.string_arg ) == NULL ) { + //~ printf( "HTTP CRITICAL - string not found|%s\n", perfstring ); + //~ curl_easy_cleanup( curl ); + //~ curl_global_cleanup( ); + //~ curlhelp_freebuffer( &body_buf ); + //~ curlhelp_freebuffer( &header_buf ); + //~ exit( STATE_CRITICAL ); + //~ } + //~ } + + /* handle redirections */ + if (onredirect == STATE_DEPENDENT) { + curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1); + /* TODO: handle the following aspects of redirection + CURLOPT_POSTREDIR: method switch + CURLINFO_REDIRECT_URL: custom redirect option + CURLOPT_REDIRECT_PROTOCOLS + CURLINFO_REDIRECT_COUNT + */ + } + + /* do the request */ + res = curl_easy_perform(curl); + + /* free header list, we don't need it anymore */ + curl_slist_free_all (header_list); + + /* Curl errors, result in critical Nagios state */ + if (res != CURLE_OK) { + remove_newlines (errbuf); + snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), + server_port, status_line.msg, status_line.msg); + die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", errbuf); + } + + /* we got the data and we executed the request in a given time, so we can append + * performance data to the answer always + */ + curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &total_time); + snprintf (perfstring, DEFAULT_BUFFER_SIZE, "time=%.6gs;%.6g;%.6g;%.6g size=%dB;;;0", + total_time, + 0.0, 0.0, + //~ args_info.warning_given ? args_info.warning_arg : 0.0, + //~ args_info.critical_given ? args_info.critical_arg : 0.0, + 0.0, + (int)body_buf.buflen); + + /* return a CRITICAL status if we couldn't read any data */ + if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) + die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); + + /* get status line of answer, check sanity of HTTP code */ + if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) { + snprintf (msg, DEFAULT_BUFFER_SIZE, "Unparseable status line in %.3g seconds response time|%s\n", + code, total_time, perfstring); + die (STATE_CRITICAL, "HTTP CRITICAL HTTP/1.x %d unknown - %s", code, msg); + } + + /* get result code from cURL */ + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code); + if (verbose>=2) + printf ("* curl CURLINFO_RESPONSE_CODE is %d\n", code); + + /* print status line, header, body if verbose */ + if (verbose >= 2) { + puts ("--- HEADER ---"); + puts (header_buf.buf); + puts ("--- BODY ---"); + puts (body_buf.buf); + } + + /* illegal return codes result in a critical state */ + if (code >= 600 || code < 100) { + die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); + /* server errors result in a critical state */ + } else if (code >= 500) { + result = STATE_CRITICAL; + /* client errors result in a warning state */ + } else if (code >= 400) { + result = STATE_WARNING; + /* check redirected page if specified */ + } else if (code >= 300) { + if (onredirect == STATE_DEPENDENT) { + code = status_line.http_code; + } + result = max_state_alt (onredirect, result); + // TODO: make sure the last status line has been + // parsed into the status_line structure + /* all other codes are considered ok */ + } else { + result = STATE_OK; + } + + /* check status codes, set exit status accordingly */ + if( status_line.http_code != code ) { + die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"), + status_line.http_major, status_line.http_minor, + status_line.http_code, status_line.msg, code); + } + + /* -w, -c: check warning and critical level */ + result = max_state_alt(get_status(total_time, thlds), result); + + //~ die (result, "HTTP %s: %s\n", state_text(result), msg); + die (result, "HTTP %s HTTP/%d.%d %d %s - %.3g seconds response time|%s\n", + state_text(result), status_line.http_major, status_line.http_minor, + status_line.http_code, status_line.msg, + total_time, perfstring); + + /* proper cleanup after die? */ + curlhelp_free_statusline(&status_line); curl_easy_cleanup (curl); curl_global_cleanup (); - + curlhelp_freebuffer(&body_buf); + curlhelp_freebuffer(&header_buf); + return result; } +/* check whether a file exists */ +void +test_file (char *path) +{ + if (access(path, R_OK) == 0) + return; + usage2 (_("file does not exist or is not readable"), path); +} + int process_arguments (int argc, char **argv) { int c; + + enum { + SNI_OPTION + }; + int option=0; static struct option longopts[] = { - {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, - {"verbose", no_argument, 0, 'v'}, + {"ssl", optional_argument, 0, 'S'}, + {"sni", no_argument, 0, SNI_OPTION}, + {"IP-address", required_argument, 0, 'I'}, + {"url", required_argument, 0, 'u'}, + {"port", required_argument, 0, 'p'}, + {"authorization", required_argument, 0, 'a'}, + {"onredirect", required_argument, 0, 'f'}, + {"client-cert", required_argument, 0, 'J'}, + {"private-key", required_argument, 0, 'K'}, + {"useragent", required_argument, 0, 'A'}, + {"certificate", required_argument, 0, 'C'}, {0, 0, 0, 0} }; @@ -98,7 +405,7 @@ process_arguments (int argc, char **argv) usage ("\n"); while (1) { - c = getopt_long (argc, argv, "Vhv", longopts, &option); + c = getopt_long (argc, argv, "Vvht:c:w:A:H:I:a:p:u:f:C:J:K:S::", longopts, &option); if (c == -1 || c == EOF || c == 1) break; @@ -115,14 +422,155 @@ process_arguments (int argc, char **argv) case 'v': verbose++; break; + case 't': /* timeout period */ + if (!is_intnonneg (optarg)) + usage2 (_("Timeout interval must be a positive integer"), optarg); + else + socket_timeout = (int)strtol (optarg, NULL, 10); + break; + case 'c': /* critical time threshold */ + critical_thresholds = optarg; + break; + case 'w': /* warning time threshold */ + warning_thresholds = optarg; + break; + case 'H': /* virtual host */ + host_name = strdup (optarg); + break; + case 'I': /* internet address */ + server_address = strdup (optarg); + break; + case 'u': /* URL path */ + server_url = strdup (optarg); + break; + case 'p': /* Server port */ + if (!is_intnonneg (optarg)) + usage2 (_("Invalid port number, expecting a non-negative number"), optarg); + else { + if( strtol(optarg, NULL, 10) > MAX_PORT) + usage2 (_("Invalid port number, supplied port number is too big"), optarg); + server_port = (unsigned short)strtol(optarg, NULL, 10); + } + break; + case 'a': /* authorization info */ + strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1); + user_auth[MAX_INPUT_BUFFER - 1] = 0; + break; + case 'A': /* useragent */ + snprintf (user_agent, DEFAULT_BUFFER_SIZE, optarg); + break; + case 'C': /* Check SSL cert validity */ +#ifdef LIBCURL_FEATURE_SSL + /* TODO: C:, check age of certificate for backward compatible + * behaviour, but we would later add more check conditions */ + check_cert = TRUE; + goto enable_ssl; +#endif + case 'J': /* use client certificate */ +#ifdef LIBCURL_FEATURE_SSL + test_file(optarg); + client_cert = optarg; + goto enable_ssl; +#endif + case 'K': /* use client private key */ +#ifdef LIBCURL_FEATURE_SSL + test_file(optarg); + client_privkey = optarg; + goto enable_ssl; +#endif + case 'S': /* use SSL */ +#ifdef LIBCURL_FEATURE_SSL + enable_ssl: + use_ssl = TRUE; + /* ssl_version initialized to CURL_SSLVERSION_TLSv1_0 as a default. Only set if it's non-zero. This helps when we include multiple + parameters, like -S and -C combinations */ + ssl_version = CURL_SSLVERSION_TLSv1_0; + if (c=='S' && optarg != NULL) { + int got_plus = strchr(optarg, '+') != NULL; + + if (!strncmp (optarg, "1.2", 3)) + ssl_version = CURL_SSLVERSION_TLSv1_2; + else if (!strncmp (optarg, "1.1", 3)) + ssl_version = CURL_SSLVERSION_TLSv1_1; + else if (optarg[0] == '1') + ssl_version = CURL_SSLVERSION_TLSv1_0; + else if (optarg[0] == '3') + ssl_version = CURL_SSLVERSION_SSLv3; + else if (optarg[0] == '2') + ssl_version = CURL_SSLVERSION_SSLv2; + else + usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)")); + } + if (server_port == DEFAULT_HTTP_PORT) + server_port = DEFAULT_HTTPS_PORT; +#else + /* -C -J and -K fall through to here without SSL */ + usage4 (_("Invalid option - SSL is not available")); +#endif + break; + case SNI_OPTION: /* --sni is parsed, but ignored, the default is TRUE with libcurl */ + use_sni = TRUE; + break; + case 'f': /* onredirect */ + if (!strcmp (optarg, "ok")) + onredirect = STATE_OK; + else if (!strcmp (optarg, "warning")) + onredirect = STATE_WARNING; + else if (!strcmp (optarg, "critical")) + onredirect = STATE_CRITICAL; + else if (!strcmp (optarg, "unknown")) + onredirect = STATE_UNKNOWN; + else if (!strcmp (optarg, "follow")) + onredirect = STATE_DEPENDENT; + else usage2 (_("Invalid onredirect option"), optarg); + //~ if (!strcmp (optarg, "stickyport")) + //~ onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT; + //~ else if (!strcmp (optarg, "sticky")) + //~ onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST; + //~ else if (!strcmp (optarg, "follow")) + //~ onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE; + if (verbose >= 2) + printf(_("* Following redirects set to %s\n"), state_text(onredirect)); + break; case '?': /* print short usage statement if args not parsable */ usage5 (); break; } } + + c = optind; - return 0; + if (server_address == NULL && c < argc) + server_address = strdup (argv[c++]); + + if (host_name == NULL && c < argc) + host_name = strdup (argv[c++]); + + if (server_address == NULL) { + if (host_name == NULL) + usage4 (_("You must specify a server address or host name")); + else + server_address = strdup (host_name); + } + + set_thresholds(&thlds, warning_thresholds, critical_thresholds); + + if (critical_thresholds && thlds->critical->end>(double)socket_timeout) + socket_timeout = (int)thlds->critical->end + 1; + if (verbose >= 2) + printf ("* Socket timeout set to %d seconds\n", socket_timeout); + + //~ if (http_method == NULL) + //~ http_method = strdup ("GET"); + + if (client_cert && !client_privkey) + usage4 (_("If you use a client certificate you must also specify a private key file")); + + //~ if (virtual_port == 0) + //~ virtual_port = server_port; + + return TRUE; } void @@ -133,32 +581,91 @@ print_help (void) printf ("Copyright (c) 2017 Andreas Baumann \n"); printf (COPYRIGHT, copyright, email); - printf ("%s\n", _("This plugin tests the HTTP(S) service on the specified host.")); - printf ("%s\n", _("It makes use of libcurl to do so.")); + printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); + printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); + printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); + printf ("%s\n", _("certificate expiration times.")); + printf ("\n"); + printf ("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); + printf ("%s\n", _("as possible.")); printf ("\n\n"); print_usage(); + printf (_("NOTE: One or both of -H and -I must be specified")); printf ("\n"); printf (UT_HELP_VRSN); + printf (UT_EXTRA_OPTS); + + printf (" %s\n", "-H, --hostname=ADDRESS"); + printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); + printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); + printf (" %s\n", "-I, --IP-address=ADDRESS"); + printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); + printf (" %s\n", "-p, --port=INTEGER"); + printf (" %s", _("Port number (default: ")); + printf ("%d)\n", DEFAULT_HTTP_PORT); + +#ifdef LIBCURL_FEATURE_SSL + printf (" %s\n", "-S, --ssl=VERSION[+]"); + printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); + printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); + printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted.")); + printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl")); + printf (" %s\n", "--sni"); + printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); +#if LIBCURL_VERSION_NUM >= 0x071801 + printf (" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); + printf (" %s\n", _(" SNI only really works since TLSv1.0")); +#else + printf (" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); +#endif + printf (" %s\n", "-C, --certificate"); + printf (" %s\n", _("Check validity of certificate")); + printf (" %s\n", "-J, --client-cert=FILE"); + printf (" %s\n", _("Name of file that contains the client certificate (PEM format)")); + printf (" %s\n", _("to be used in establishing the SSL session")); + printf (" %s\n", "-K, --private-key=FILE"); + printf (" %s\n", _("Name of file containing the private key (PEM format)")); + printf (" %s\n", _("matching the client certificate")); +#endif + + printf (" %s\n", "-u, --url=PATH"); + printf (" %s\n", _("URL to GET or POST (default: /)")); + + printf (" %s\n", "-a, --authorization=AUTH_PAIR"); + printf (" %s\n", _("Username:password on sites with basic authentication")); + printf (" %s\n", "-A, --useragent=STRING"); + printf (" %s\n", _("String to be sent in http header as \"User Agent\"")); + printf (" %s\n", "-f, --onredirect="); + printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); + printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); + + printf (UT_WARN_CRIT); + + printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + printf (UT_VERBOSE); printf (UT_SUPPORT); - - printf ("%s\n", _("WARNING: check_curl is experimental. Please use")); - printf ("%s\n\n", _("check_http if you need a stable version.")); } void print_usage (void) { + printf ("%s\n", _("Usage:")); + printf (" %s -H | -I [-u ] [-p ]\n",progname); + printf (" [-J ] [-K ]\n"); + printf (" [-w ] [-c ] [-t ] [-a auth]\n"); + printf (" [-f ]\n"); + printf (" [-A string] [-S ] [-C]\n"); + printf (" [-v verbose]\n", progname); + printf ("\n"); printf ("%s\n", _("WARNING: check_curl is experimental. Please use")); printf ("%s\n\n", _("check_http if you need a stable version.")); - printf ("%s\n", _("Usage:")); - printf (" %s [-v verbose]\n", progname); } void @@ -166,3 +673,118 @@ print_curl_version (void) { printf( "%s\n", curl_version()); } + +int +curlhelp_initbuffer (curlhelp_curlbuf *buf) +{ + buf->bufsize = DEFAULT_BUFFER_SIZE; + buf->buflen = 0; + buf->buf = (char *)malloc ((size_t)buf->bufsize); + if (buf->buf == NULL) return -1; + return 0; +} + +int +curlhelp_buffer_callback (void *buffer, size_t size, size_t nmemb, void *stream) +{ + curlhelp_curlbuf *buf = (curlhelp_curlbuf *)stream; + + while (buf->bufsize < buf->buflen + size * nmemb + 1) { + buf->bufsize *= buf->bufsize * 2; + buf->buf = (char *)realloc (buf->buf, buf->bufsize); + if (buf->buf == NULL) return -1; + } + + memcpy (buf->buf + buf->buflen, buffer, size * nmemb); + buf->buflen += size * nmemb; + buf->buf[buf->buflen] = '\0'; + + return (int)(size * nmemb); +} + +void +curlhelp_freebuffer (curlhelp_curlbuf *buf) +{ + free (buf->buf); + buf->buf = NULL; +} + +/* TODO: when redirecting we get more than one HTTP header, make sure + * we parse the last one + */ +int +curlhelp_parse_statusline (char *buf, curlhelp_statusline *status_line) +{ + char *first_line_end; + char *p; + size_t first_line_len; + char *pp; + + first_line_end = strstr(buf, "\r\n"); + if (first_line_end == NULL) return -1; + + first_line_len = (size_t)(first_line_end - buf); + status_line->first_line = (char *)malloc (first_line_len + 1); + if (status_line->first_line == NULL) return -1; + memcpy (status_line->first_line, buf, first_line_len); + status_line->first_line[first_line_len] = '\0'; + + /* protocol and version: "HTTP/x.x" SP */ + + p = strtok(status_line->first_line, "/"); + if( p == NULL ) { free( status_line->first_line ); return -1; } + if( strcmp( p, "HTTP" ) != 0 ) { free( status_line->first_line ); return -1; } + + p = strtok( NULL, "." ); + if( p == NULL ) { free( status_line->first_line ); return -1; } + status_line->http_major = (int)strtol( p, &pp, 10 ); + if( *pp != '\0' ) { free( status_line->first_line ); return -1; } + + p = strtok( NULL, " " ); + if( p == NULL ) { free( status_line->first_line ); return -1; } + status_line->http_minor = (int)strtol( p, &pp, 10 ); + if( *pp != '\0' ) { free( status_line->first_line ); return -1; } + + /* status code: "404" or "404.1", then SP */ + + p = strtok( NULL, " ." ); + if( p == NULL ) { free( status_line->first_line ); return -1; } + if( strchr( p, '.' ) != NULL ) { + char *ppp; + ppp = strtok( p, "." ); + status_line->http_code = (int)strtol( ppp, &pp, 10 ); + if( *pp != '\0' ) { free( status_line->first_line ); return -1; } + + ppp = strtok( NULL, "" ); + status_line->http_subcode = (int)strtol( ppp, &pp, 10 ); + if( *pp != '\0' ) { free( status_line->first_line ); return -1; } + } else { + status_line->http_code = (int)strtol( p, &pp, 10 ); + status_line->http_subcode = -1; + if( *pp != '\0' ) { free( status_line->first_line ); return -1; } + } + + /* Human readable message: "Not Found" CRLF */ + + p = strtok( NULL, "" ); + if( p == NULL ) { free( status_line->first_line ); return -1; } + status_line->msg = p; + + return 0; +} + +void +curlhelp_free_statusline (curlhelp_statusline *status_line) +{ + free (status_line->first_line); +} + +void +remove_newlines (char *s) +{ + char *p; + + for (p = s; *p != '\0'; p++) + if (*p == '\r' || *p == '\n') + *p = ' '; +} -- cgit v0.10-9-g596f