1 /******************************************************************************
3 * Nagios check_smtp plugin
6 * Copyright (c) 1999-2006 nagios-plugins team
8 * Last Modified: $Date$
12 * This file contains the check_smtp plugin
14 * This plugin will attempt to open an SMTP connection with the host.
17 * License Information:
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 ******************************************************************************/
38 const char *progname
= "check_smtp";
39 const char *revision
= "$Revision$";
40 const char *copyright
= "2000-2006";
41 const char *email
= "nagiosplug-devel@lists.sourceforge.net";
48 int check_cert
= FALSE
;
50 # define my_recv(buf, len) ((use_ssl && ssl_established) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
51 # define my_send(buf, len) ((use_ssl && ssl_established) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
52 #else /* ifndef HAVE_SSL */
53 # define my_recv(buf, len) read(sd, buf, len)
54 # define my_send(buf, len) send(sd, buf, len, 0)
60 #define SMTP_EXPECT "220"
61 #define SMTP_HELO "HELO "
62 #define SMTP_EHLO "EHLO "
63 #define SMTP_QUIT "QUIT\r\n"
64 #define SMTP_STARTTLS "STARTTLS\r\n"
65 #define SMTP_AUTH_LOGIN "AUTH LOGIN\r\n"
67 #ifndef HOST_MAX_BYTES
68 #define HOST_MAX_BYTES 255
71 #define EHLO_SUPPORTS_STARTTLS 1
73 int process_arguments (int, char **);
74 int validate_arguments (void);
75 void print_help (void);
76 void print_usage (void);
80 char regex_expect
[MAX_INPUT_BUFFER
] = "";
82 regmatch_t pmatch
[10];
83 char timestamp
[20] = "";
84 char errbuf
[MAX_INPUT_BUFFER
];
85 int cflags
= REG_EXTENDED
| REG_NOSUB
| REG_NEWLINE
;
89 int server_port
= SMTP_PORT
;
90 char *server_address
= NULL
;
91 char *server_expect
= NULL
;
92 int smtp_use_dummycmd
= 0;
93 char *mail_command
= NULL
;
94 char *from_arg
= NULL
;
99 char **commands
= NULL
;
100 char **responses
= NULL
;
101 char *authtype
= NULL
;
102 char *authuser
= NULL
;
103 char *authpass
= NULL
;
104 int warning_time
= 0;
105 int check_warning_time
= FALSE
;
106 int critical_time
= 0;
107 int check_critical_time
= FALSE
;
110 short use_ehlo
= FALSE
;
111 short ssl_established
= 0;
112 char *localhostname
= NULL
;
114 char buffer
[MAX_INPUT_BUFFER
];
121 /* written by lauri alanko */
123 base64 (const char *bin
, size_t len
)
126 char *buf
= (char *) malloc ((len
+ 2) / 3 * 4 + 1);
129 char BASE64_END
= '=';
130 char base64_table
[64];
131 strncpy (base64_table
, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
133 while (j
< len
- 2) {
134 buf
[i
++] = base64_table
[bin
[j
] >> 2];
135 buf
[i
++] = base64_table
[((bin
[j
] & 3) << 4) | (bin
[j
+ 1] >> 4)];
136 buf
[i
++] = base64_table
[((bin
[j
+ 1] & 15) << 2) | (bin
[j
+ 2] >> 6)];
137 buf
[i
++] = base64_table
[bin
[j
+ 2] & 63];
143 buf
[i
++] = base64_table
[bin
[j
] >> 2];
144 buf
[i
++] = base64_table
[(bin
[j
] & 3) << 4];
145 buf
[i
++] = BASE64_END
;
146 buf
[i
++] = BASE64_END
;
149 buf
[i
++] = base64_table
[bin
[j
] >> 2];
150 buf
[i
++] = base64_table
[((bin
[j
] & 3) << 4) | (bin
[j
+ 1] >> 4)];
151 buf
[i
++] = base64_table
[(bin
[j
+ 1] & 15) << 2];
152 buf
[i
++] = BASE64_END
;
163 main (int argc
, char **argv
)
165 short supports_tls
=FALSE
;
169 int result
= STATE_UNKNOWN
;
170 char *cmd_str
= NULL
;
171 char *helocmd
= NULL
;
172 char *error_msg
= "";
175 setlocale (LC_ALL
, "");
176 bindtextdomain (PACKAGE
, LOCALEDIR
);
177 textdomain (PACKAGE
);
179 if (process_arguments (argc
, argv
) == ERROR
)
180 usage4 (_("Could not parse arguments"));
182 /* If localhostname not set on command line, use gethostname to set */
184 localhostname
= malloc (HOST_MAX_BYTES
);
186 printf(_("malloc() failed!\n"));
187 return STATE_CRITICAL
;
189 if(gethostname(localhostname
, HOST_MAX_BYTES
)){
190 printf(_("gethostname() failed!\n"));
191 return STATE_CRITICAL
;
195 asprintf (&helocmd
, "%s%s%s", SMTP_EHLO
, localhostname
, "\r\n");
197 asprintf (&helocmd
, "%s%s%s", SMTP_HELO
, localhostname
, "\r\n");
200 printf("HELOCMD: %s", helocmd
);
202 /* initialize the MAIL command with optional FROM command */
203 asprintf (&cmd_str
, "%sFROM: %s%s", mail_command
, from_arg
, "\r\n");
205 if (verbose
&& smtp_use_dummycmd
)
206 printf ("FROM CMD: %s", cmd_str
);
208 /* initialize alarm signal handling */
209 (void) signal (SIGALRM
, socket_timeout_alarm_handler
);
211 /* set socket timeout */
212 (void) alarm (socket_timeout
);
215 gettimeofday (&tv
, NULL
);
217 /* try to connect to the host at the given port number */
218 result
= my_tcp_connect (server_address
, server_port
, &sd
);
220 if (result
== STATE_OK
) { /* we connected */
222 /* watch for the SMTP connection string and */
223 /* return a WARNING status if we couldn't read any data */
224 if (recv (sd
, buffer
, MAX_INPUT_BUFFER
- 1, 0) == -1) {
225 printf (_("recv() failed\n"));
226 result
= STATE_WARNING
;
230 printf ("%s", buffer
);
231 /* strip the buffer of carriage returns */
233 /* make sure we find the response we are looking for */
234 if (!strstr (buffer
, server_expect
)) {
235 if (server_port
== SMTP_PORT
)
236 printf (_("Invalid SMTP response received from host\n"));
238 printf (_("Invalid SMTP response received from host on port %d\n"),
240 result
= STATE_WARNING
;
244 /* send the HELO/EHLO command */
245 send(sd
, helocmd
, strlen(helocmd
), 0);
247 /* allow for response to helo command to reach us */
248 if(read (sd
, buffer
, MAXBUF
- 1) < 0){
249 printf (_("recv() failed\n"));
250 return STATE_WARNING
;
252 buffer
[MAXBUF
-1]='\0';
253 if(strstr(buffer
, "250 STARTTLS") != NULL
||
254 strstr(buffer
, "250-STARTTLS") != NULL
){
259 if(use_ssl
&& ! supports_tls
){
260 printf(_("WARNING - TLS not supported by server\n"));
261 send (sd
, SMTP_QUIT
, strlen (SMTP_QUIT
), 0);
262 return STATE_WARNING
;
267 /* send the STARTTLS command */
268 send(sd
, SMTP_STARTTLS
, strlen(SMTP_STARTTLS
), 0);
270 recv(sd
,buffer
, MAX_INPUT_BUFFER
-1, 0); /* wait for it */
271 if (!strstr (buffer
, server_expect
)) {
272 printf (_("Server does not support STARTTLS\n"));
273 send (sd
, SMTP_QUIT
, strlen (SMTP_QUIT
), 0);
274 return STATE_UNKNOWN
;
276 result
= np_net_ssl_init(sd
);
277 if(result
!= STATE_OK
) {
278 printf (_("CRITICAL - Cannot create SSL context.\n"));
279 np_net_ssl_cleanup();
281 return STATE_CRITICAL
;
287 * Resend the EHLO command.
289 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
290 * obtained from the server, such as the list of SMTP service
291 * extensions, which was not obtained from the TLS negotiation
292 * itself. The client SHOULD send an EHLO command as the first
293 * command after a successful TLS negotiation.'' For this
294 * reason, some MTAs will not allow an AUTH LOGIN command before
295 * we resent EHLO via TLS.
297 if (my_send(helocmd
, strlen(helocmd
)) <= 0) {
298 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
300 return STATE_UNKNOWN
;
303 printf(_("sent %s"), helocmd
);
304 if ((n
= my_recv(buffer
, MAX_INPUT_BUFFER
- 1)) <= 0) {
305 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
307 return STATE_UNKNOWN
;
311 printf("%s", buffer
);
316 result
= np_net_ssl_check_cert(days_till_exp
);
317 if(result
!= STATE_OK
){
318 printf ("%s\n", _("CRITICAL - Cannot retrieve server certificate."));
323 # endif /* USE_OPENSSL */
327 /* sendmail will syslog a "NOQUEUE" error if session does not attempt
328 * to do something useful. This can be prevented by giving a command
329 * even if syntax is illegal (MAIL requires a FROM:<...> argument)
331 * According to rfc821 you can include a null reversepath in the from command
332 * - but a log message is generated on the smtp server.
334 * You can disable sending mail_command with '--nocommand'
335 * Use the -f option to provide a FROM address
337 if (smtp_use_dummycmd
) {
338 my_send(cmd_str
, strlen(cmd_str
));
339 my_recv(buffer
, MAX_INPUT_BUFFER
-1);
341 printf("%s", buffer
);
344 while (n
< ncommands
) {
345 asprintf (&cmd_str
, "%s%s", commands
[n
], "\r\n");
346 my_send(cmd_str
, strlen(cmd_str
));
347 my_recv(buffer
, MAX_INPUT_BUFFER
-1);
349 printf("%s", buffer
);
351 if (n
< nresponses
) {
352 cflags
|= REG_EXTENDED
| REG_NOSUB
| REG_NEWLINE
;
353 errcode
= regcomp (&preg
, responses
[n
], cflags
);
355 regerror (errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
356 printf (_("Could Not Compile Regular Expression"));
359 excode
= regexec (&preg
, buffer
, 10, pmatch
, eflags
);
363 else if (excode
== REG_NOMATCH
) {
364 result
= STATE_WARNING
;
365 printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result
), buffer
, commands
[n
]);
368 regerror (excode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
369 printf (_("Execute Error: %s\n"), errbuf
);
370 result
= STATE_UNKNOWN
;
376 if (authtype
!= NULL
) {
377 if (strcmp (authtype
, "LOGIN") == 0) {
381 if (authuser
== NULL
) {
382 result
= STATE_CRITICAL
;
383 asprintf(&error_msg
, _("no authuser specified, "));
386 if (authpass
== NULL
) {
387 result
= STATE_CRITICAL
;
388 asprintf(&error_msg
, _("no authpass specified, "));
392 /* send AUTH LOGIN */
393 my_send(SMTP_AUTH_LOGIN
, strlen(SMTP_AUTH_LOGIN
));
395 printf (_("sent %s\n"), "AUTH LOGIN");
397 if((ret
= my_recv(buffer
, MAXBUF
- 1)) < 0){
398 asprintf(&error_msg
, _("recv() failed after AUTH LOGIN, "));
399 result
= STATE_WARNING
;
404 printf (_("received %s\n"), buffer
);
406 if (strncmp (buffer
, "334", 3) != 0) {
407 result
= STATE_CRITICAL
;
408 asprintf(&error_msg
, _("invalid response received after AUTH LOGIN, "));
412 /* encode authuser with base64 */
413 abuf
= base64 (authuser
, strlen(authuser
));
414 strcat (abuf
, "\r\n");
415 my_send(abuf
, strlen(abuf
));
417 printf (_("sent %s\n"), abuf
);
419 if ((ret
= my_recv(buffer
, MAX_INPUT_BUFFER
-1)) == -1) {
420 result
= STATE_CRITICAL
;
421 asprintf(&error_msg
, _("recv() failed after sending authuser, "));
426 printf (_("received %s\n"), buffer
);
428 if (strncmp (buffer
, "334", 3) != 0) {
429 result
= STATE_CRITICAL
;
430 asprintf(&error_msg
, _("invalid response received after authuser, "));
433 /* encode authpass with base64 */
434 abuf
= base64 (authpass
, strlen(authpass
));
435 strcat (abuf
, "\r\n");
436 my_send(abuf
, strlen(abuf
));
438 printf (_("sent %s\n"), abuf
);
440 if ((ret
= my_recv(buffer
, MAX_INPUT_BUFFER
-1)) == -1) {
441 result
= STATE_CRITICAL
;
442 asprintf(&error_msg
, _("recv() failed after sending authpass, "));
447 printf (_("received %s\n"), buffer
);
449 if (strncmp (buffer
, "235", 3) != 0) {
450 result
= STATE_CRITICAL
;
451 asprintf(&error_msg
, _("invalid response received after authpass, "));
457 result
= STATE_CRITICAL
;
458 asprintf(&error_msg
, _("only authtype LOGIN is supported, "));
462 /* tell the server we're done */
463 my_send (SMTP_QUIT
, strlen (SMTP_QUIT
));
465 /* finally close the connection */
469 /* reset the alarm */
472 microsec
= deltime (tv
);
473 elapsed_time
= (double)microsec
/ 1.0e6
;
475 if (result
== STATE_OK
) {
476 if (check_critical_time
&& elapsed_time
> (double) critical_time
)
477 result
= STATE_CRITICAL
;
478 else if (check_warning_time
&& elapsed_time
> (double) warning_time
)
479 result
= STATE_WARNING
;
482 printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"),
486 verbose
?", ":"", verbose
?buffer
:"",
487 fperfdata ("time", elapsed_time
, "s",
488 (int)check_warning_time
, warning_time
,
489 (int)check_critical_time
, critical_time
,
497 /* process command-line arguments */
499 process_arguments (int argc
, char **argv
)
504 static struct option longopts
[] = {
505 {"hostname", required_argument
, 0, 'H'},
506 {"expect", required_argument
, 0, 'e'},
507 {"critical", required_argument
, 0, 'c'},
508 {"warning", required_argument
, 0, 'w'},
509 {"timeout", required_argument
, 0, 't'},
510 {"port", required_argument
, 0, 'p'},
511 {"from", required_argument
, 0, 'f'},
512 {"fqdn", required_argument
, 0, 'F'},
513 {"authtype", required_argument
, 0, 'A'},
514 {"authuser", required_argument
, 0, 'U'},
515 {"authpass", required_argument
, 0, 'P'},
516 {"command", required_argument
, 0, 'C'},
517 {"response", required_argument
, 0, 'R'},
518 {"nocommand", required_argument
, 0, 'n'},
519 {"verbose", no_argument
, 0, 'v'},
520 {"version", no_argument
, 0, 'V'},
521 {"use-ipv4", no_argument
, 0, '4'},
522 {"use-ipv6", no_argument
, 0, '6'},
523 {"help", no_argument
, 0, 'h'},
524 {"starttls",no_argument
,0,'S'},
525 {"certificate",required_argument
,0,'D'},
532 for (c
= 1; c
< argc
; c
++) {
533 if (strcmp ("-to", argv
[c
]) == 0)
534 strcpy (argv
[c
], "-t");
535 else if (strcmp ("-wt", argv
[c
]) == 0)
536 strcpy (argv
[c
], "-w");
537 else if (strcmp ("-ct", argv
[c
]) == 0)
538 strcpy (argv
[c
], "-c");
542 c
= getopt_long (argc
, argv
, "+hVv46t:p:f:e:c:w:H:C:R:SD:F:A:U:P:",
545 if (c
== -1 || c
== EOF
)
549 case 'H': /* hostname */
550 if (is_host (optarg
)) {
551 server_address
= optarg
;
554 usage2 (_("Invalid hostname/address"), optarg
);
558 if (is_intpos (optarg
))
559 server_port
= atoi (optarg
);
561 usage4 (_("Port must be a positive integer"));
565 localhostname
= strdup(optarg
);
567 case 'f': /* from argument */
569 smtp_use_dummycmd
= 1;
580 case 'e': /* server expect string on 220 */
581 server_expect
= optarg
;
583 case 'C': /* commands */
584 if (ncommands
>= command_size
) {
585 commands
= realloc (commands
, command_size
+8);
586 if (commands
== NULL
)
588 _("Could not realloc() units [%d]\n"), ncommands
);
590 commands
[ncommands
] = optarg
;
593 case 'R': /* server responses */
594 if (nresponses
>= response_size
) {
595 responses
= realloc (responses
, response_size
+8);
596 if (responses
== NULL
)
598 _("Could not realloc() units [%d]\n"), nresponses
);
600 responses
[nresponses
] = optarg
;
603 case 'c': /* critical time threshold */
604 if (is_intnonneg (optarg
)) {
605 critical_time
= atoi (optarg
);
606 check_critical_time
= TRUE
;
609 usage4 (_("Critical time must be a positive integer"));
612 case 'w': /* warning time threshold */
613 if (is_intnonneg (optarg
)) {
614 warning_time
= atoi (optarg
);
615 check_warning_time
= TRUE
;
618 usage4 (_("Warning time must be a positive integer"));
621 case 'v': /* verbose */
624 case 't': /* timeout */
625 if (is_intnonneg (optarg
)) {
626 socket_timeout
= atoi (optarg
);
629 usage4 (_("Timeout interval must be a positive integer"));
638 /* Check SSL cert validity */
640 if (!is_intnonneg (optarg
))
641 usage2 ("Invalid certificate expiration period",optarg
);
642 days_till_exp
= atoi (optarg
);
645 usage (_("SSL support not available - install OpenSSL and recompile"));
649 address_family
= AF_INET
;
653 address_family
= AF_INET6
;
655 usage4 (_("IPv6 support not available"));
658 case 'V': /* version */
659 print_revision (progname
, revision
);
670 if (server_address
== NULL
) {
672 if (is_host (argv
[c
]))
673 server_address
= argv
[c
];
675 usage2 (_("Invalid hostname/address"), argv
[c
]);
678 asprintf (&server_address
, "127.0.0.1");
682 if (server_expect
== NULL
)
683 server_expect
= strdup (SMTP_EXPECT
);
685 if (mail_command
== NULL
)
686 mail_command
= strdup("MAIL ");
689 from_arg
= strdup(" ");
691 return validate_arguments ();
697 validate_arguments (void)
707 np_net_ssl_cleanup();
717 asprintf (&myport
, "%d", SMTP_PORT
);
719 print_revision (progname
, revision
);
721 printf ("Copyright (c) 1999-2001 Ethan Galstad <nagios@nagios.org>\n");
722 printf (COPYRIGHT
, copyright
, email
);
724 printf("%s\n", _("This plugin will attempt to open an SMTP connection with the host."));
730 printf (_(UT_HELP_VRSN
));
732 printf (_(UT_HOST_PORT
), 'p', myport
);
734 printf (_(UT_IPv46
));
736 printf (" %s\n", "-e, --expect=STRING");
737 printf (_(" String to expect in first line of server response (default: '%s')\n"), SMTP_EXPECT
);
738 printf (" %s\n", "-n, nocommand");
739 printf (" %s\n", _("Suppress SMTP command"));
740 printf (" %s\n", "-C, --command=STRING");
741 printf (" %s\n", _("SMTP command (may be used repeatedly)"));
742 printf (" %s\n", "-R, --command=STRING");
743 printf (" %s\n", _("Expected response to command (may be used repeatedly)"));
744 printf (" %s\n", "-f, --from=STRING");
745 printf (" %s\n", _("FROM-address to include in MAIL command, required by Exchange 2000")),
747 printf (" %s\n", "-D, --certificate=INTEGER");
748 printf (" %s\n", _("Minimum number of days a certificate has to be valid."));
749 printf (" %s\n", "-S, --starttls");
750 printf (" %s\n", _("Use STARTTLS for the connection."));
753 printf (" %s\n", "-A, --authtype=STRING");
754 printf (" %s\n", _("SMTP AUTH type to check (default none, only LOGIN supported)"));
755 printf (" %s\n", "-U, --authuser=STRING");
756 printf (" %s\n", _("SMTP AUTH username"));
757 printf (" %s\n", "-P, --authpass=STRING");
758 printf (" %s\n", _("SMTP AUTH password"));
760 printf (_(UT_WARN_CRIT
));
762 printf (_(UT_TIMEOUT
), DEFAULT_SOCKET_TIMEOUT
);
764 printf (_(UT_VERBOSE
));
767 printf ("%s\n", _("Successul connects return STATE_OK, refusals and timeouts return"));
768 printf ("%s\n", _("STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful"));
769 printf ("%s\n", _("connects, but incorrect reponse messages from the host result in"));
770 printf ("%s\n", _("STATE_WARNING return values."));
772 printf (_(UT_SUPPORT
));
780 printf (_("Usage:"));
781 printf ("%s -H host [-p port] [-e expect] [-C command] [-f from addr]", progname
);
782 printf ("[-A authtype -U authuser -P authpass] [-w warn] [-c crit] [-t timeout]\n");
783 printf ("[-S] [-D days] [-n] [-v] [-4|-6]\n");