1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package it.jnrpe.plugin;
17
18 import it.jnrpe.ICommandLine;
19 import it.jnrpe.Status;
20 import it.jnrpe.plugin.utils.HttpUtils;
21 import it.jnrpe.plugin.utils.Utils;
22 import it.jnrpe.plugins.Metric;
23 import it.jnrpe.plugins.MetricGatheringException;
24 import it.jnrpe.plugins.PluginBase;
25 import it.jnrpe.utils.BadThresholdException;
26 import it.jnrpe.utils.thresholds.ThresholdsEvaluatorBuilder;
27
28 import java.io.UnsupportedEncodingException;
29 import java.math.BigDecimal;
30 import java.net.HttpURLConnection;
31 import java.net.MalformedURLException;
32 import java.net.URL;
33 import java.net.URLEncoder;
34 import java.security.SecureRandom;
35 import java.security.cert.Certificate;
36 import java.security.cert.CertificateException;
37 import java.security.cert.X509Certificate;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.Date;
41 import java.util.List;
42 import java.util.Properties;
43 import java.util.regex.Pattern;
44
45 import javax.net.ssl.HostnameVerifier;
46 import javax.net.ssl.HttpsURLConnection;
47 import javax.net.ssl.KeyManager;
48 import javax.net.ssl.SSLContext;
49 import javax.net.ssl.SSLSession;
50 import javax.net.ssl.TrustManager;
51 import javax.net.ssl.X509TrustManager;
52
53 import org.apache.commons.codec.binary.Base64;
54
55
56
57
58
59
60
61
62
63
64 public class CheckHttp extends PluginBase {
65
66
67
68
69 private static final String DEFAULT_PORT = "80";
70
71
72
73
74 private static final String DEFAULT_SSL_PORT = "443";
75
76
77
78
79 private static final int DEFAULT_TIMEOUT = 30;
80
81
82
83
84 private static final String DEFAULT_PATH = "/";
85
86
87
88
89 private static final String DEFAULT_METHOD = "GET";
90
91
92
93
94 private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 "
95 + "(KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36";
96
97 private static final String CHARSET = "UTF-8";
98
99
100
101
102
103
104
105
106
107
108
109
110 @Override
111 public final void configureThresholdEvaluatorBuilder(
112 final ThresholdsEvaluatorBuilder thrb, final ICommandLine cl)
113 throws BadThresholdException {
114 thrb.withLegacyThreshold("time", null, cl.getOptionValue("warning"),
115 cl.getOptionValue("critical"));
116 if (cl.hasOption("regex")) {
117 if (cl.hasOption("invert-regex")) {
118
119 thrb.withLegacyThreshold("invert-regex", null, null, "1");
120 } else {
121
122 thrb.withLegacyThreshold("regex", null, null, "0");
123 }
124 }
125 if (cl.hasOption("expect")) {
126
127 thrb.withLegacyThreshold("expect", null, "0", null);
128 }
129 if (cl.hasOption("string")) {
130
131 thrb.withLegacyThreshold("string", null, "0", null);
132 }
133 if (cl.hasOption("onredirect")) {
134 String redirect = cl.getOptionValue("onredirect").toUpperCase();
135 if ("OK".equals(redirect)) {
136 thrb.withLegacyThreshold("onredirect", "1:", null, null);
137 } else if ("CRITICAL".equals(redirect)) {
138 thrb.withLegacyThreshold("onredirect", null, null, "1:");
139 } else if ("WARNING".equals(redirect)) {
140 thrb.withLegacyThreshold("onredirect", null, "1:", null);
141 }
142 }
143 if (cl.hasOption("certificate")) {
144 String ok = cl.getOptionValue("certificate");
145 thrb.withLegacyThreshold("certificate", ok, null, null);
146 }
147 }
148
149
150
151
152
153
154
155
156
157
158 @Override
159 public final Collection<Metric> gatherMetrics(final ICommandLine cl)
160 throws MetricGatheringException {
161 List<Metric> metrics = new ArrayList<Metric>();
162 String hostname = cl.getOptionValue("hostname");
163 if (hostname == null) {
164 throw new MetricGatheringException("No hostname specified.",
165 Status.WARNING, null);
166 }
167
168 String port = cl.getOptionValue("port", DEFAULT_PORT);
169 String path = cl.getOptionValue("url", DEFAULT_PATH);
170 String method = cl.getOptionValue("method", DEFAULT_METHOD);
171
172 int timeout = DEFAULT_TIMEOUT;
173 if (cl.hasOption("post")) {
174 method = "POST";
175 }
176 boolean ssl = false;
177 if (cl.hasOption("ssl") || cl.getOptionValue("certificate") != null) {
178 port = cl.getOptionValue("ssl", DEFAULT_SSL_PORT);
179 ssl = true;
180 }
181
182 if (cl.hasOption("timeout")) {
183 try {
184 timeout = Integer.parseInt(cl.getOptionValue("timeout"));
185 } catch (NumberFormatException e) {
186 throw new MetricGatheringException(
187 "Invalid numeric value for timeout.", Status.CRITICAL,
188 e);
189 }
190 }
191 if (!path.startsWith("/")) {
192 path = "/" + path;
193 }
194 if (hostname.endsWith("/")) {
195 hostname = hostname.substring(0, hostname.length() - 1);
196 }
197
198 long then = System.currentTimeMillis();
199
200 String response = getHttpResponse(cl, hostname, port, method, path,
201 timeout, ssl, metrics);
202 int elapsed = (int) Utils.milliToSec(System.currentTimeMillis() - then);
203 if (response != null) {
204 metrics.addAll(analyzeResponse(cl, response, elapsed));
205 }
206 return metrics;
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 private String getHttpResponse(final ICommandLine cl,
233 final String hostname, final String port, final String method,
234 final String path, final int timeout, final boolean ssl,
235 final List<Metric> metrics) throws MetricGatheringException {
236 Properties props = null;
237 try {
238 props = getRequestProperties(cl, method);
239 } catch (UnsupportedEncodingException e) {
240 throw new MetricGatheringException("Error occurred: "
241 + e.getMessage(), Status.CRITICAL, e);
242 }
243 String response = null;
244 String redirect = cl.getOptionValue("onredirect");
245 boolean ignoreBody = false;
246 try {
247 String data = null;
248 if ("POST".equals(method)) {
249 data = getPostData(cl);
250 }
251
252 if (cl.hasOption("no-body")) {
253 ignoreBody = true;
254 }
255 String urlString = hostname + ":" + port + path;
256 if (cl.hasOption("authorization")) {
257 urlString = cl.getOptionValue("authorization") + "@"
258 + urlString;
259 } else if (cl.hasOption("proxy-authorization")) {
260 urlString = cl.getOptionValue("proxy-authorization") + "@"
261 + urlString;
262 }
263 if (ssl) {
264 urlString = "https://" + urlString;
265 } else {
266 urlString = "http://" + urlString;
267 }
268 URL url = new URL(urlString);
269 if (cl.getOptionValue("certificate") != null) {
270 checkCertificateExpiryDate(url, metrics);
271 } else if (redirect != null) {
272 response = checkRedirectResponse(url, method, timeout, props,
273 data, redirect, ignoreBody, metrics);
274 } else {
275 try {
276 if ("GET".equals(method)) {
277 response = HttpUtils.doGET(url, props, timeout, true,
278 ignoreBody);
279 } else if ("POST".equals(method)) {
280 response = HttpUtils.doPOST(url, props, null, data,
281 true, ignoreBody);
282 } else if ("HEAD".equals(method)) {
283 response = HttpUtils.doHEAD(url, props, timeout, true,
284 ignoreBody);
285 }
286
287
288 } catch (MalformedURLException e) {
289 log.error("Bad url", e);
290 throw new MetricGatheringException("Bad url string : "
291 + urlString, Status.CRITICAL, e);
292 }
293 }
294
295 } catch (Exception e) {
296 log.error("Exception: " + e.getMessage(), e);
297 throw new MetricGatheringException(e.getClass().getName() + ": "
298 + e.getMessage(), Status.CRITICAL, e);
299 }
300 return response;
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326 private String checkRedirectResponse(final URL url, final String method,
327 final Integer timeout, final Properties props,
328 final String postData, final String redirect,
329 final boolean ignoreBody, final List<Metric> metrics)
330 throws Exception {
331
332
333 String response = null;
334 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
335 conn.setRequestMethod(method);
336 HttpUtils.setRequestProperties(props, conn, timeout);
337 String initialUrl = conn.getURL() + "";
338 String redirectedUrl = null;
339 if (method.equals("POST")) {
340 HttpUtils.sendPostData(conn, postData);
341 }
342 response = HttpUtils.parseHttpResponse(conn, false, ignoreBody);
343 redirectedUrl = conn.getURL() + "";
344
345 if (!redirectedUrl.equals(initialUrl)) {
346 Metric metric = new Metric("onredirect", "", new BigDecimal(1),
347 null, null);
348 metrics.add(metric);
349 }
350 return response;
351 }
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366 private List<Metric> analyzeResponse(final ICommandLine opt,
367 final String response, final int elapsed)
368 throws MetricGatheringException {
369 List<Metric> metrics = new ArrayList<Metric>();
370 metrics.add(new Metric("time", "", new BigDecimal(elapsed), null, null));
371
372 if (opt.getOptionValue("certificate") == null
373 && !opt.hasOption("certificate")) {
374 if (opt.getOptionValue("string") != null
375 && !opt.getOptionValue("string").equals("")) {
376 boolean found = false;
377 String string = opt.getOptionValue("string");
378 found = response.contains(string);
379 metrics.add(new Metric("string", "", new BigDecimal(Utils
380 .getIntValue(found)), null, null));
381 }
382 if (opt.getOptionValue("expect") != null) {
383 int count = 0;
384 String[] values = opt.getOptionValue("expect").split(",");
385 for (String value : values) {
386 if (response.contains(value)) {
387 count++;
388 }
389 }
390 metrics.add(new Metric("expect", "" + count + " times. ",
391 new BigDecimal(count), null, null));
392 }
393 if (opt.getOptionValue("regex") != null) {
394 String regex = opt.getOptionValue("regex");
395 Pattern p = null;
396 int flags = 0;
397 if (opt.hasOption("eregi")) {
398 flags = Pattern.CASE_INSENSITIVE;
399 }
400 if (opt.hasOption("linespan")) {
401 flags = flags | Pattern.MULTILINE;
402 }
403 p = Pattern.compile(regex, flags);
404 boolean found = p.matcher(response).find();
405 if (opt.hasOption("invert-regex")) {
406 metrics.add(new Metric("invert-regex", "" + found,
407 new BigDecimal(Utils.getIntValue(found)), null,
408 null));
409 } else {
410 metrics.add(new Metric("regex", "" + found, new BigDecimal(
411 Utils.getIntValue(found)), null, null));
412 }
413 }
414 }
415 return metrics;
416 }
417
418
419
420
421
422
423
424
425
426
427
428 private Properties getRequestProperties(final ICommandLine cl,
429 final String method) throws UnsupportedEncodingException {
430 Properties props = new Properties();
431 if (cl.getOptionValue("useragent") != null) {
432 props.put("User-Agent", cl.getOptionValue("useragent"));
433 } else {
434 props.put("User-Agent", DEFAULT_USER_AGENT);
435 }
436 if (cl.getOptionValue("content-type") != null
437 && method.toUpperCase().equals("POST")) {
438 props.put("Content-Type", cl.getOptionValue("content-type"));
439 }
440 if (cl.getOptionValues("header") != null) {
441 List headers = cl.getOptionValues("header");
442 for (Object obj : headers) {
443 String header = (String) obj;
444 String key = header.split(":")[0].trim();
445 String value = header.split(":")[1].trim();
446 props.put(key, value);
447 }
448 }
449 String auth = null;
450 String encoded = null;
451 if (cl.hasOption("authorization")) {
452 encoded = Base64.encodeBase64String(cl.getOptionValue(
453 "authorization").getBytes(CHARSET));
454 auth = "Authorization";
455 } else if (cl.hasOption("proxy-authorization")) {
456 encoded = Base64.encodeBase64String(cl.getOptionValue(
457 "proxy-authorization").getBytes(CHARSET));
458 auth = "Proxy-Authorization";
459 }
460 if (auth != null && encoded != null) {
461 props.put(auth, "Basic " + encoded);
462 }
463 return props;
464 }
465
466
467
468
469
470
471
472
473
474
475 private String getPostData(final ICommandLine cl) throws Exception {
476
477 StringBuilder encoded = new StringBuilder();
478 String data = cl.getOptionValue("post");
479 if (data == null) {
480 return null;
481 }
482 String[] values = data.split("&");
483 for (String value : values) {
484 String[] splitted = value.split("=");
485 String key = splitted[0];
486 String val = "";
487 if (splitted.length > 1) {
488 val = splitted[1];
489 }
490 if (encoded.length() != 0) {
491 encoded.append('&');
492 }
493 encoded.append(key).append('=')
494 .append(URLEncoder.encode(val, "UTF-8"));
495
496 }
497
498
499
500 return encoded.toString();
501 }
502
503
504
505
506
507
508 @Override
509 protected String getPluginName() {
510 return "CHECK_HTTP";
511 }
512
513
514 private void checkCertificateExpiryDate(URL url, List<Metric> metrics)
515 throws Exception {
516 SSLContext ctx = SSLContext.getInstance("TLS");
517 ctx.init(new KeyManager[0],
518 new TrustManager[] { new DefaultTrustManager() },
519 new SecureRandom());
520 SSLContext.setDefault(ctx);
521 HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
522 conn.setHostnameVerifier(new HostnameVerifier() {
523 public boolean verify(String arg0, SSLSession arg1) {
524 return true;
525 }
526 });
527 List<Date> expiryDates = new ArrayList<Date>();
528 conn.getResponseCode();
529 Certificate[] certs = conn.getServerCertificates();
530 for (Certificate cert : certs) {
531 X509Certificate x509 = (X509Certificate) cert;
532 Date expiry = x509.getNotAfter();
533 expiryDates.add(expiry);
534 }
535
536 conn.disconnect();
537 Date today = new Date();
538 for (Date date : expiryDates) {
539 int diffInDays = (int) ((date.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
540 metrics.add(new Metric("certificate", "",
541 new BigDecimal(diffInDays), null, null));
542 }
543 }
544
545
546
547
548 private static class DefaultTrustManager implements X509TrustManager {
549
550
551
552
553
554
555
556
557 public void checkClientTrusted(X509Certificate[] arg0, String arg1)
558 throws CertificateException {
559
560 }
561
562
563
564
565
566
567
568
569 public void checkServerTrusted(X509Certificate[] arg0, String arg1)
570 throws CertificateException {
571
572 }
573
574
575
576
577
578
579 public X509Certificate[] getAcceptedIssuers() {
580 return null;
581 }
582 }
583
584 }