check_http.c 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. /****************************************************************************
  2. *
  3. * Program: HTTP plugin for Nagios
  4. * License: GPL
  5. *
  6. * License Information:
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. *
  22. * $Id$
  23. *
  24. *****************************************************************************/
  25. #define PROGNAME "check_http"
  26. #define REVISION "$Revision$"
  27. #define COPYRIGHT "1999-2001"
  28. #define AUTHORS "Ethan Galstad/Karl DeBisschop"
  29. #define EMAIL "kdebisschop@users.sourceforge.net"
  30. #include "config.h"
  31. #include "common.h"
  32. #include "version.h"
  33. #include "netutils.h"
  34. #include "utils.h"
  35. #define SUMMARY "\
  36. This plugin tests the HTTP service on the specified host. It can test\n\
  37. normal (http) and secure (https) servers, follow redirects, search for\n\
  38. strings and regular expressions, check connection times, and report on\n\
  39. certificate expiration times.\n"
  40. #define OPTIONS "\
  41. \(-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
  42. [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
  43. [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
  44. [-s string] [-r <regex> | -R <case-insensitive regex>]\n\
  45. [-P string]"
  46. #define LONGOPTIONS "\
  47. -H, --hostname=ADDRESS\n\
  48. Host name argument for servers using host headers (virtual host)\n\
  49. -I, --IP-address=ADDRESS\n\
  50. IP address or name (use numeric address if possible to bypass DNS lookup).\n\
  51. -e, --expect=STRING\n\
  52. String to expect in first line of server response (default: %s)\n\
  53. -s, --string=STRING\n\
  54. String to expect in the content\n\
  55. -u, --url=PATH\n\
  56. URL to GET or POST (default: /)\n\
  57. -p, --port=INTEGER\n\
  58. Port number (default: %d)\n\
  59. -P, --post=STRING\n\
  60. URL encoded http POST data\n\
  61. -w, --warning=INTEGER\n\
  62. Response time to result in warning status (seconds)\n\
  63. -c, --critical=INTEGER\n\
  64. Response time to result in critical status (seconds)\n\
  65. -t, --timeout=INTEGER\n\
  66. Seconds before connection times out (default: %d)\n\
  67. -a, --authorization=AUTH_PAIR\n\
  68. Username:password on sites with basic authentication\n\
  69. -L, --link=URL\n\
  70. Wrap output in HTML link (obsoleted by urlize)\n\
  71. -f, --onredirect=<ok|warning|critical|follow>\n\
  72. How to handle redirected pages\n%s\
  73. -v, --verbose\n\
  74. Show details for command-line debugging (do not use with nagios server)\n\
  75. -h, --help\n\
  76. Print detailed help screen\n\
  77. -V, --version\n\
  78. Print version information\n"
  79. #ifdef HAVE_SSL
  80. #define SSLOPTIONS "\
  81. -S, --ssl\n\
  82. Connect via SSL\n\
  83. -C, --certificate=INTEGER\n\
  84. Minimum number of days a certificate has to be valid.\n\
  85. (when this option is used the url is not checked.)\n"
  86. #else
  87. #define SSLOPTIONS ""
  88. #endif
  89. #define DESCRIPTION "\
  90. This plugin will attempt to open an HTTP connection with the host. Successul\n\
  91. connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
  92. errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
  93. messages from the host result in STATE_WARNING return values. If you are\n\
  94. checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
  95. \(fully qualified domain name) as the [host_name] argument.\n"
  96. #define SSLDESCRIPTION "\
  97. This plugin can also check whether an SSL enabled web server is able to\n\
  98. serve content (optionally within a specified time) or whether the X509 \n\
  99. certificate is still valid for the specified number of days.\n\n\
  100. CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
  101. When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
  102. STATE_OK will be returned. When the server returns its content but exceeds\n\
  103. the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
  104. a STATE_CRITICAL will be returned.\n\n\
  105. CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
  106. When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
  107. STATE_OK is returned. When the certificate is still valid, but for less than\n\
  108. 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
  109. the certificate is expired.\n"
  110. #ifdef HAVE_SSL_H
  111. #include <rsa.h>
  112. #include <crypto.h>
  113. #include <x509.h>
  114. #include <pem.h>
  115. #include <ssl.h>
  116. #include <err.h>
  117. #include <rand.h>
  118. #endif
  119. #ifdef HAVE_OPENSSL_SSL_H
  120. #include <openssl/rsa.h>
  121. #include <openssl/crypto.h>
  122. #include <openssl/x509.h>
  123. #include <openssl/pem.h>
  124. #include <openssl/ssl.h>
  125. #include <openssl/err.h>
  126. #include <openssl/rand.h>
  127. #endif
  128. #ifdef HAVE_SSL
  129. int check_cert = FALSE;
  130. int days_till_exp;
  131. unsigned char *randbuff;
  132. SSL_CTX *ctx;
  133. SSL *ssl;
  134. X509 *server_cert;
  135. int connect_SSL (void);
  136. int check_certificate (X509 **);
  137. #endif
  138. #ifdef HAVE_REGEX_H
  139. #define REGS 2
  140. #define MAX_RE_SIZE 256
  141. #include <regex.h>
  142. regex_t preg;
  143. regmatch_t pmatch[REGS];
  144. char regexp[MAX_RE_SIZE];
  145. char errbuf[MAX_INPUT_BUFFER];
  146. int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
  147. int errcode;
  148. #endif
  149. #define server_type_check(server_type) \
  150. (strcmp (server_type, "https") ? FALSE : TRUE)
  151. #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
  152. #define MAX_IPV4_HOSTLENGTH 64
  153. #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
  154. #define URI_HTTP "%[HTPShtps]://"
  155. #define URI_HOST "%[a-zA-Z0-9.-]"
  156. #define URI_PORT ":%[0-9]"
  157. #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
  158. #define HTTP_PORT 80
  159. #define HTTPS_PORT 443
  160. #define HTTP_EXPECT "HTTP/1."
  161. #define HTTP_URL "/"
  162. time_t start_time, end_time;
  163. char timestamp[10] = "";
  164. int specify_port = FALSE;
  165. int server_port = HTTP_PORT;
  166. char server_port_text[6] = "";
  167. char server_type[6] = "http";
  168. char *server_address = NULL;
  169. char *host_name = NULL;
  170. char *server_url = NULL;
  171. int server_url_length = 0;
  172. char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
  173. char string_expect[MAX_INPUT_BUFFER] = "";
  174. int warning_time = 0;
  175. int check_warning_time = FALSE;
  176. int critical_time = 0;
  177. int check_critical_time = FALSE;
  178. char user_auth[MAX_INPUT_BUFFER] = "";
  179. int display_html = FALSE;
  180. int onredirect = STATE_OK;
  181. int use_ssl = FALSE;
  182. int verbose = FALSE;
  183. int sd;
  184. char *http_method = NULL;
  185. char *http_post_data = NULL;
  186. char buffer[MAX_INPUT_BUFFER];
  187. void print_usage (void);
  188. void print_help (void);
  189. int process_arguments (int, char **);
  190. int call_getopt (int, char **);
  191. static char *base64 (char *bin, int len);
  192. int check_http (void);
  193. int my_recv (void);
  194. int my_close (void);
  195. int
  196. main (int argc, char **argv)
  197. {
  198. int result = STATE_UNKNOWN;
  199. if (process_arguments (argc, argv) == ERROR)
  200. usage ("check_http: could not parse arguments\n");
  201. if (strstr (timestamp, ":")) {
  202. if (strstr (server_url, "?"))
  203. sprintf (server_url, "%s&%s", server_url, timestamp);
  204. else
  205. sprintf (server_url, "%s?%s", server_url, timestamp);
  206. }
  207. if (display_html == TRUE)
  208. printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
  209. host_name, server_port, server_url);
  210. /* initialize alarm signal handling, set socket timeout, start timer */
  211. signal (SIGALRM, socket_timeout_alarm_handler);
  212. alarm (socket_timeout);
  213. time (&start_time);
  214. #ifdef HAVE_SSL
  215. if (use_ssl && check_cert == TRUE) {
  216. if (connect_SSL () != OK)
  217. terminate (STATE_CRITICAL,
  218. "HTTP CRITICAL - Could not make SSL connection\n");
  219. if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
  220. result = check_certificate (&server_cert);
  221. X509_free (server_cert);
  222. }
  223. else {
  224. printf ("ERROR: Cannot retrieve server certificate.\n");
  225. result = STATE_CRITICAL;
  226. }
  227. SSL_shutdown (ssl);
  228. SSL_free (ssl);
  229. SSL_CTX_free (ctx);
  230. close (sd);
  231. }
  232. else {
  233. result = check_http ();
  234. }
  235. #else
  236. result = check_http ();
  237. #endif
  238. return result;
  239. }
  240. /* process command-line arguments */
  241. int
  242. process_arguments (int argc, char **argv)
  243. {
  244. int c, i = 1;
  245. char optchars[MAX_INPUT_BUFFER];
  246. #ifdef HAVE_GETOPT_H
  247. int option_index = 0;
  248. static struct option long_options[] = {
  249. STD_OPTS_LONG,
  250. {"link", no_argument, 0, 'L'},
  251. {"nohtml", no_argument, 0, 'n'},
  252. {"ssl", no_argument, 0, 'S'},
  253. {"verbose", no_argument, 0, 'v'},
  254. {"post", required_argument, 0, 'P'},
  255. {"IP-address", required_argument, 0, 'I'},
  256. {"string", required_argument, 0, 's'},
  257. {"regex", required_argument, 0, 'r'},
  258. {"ereg", required_argument, 0, 'r'},
  259. {"eregi", required_argument, 0, 'R'},
  260. {"onredirect", required_argument, 0, 'f'},
  261. {"certificate", required_argument, 0, 'C'},
  262. {0, 0, 0, 0}
  263. };
  264. #endif
  265. if (argc < 2)
  266. return ERROR;
  267. for (c = 1; c < argc; c++) {
  268. if (strcmp ("-to", argv[c]) == 0)
  269. strcpy (argv[c], "-t");
  270. if (strcmp ("-hn", argv[c]) == 0)
  271. strcpy (argv[c], "-H");
  272. if (strcmp ("-wt", argv[c]) == 0)
  273. strcpy (argv[c], "-w");
  274. if (strcmp ("-ct", argv[c]) == 0)
  275. strcpy (argv[c], "-c");
  276. if (strcmp ("-nohtml", argv[c]) == 0)
  277. strcpy (argv[c], "-n");
  278. }
  279. snprintf (optchars, MAX_INPUT_BUFFER, "%s%s", STD_OPTS,
  280. "P:I:a:e:p:s:R:r:u:f:C:nLS");
  281. while (1) {
  282. #ifdef HAVE_GETOPT_H
  283. c = getopt_long (argc, argv, optchars, long_options, &option_index);
  284. #else
  285. c = getopt (argc, argv, optchars);
  286. #endif
  287. if (c == -1 || c == EOF)
  288. break;
  289. switch (c) {
  290. case '?': /* usage */
  291. usage2 ("unknown argument", optarg);
  292. break;
  293. case 'h': /* help */
  294. print_help ();
  295. exit (STATE_OK);
  296. break;
  297. case 'V': /* version */
  298. print_revision (PROGNAME, REVISION);
  299. exit (STATE_OK);
  300. break;
  301. case 't': /* timeout period */
  302. if (!is_intnonneg (optarg))
  303. usage2 ("timeout interval must be a non-negative integer", optarg);
  304. socket_timeout = atoi (optarg);
  305. break;
  306. case 'c': /* critical time threshold */
  307. if (!is_intnonneg (optarg))
  308. usage2 ("invalid critical threshold", optarg);
  309. critical_time = atoi (optarg);
  310. check_critical_time = TRUE;
  311. break;
  312. case 'w': /* warning time threshold */
  313. if (!is_intnonneg (optarg))
  314. usage2 ("invalid warning threshold", optarg);
  315. warning_time = atoi (optarg);
  316. check_warning_time = TRUE;
  317. break;
  318. case 'L': /* show html link */
  319. display_html = TRUE;
  320. break;
  321. case 'n': /* do not show html link */
  322. display_html = FALSE;
  323. break;
  324. case 'S': /* use SSL */
  325. #ifndef HAVE_SSL
  326. usage ("check_http: invalid option - SSL is not available\n");
  327. #endif
  328. use_ssl = TRUE;
  329. if (specify_port == FALSE)
  330. server_port = HTTPS_PORT;
  331. break;
  332. case 'C': /* warning time threshold */
  333. #ifdef HAVE_SSL
  334. if (!is_intnonneg (optarg))
  335. usage2 ("invalid certificate expiration period", optarg);
  336. days_till_exp = atoi (optarg);
  337. check_cert = TRUE;
  338. #else
  339. usage ("check_http: invalid option - SSL is not available\n");
  340. #endif
  341. break;
  342. case 'f': /* onredirect */
  343. if (!strcmp (optarg, "follow"))
  344. onredirect = STATE_DEPENDENT;
  345. if (!strcmp (optarg, "unknown"))
  346. onredirect = STATE_UNKNOWN;
  347. if (!strcmp (optarg, "ok"))
  348. onredirect = STATE_OK;
  349. if (!strcmp (optarg, "warning"))
  350. onredirect = STATE_WARNING;
  351. if (!strcmp (optarg, "critical"))
  352. onredirect = STATE_CRITICAL;
  353. break;
  354. /* Note: H, I, and u must be malloc'd or will fail on redirects */
  355. case 'H': /* Host Name (virtual host) */
  356. host_name = strscpy (host_name, optarg);
  357. break;
  358. case 'I': /* Server IP-address */
  359. server_address = strscpy (server_address, optarg);
  360. break;
  361. case 'u': /* Host or server */
  362. server_url = strscpy (server_url, optarg);
  363. server_url_length = strlen (optarg);
  364. break;
  365. case 'p': /* Host or server */
  366. if (!is_intnonneg (optarg))
  367. usage2 ("invalid port number", optarg);
  368. server_port = atoi (optarg);
  369. specify_port = TRUE;
  370. break;
  371. case 'a': /* authorization info */
  372. strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
  373. user_auth[MAX_INPUT_BUFFER - 1] = 0;
  374. break;
  375. case 'P': /* HTTP POST data in URL encoded format */
  376. http_method = strscpy (http_method, "POST");
  377. http_post_data = strscpy (http_post_data, optarg);
  378. break;
  379. case 's': /* string or substring */
  380. strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
  381. string_expect[MAX_INPUT_BUFFER - 1] = 0;
  382. break;
  383. case 'e': /* string or substring */
  384. strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
  385. server_expect[MAX_INPUT_BUFFER - 1] = 0;
  386. break;
  387. case 'R': /* regex */
  388. #ifdef HAVE_REGEX_H
  389. cflags = REG_ICASE;
  390. #else
  391. usage ("check_http: call for regex which was not a compiled option\n");
  392. #endif
  393. case 'r': /* regex */
  394. #ifdef HAVE_REGEX_H
  395. cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
  396. strncpy (regexp, optarg, MAX_INPUT_BUFFER - 1);
  397. regexp[MAX_INPUT_BUFFER - 1] = 0;
  398. errcode = regcomp (&preg, regexp, cflags);
  399. if (errcode != 0) {
  400. regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
  401. printf ("Could Not Compile Regular Expression: %s", errbuf);
  402. return ERROR;
  403. }
  404. #else
  405. usage ("check_http: call for regex which was not a compiled option\n");
  406. #endif
  407. break;
  408. case 'v': /* verbose */
  409. verbose = TRUE;
  410. break;
  411. }
  412. }
  413. c = optind;
  414. if (server_address == NULL && host_name == NULL) {
  415. server_address = strscpy (NULL, argv[c]);
  416. host_name = strscpy (NULL, argv[c++]);
  417. }
  418. if (server_address == NULL && host_name == NULL)
  419. usage ("check_http: you must specify a host name\n");
  420. if (server_address == NULL)
  421. server_address = strscpy (NULL, host_name);
  422. if (host_name == NULL)
  423. host_name = strscpy (NULL, server_address);
  424. if (http_method == NULL)
  425. http_method = strscpy (http_method, "GET");
  426. if (server_url == NULL) {
  427. server_url = strscpy (NULL, "/");
  428. server_url_length = strlen(HTTP_URL);
  429. }
  430. return TRUE;
  431. }
  432. /* written by lauri alanko */
  433. static char *
  434. base64 (char *bin, int len)
  435. {
  436. char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
  437. int i = 0, j = 0;
  438. char BASE64_END = '=';
  439. char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  440. "abcdefghijklmnopqrstuvwxyz"
  441. "0123456789+/";
  442. while (j < len - 2) {
  443. buf[i++] = base64_table[bin[j] >> 2];
  444. buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
  445. buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
  446. buf[i++] = base64_table[bin[j + 2] & 63];
  447. j += 3;
  448. }
  449. switch (len - j) {
  450. case 1:
  451. buf[i++] = base64_table[bin[j] >> 2];
  452. buf[i++] = base64_table[(bin[j] & 3) << 4];
  453. buf[i++] = BASE64_END;
  454. buf[i++] = BASE64_END;
  455. break;
  456. case 2:
  457. buf[i++] = base64_table[bin[j] >> 2];
  458. buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
  459. buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
  460. buf[i++] = BASE64_END;
  461. break;
  462. case 0:
  463. break;
  464. }
  465. buf[i] = '\0';
  466. return buf;
  467. }
  468. int
  469. check_http (void)
  470. {
  471. char *msg = NULL;
  472. char *status_line = NULL;
  473. char *header = NULL;
  474. char *page = NULL;
  475. char *auth = NULL;
  476. int i = 0;
  477. size_t pagesize = 0;
  478. char *full_page = NULL;
  479. char *pos = NULL;
  480. /* try to connect to the host at the given port number */
  481. #ifdef HAVE_SSL
  482. if (use_ssl == TRUE) {
  483. if (connect_SSL () != OK) {
  484. msg = ssprintf (msg, "Unable to open TCP socket");
  485. terminate (STATE_CRITICAL, msg);
  486. }
  487. if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
  488. X509_free (server_cert);
  489. }
  490. else {
  491. printf ("ERROR: Cannot retrieve server certificate.\n");
  492. return STATE_CRITICAL;
  493. }
  494. sprintf (buffer, "%s %s HTTP/1.0\r\n", http_method, server_url);
  495. if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
  496. ERR_print_errors_fp (stderr);
  497. return STATE_CRITICAL;
  498. }
  499. /* optionally send the host header info (not clear if it's usable) */
  500. if (strcmp (host_name, "")) {
  501. sprintf (buffer, "Host: %s\r\n", host_name);
  502. if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
  503. ERR_print_errors_fp (stderr);
  504. return STATE_CRITICAL;
  505. }
  506. }
  507. /* send user agent */
  508. sprintf (buffer, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
  509. clean_revstring (REVISION), PACKAGE_VERSION);
  510. if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
  511. ERR_print_errors_fp (stderr);
  512. return STATE_CRITICAL;
  513. }
  514. /* optionally send the authentication info */
  515. if (strcmp (user_auth, "")) {
  516. auth = base64 (user_auth, strlen (user_auth));
  517. sprintf (buffer, "Authorization: Basic %s\r\n", auth);
  518. if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
  519. ERR_print_errors_fp (stderr);
  520. return STATE_CRITICAL;
  521. }
  522. }
  523. /* optionally send http POST data */
  524. if (http_post_data) {
  525. sprintf (buffer, "Content-Type: application/x-www-form-urlencoded\r\n");
  526. if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
  527. ERR_print_errors_fp (stderr);
  528. return STATE_CRITICAL;
  529. }
  530. sprintf (buffer, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
  531. if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
  532. ERR_print_errors_fp (stderr);
  533. return STATE_CRITICAL;
  534. }
  535. http_post_data = strscat (http_post_data, "\r\n");
  536. if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
  537. ERR_print_errors_fp (stderr);
  538. return STATE_CRITICAL;
  539. }
  540. }
  541. /* send a newline so the server knows we're done with the request */
  542. sprintf (buffer, "\r\n\r\n");
  543. if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
  544. ERR_print_errors_fp (stderr);
  545. return STATE_CRITICAL;
  546. }
  547. }
  548. else {
  549. #endif
  550. if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) {
  551. msg = ssprintf (msg, "Unable to open TCP socket");
  552. terminate (STATE_CRITICAL, msg);
  553. }
  554. sprintf (buffer, "%s %s HTTP/1.0\r\n", http_method, server_url);
  555. send (sd, buffer, strlen (buffer), 0);
  556. /* optionally send the host header info */
  557. if (strcmp (host_name, "")) {
  558. sprintf (buffer, "Host: %s\r\n", host_name);
  559. send (sd, buffer, strlen (buffer), 0);
  560. }
  561. /* send user agent */
  562. sprintf (buffer,
  563. "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
  564. clean_revstring (REVISION), PACKAGE_VERSION);
  565. send (sd, buffer, strlen (buffer), 0);
  566. /* optionally send the authentication info */
  567. if (strcmp (user_auth, "")) {
  568. auth = base64 (user_auth, strlen (user_auth));
  569. sprintf (buffer, "Authorization: Basic %s\r\n", auth);
  570. send (sd, buffer, strlen (buffer), 0);
  571. }
  572. /* optionally send http POST data */
  573. /* written by Chris Henesy <lurker@shadowtech.org> */
  574. if (http_post_data) {
  575. sprintf (buffer, "Content-Type: application/x-www-form-urlencoded\r\n");
  576. send (sd, buffer, strlen (buffer), 0);
  577. sprintf (buffer, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
  578. send (sd, buffer, strlen (buffer), 0);
  579. http_post_data = strscat (http_post_data, "\r\n");
  580. send (sd, http_post_data, strlen (http_post_data), 0);
  581. }
  582. /* send a newline so the server knows we're done with the request */
  583. sprintf (buffer, "\r\n\r\n");
  584. send (sd, buffer, strlen (buffer), 0);
  585. #ifdef HAVE_SSL
  586. }
  587. #endif
  588. /* fetch the page */
  589. pagesize = (size_t) 0;
  590. while ((i = my_recv ()) > 0) {
  591. full_page = strscat (full_page, buffer);
  592. pagesize += i;
  593. }
  594. if (i < 0)
  595. terminate (STATE_CRITICAL, "Error in recv()");
  596. /* return a CRITICAL status if we couldn't read any data */
  597. if (pagesize == (size_t) 0)
  598. terminate (STATE_CRITICAL, "No data received %s", timestamp);
  599. /* close the connection */
  600. my_close ();
  601. /* reset the alarm */
  602. alarm (0);
  603. /* leave full_page untouched so we can free it later */
  604. page = full_page;
  605. if (verbose)
  606. printf ("Page is %d characters\n", pagesize);
  607. /* find status line and null-terminate it */
  608. status_line = page;
  609. page += (size_t) strcspn (page, "\r\n");
  610. pos = page;
  611. page += (size_t) strspn (page, "\r\n");
  612. status_line[pos - status_line] = 0;
  613. strip (status_line);
  614. if (verbose)
  615. printf ("STATUS: %s\n", status_line);
  616. /* find header info and null terminate it */
  617. header = page;
  618. while (strcspn (page, "\r\n") > 0) {
  619. page += (size_t) strcspn (page, "\r\n");
  620. pos = page;
  621. if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
  622. (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
  623. page += (size_t) 2;
  624. else
  625. page += (size_t) 1;
  626. }
  627. page += (size_t) strspn (page, "\r\n");
  628. header[pos - header] = 0;
  629. if (verbose)
  630. printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
  631. /* make sure the status line matches the response we are looking for */
  632. if (!strstr (status_line, server_expect)) {
  633. if (server_port == HTTP_PORT)
  634. msg = ssprintf (msg, "Invalid HTTP response received from host\n");
  635. else
  636. msg = ssprintf (msg,
  637. "Invalid HTTP response received from host on port %d\n",
  638. server_port);
  639. terminate (STATE_CRITICAL, msg);
  640. }
  641. /* check the return code */
  642. /* server errors result in a critical state */
  643. if (strstr (status_line, "500") ||
  644. strstr (status_line, "501") ||
  645. strstr (status_line, "502") ||
  646. strstr (status_line, "503")) {
  647. msg = ssprintf (msg, "HTTP CRITICAL: %s\n", status_line);
  648. terminate (STATE_CRITICAL, msg);
  649. }
  650. /* client errors result in a warning state */
  651. if (strstr (status_line, "400") ||
  652. strstr (status_line, "401") ||
  653. strstr (status_line, "402") ||
  654. strstr (status_line, "403") ||
  655. strstr (status_line, "404")) {
  656. msg = ssprintf (msg, "HTTP WARNING: %s\n", status_line);
  657. terminate (STATE_WARNING, msg);
  658. }
  659. /* check redirected page if specified */
  660. if (strstr (status_line, "300") ||
  661. strstr (status_line, "301") ||
  662. strstr (status_line, "302") ||
  663. strstr (status_line, "303") ||
  664. strstr (status_line, "304")) {
  665. if (onredirect == STATE_DEPENDENT) {
  666. pos = header;
  667. while (pos) {
  668. server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
  669. if (server_address == NULL)
  670. terminate (STATE_UNKNOWN,
  671. "HTTP UNKNOWN: could not allocate server_address");
  672. if (strspn (pos, "\r\n") > server_url_length) {
  673. server_url = realloc (server_url, strspn (pos, "\r\n"));
  674. if (server_url == NULL)
  675. terminate (STATE_UNKNOWN,
  676. "HTTP UNKNOWN: could not allocate server_url");
  677. server_url_length = strspn (pos, "\r\n");
  678. }
  679. if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
  680. host_name = strscpy (host_name, server_address);
  681. use_ssl = server_type_check (server_type);
  682. server_port = atoi (server_port_text);
  683. check_http ();
  684. }
  685. else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3) {
  686. host_name = strscpy (host_name, server_address);
  687. use_ssl = server_type_check (server_type);
  688. server_port = server_port_check (use_ssl);
  689. check_http ();
  690. }
  691. else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
  692. host_name = strscpy (host_name, server_address);
  693. strcpy (server_url, "/");
  694. use_ssl = server_type_check (server_type);
  695. server_port = atoi (server_port_text);
  696. check_http ();
  697. }
  698. else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
  699. host_name = strscpy (host_name, server_address);
  700. strcpy (server_url, "/");
  701. use_ssl = server_type_check (server_type);
  702. server_port = server_port_check (use_ssl);
  703. check_http ();
  704. }
  705. else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
  706. check_http ();
  707. }
  708. pos += (size_t) strcspn (pos, "\r\n");
  709. pos += (size_t) strspn (pos, "\r\n");
  710. } /* end while (pos) */
  711. printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
  712. status_line, (display_html ? "</A>" : ""));
  713. exit (STATE_UNKNOWN);
  714. } /* end if (onredirect == STATE_DEPENDENT) */
  715. else if (onredirect == STATE_UNKNOWN)
  716. printf ("HTTP UNKNOWN");
  717. else if (onredirect == STATE_OK)
  718. printf ("HTTP ok");
  719. else if (onredirect == STATE_WARNING)
  720. printf ("HTTP WARNING");
  721. else if (onredirect == STATE_CRITICAL)
  722. printf ("HTTP CRITICAL");
  723. time (&end_time);
  724. msg = ssprintf (msg, ": %s - %d second response time %s%s\n",
  725. status_line, (int) (end_time - start_time),
  726. timestamp, (display_html ? "</A>" : ""));
  727. terminate (onredirect, msg);
  728. } /* end if (strstr (status_line, "30[0-4]") */
  729. /* check elapsed time */
  730. time (&end_time);
  731. msg = ssprintf (msg, "HTTP problem: %s - %d second response time %s%s\n",
  732. status_line, (int) (end_time - start_time),
  733. timestamp, (display_html ? "</A>" : ""));
  734. if (check_critical_time == TRUE && (end_time - start_time) > critical_time)
  735. terminate (STATE_CRITICAL, msg);
  736. if (check_warning_time == TRUE && (end_time - start_time) > warning_time)
  737. terminate (STATE_WARNING, msg);
  738. /* Page and Header content checks go here */
  739. /* these checks should be last */
  740. if (strlen (string_expect)) {
  741. if (strstr (page, string_expect)) {
  742. printf ("HTTP ok: %s - %d second response time %s%s\n",
  743. status_line, (int) (end_time - start_time),
  744. timestamp, (display_html ? "</A>" : ""));
  745. exit (STATE_OK);
  746. }
  747. else {
  748. printf ("HTTP CRITICAL: string not found%s\n",
  749. (display_html ? "</A>" : ""));
  750. exit (STATE_CRITICAL);
  751. }
  752. }
  753. #ifdef HAVE_REGEX_H
  754. if (strlen (regexp)) {
  755. errcode = regexec (&preg, page, REGS, pmatch, 0);
  756. if (errcode == 0) {
  757. printf ("HTTP ok: %s - %d second response time %s%s\n",
  758. status_line, (int) (end_time - start_time),
  759. timestamp, (display_html ? "</A>" : ""));
  760. exit (STATE_OK);
  761. }
  762. else {
  763. if (errcode == REG_NOMATCH) {
  764. printf ("HTTP CRITICAL: pattern not found%s\n",
  765. (display_html ? "</A>" : ""));
  766. exit (STATE_CRITICAL);
  767. }
  768. else {
  769. regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
  770. printf ("Execute Error: %s\n", errbuf);
  771. exit (STATE_CRITICAL);
  772. }
  773. }
  774. }
  775. #endif
  776. /* We only get here if all tests have been passed */
  777. msg = ssprintf (msg, "HTTP ok: %s - %d second response time %s%s\n",
  778. status_line, (int) (end_time - start_time),
  779. timestamp, (display_html ? "</A>" : ""));
  780. terminate (STATE_OK, msg);
  781. return STATE_UNKNOWN;
  782. }
  783. #ifdef HAVE_SSL
  784. int
  785. connect_SSL (void)
  786. {
  787. SSL_METHOD *meth;
  788. randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
  789. RAND_seed (randbuff, strlen (randbuff));
  790. /* Initialize SSL context */
  791. SSLeay_add_ssl_algorithms ();
  792. meth = SSLv23_client_method ();
  793. SSL_load_error_strings ();
  794. if ((ctx = SSL_CTX_new (meth)) == NULL) {
  795. printf ("ERROR: Cannot create SSL context.\n");
  796. return STATE_CRITICAL;
  797. }
  798. /* Initialize alarm signal handling */
  799. signal (SIGALRM, socket_timeout_alarm_handler);
  800. /* Set socket timeout */
  801. alarm (socket_timeout);
  802. /* Save start time */
  803. time (&start_time);
  804. /* Make TCP connection */
  805. if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
  806. /* Do the SSL handshake */
  807. if ((ssl = SSL_new (ctx)) != NULL) {
  808. SSL_set_cipher_list(ssl, "ALL");
  809. SSL_set_fd (ssl, sd);
  810. if (SSL_connect (ssl) != -1)
  811. return OK;
  812. ERR_print_errors_fp (stderr);
  813. }
  814. else {
  815. printf ("ERROR: Cannot initiate SSL handshake.\n");
  816. }
  817. SSL_free (ssl);
  818. }
  819. SSL_CTX_free (ctx);
  820. close (sd);
  821. return STATE_CRITICAL;
  822. }
  823. #endif
  824. #ifdef HAVE_SSL
  825. int
  826. check_certificate (X509 ** certificate)
  827. {
  828. ASN1_STRING *tm;
  829. int offset;
  830. struct tm stamp;
  831. int days_left;
  832. /* int result = STATE_OK; */
  833. /* char timestamp[14]; */
  834. /* Retrieve timestamp of certificate */
  835. tm = X509_get_notAfter (*certificate);
  836. /* Generate tm structure to process timestamp */
  837. if (tm->type == V_ASN1_UTCTIME) {
  838. if (tm->length < 10) {
  839. printf ("ERROR: Wrong time format in certificate.\n");
  840. return STATE_CRITICAL;
  841. }
  842. else {
  843. stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
  844. if (stamp.tm_year < 50)
  845. stamp.tm_year += 100;
  846. offset = 0;
  847. }
  848. }
  849. else {
  850. if (tm->length < 12) {
  851. printf ("ERROR: Wrong time format in certificate.\n");
  852. return STATE_CRITICAL;
  853. }
  854. else {
  855. stamp.tm_year =
  856. (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
  857. (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
  858. stamp.tm_year -= 1900;
  859. offset = 2;
  860. }
  861. }
  862. stamp.tm_mon =
  863. (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
  864. stamp.tm_mday =
  865. (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
  866. stamp.tm_hour =
  867. (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
  868. stamp.tm_min =
  869. (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
  870. stamp.tm_sec = 0;
  871. stamp.tm_isdst = -1;
  872. days_left = (mktime (&stamp) - time (NULL)) / 86400;
  873. sprintf
  874. (timestamp, "%02d/%02d/%04d %02d:%02d",
  875. stamp.tm_mon + 1,
  876. stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
  877. if (days_left > 0 && days_left <= days_till_exp) {
  878. printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
  879. return STATE_WARNING;
  880. }
  881. if (days_left < 0) {
  882. printf ("Certificate expired on %s.\n", timestamp);
  883. return STATE_CRITICAL;
  884. }
  885. if (days_left == 0) {
  886. printf ("Certificate expires today (%s).\n", timestamp);
  887. return STATE_WARNING;
  888. }
  889. printf ("Certificate will expire on %s.\n", timestamp);
  890. return STATE_OK;
  891. }
  892. #endif
  893. int
  894. my_recv (void)
  895. {
  896. int i;
  897. #ifdef HAVE_SSL
  898. if (use_ssl) {
  899. i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
  900. }
  901. else {
  902. i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
  903. }
  904. #else
  905. i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
  906. #endif
  907. return i;
  908. }
  909. int
  910. my_close (void)
  911. {
  912. #ifdef HAVE_SSL
  913. if (use_ssl == TRUE) {
  914. SSL_shutdown (ssl);
  915. SSL_free (ssl);
  916. SSL_CTX_free (ctx);
  917. return 0;
  918. }
  919. else {
  920. #endif
  921. return close (sd);
  922. #ifdef HAVE_SSL
  923. }
  924. #endif
  925. }
  926. void
  927. print_help (void)
  928. {
  929. print_revision (PROGNAME, REVISION);
  930. printf
  931. ("Copyright (c) %s %s <%s>\n\n%s\n",
  932. COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
  933. print_usage ();
  934. printf ("NOTE: One or both of -H and -I must be specified\n");
  935. printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
  936. DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
  937. #ifdef HAVE_SSL
  938. printf (SSLDESCRIPTION);
  939. #endif
  940. }
  941. void
  942. print_usage (void)
  943. {
  944. printf ("Usage:\n" " %s %s\n"
  945. #ifdef HAVE_GETOPT_H
  946. " %s (-h | --help) for detailed help\n"
  947. " %s (-V | --version) for version information\n",
  948. #else
  949. " %s -h for detailed help\n"
  950. " %s -V for version information\n",
  951. #endif
  952. PROGNAME, OPTIONS, PROGNAME, PROGNAME);
  953. }