1 /******************************************************************************
2 *
3 * CHECK_SMTP.C
4 *
5 * Program: SMTP plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 *
9 * $Id$
10 *
11 * Description:
12 *
13 * This plugin will attempt to open an SMTP connection with the host.
14 * Successul connects return STATE_OK, refusals and timeouts return
15 * STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful
16 * connects, but incorrect reponse messages from the host result in
17 * STATE_WARNING return values.
18 *
19 * License Information:
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 *
35 *****************************************************************************/
37 #include "config.h"
38 #include "common.h"
39 #include "netutils.h"
40 #include "utils.h"
42 const char *progname = "check_smtp";
44 #define SMTP_PORT 25
45 #define SMTP_EXPECT "220"
46 #define SMTP_HELO "HELO "
48 /* sendmail will syslog a "NOQUEUE" error if session does not attempt
49 * to do something useful. This can be prevented by giving a command
50 * even if syntax is illegal (MAIL requires a FROM:<...> argument)
51 * You can disable sending DUMMYCMD by undefining SMTP_USE_DUMMYCMD.
52 *
53 * According to rfc821 you can include a null reversepath in the from command
54 * - but a log message is generated on the smtp server.
55 *
56 * Use the -f option to provide a FROM address
57 */
59 #define SMTP_DUMMYCMD "MAIL "
60 #define SMTP_USE_DUMMYCMD 1
61 #define SMTP_QUIT "QUIT\r\n"
63 int process_arguments (int, char **);
64 int validate_arguments (void);
65 void print_help (void);
66 void print_usage (void);
68 int server_port = SMTP_PORT;
69 char *server_address = NULL;
70 char *server_expect = NULL;
71 char *from_arg = " ";
72 int warning_time = 0;
73 int check_warning_time = FALSE;
74 int critical_time = 0;
75 int check_critical_time = FALSE;
76 int verbose = FALSE;
78 int
79 main (int argc, char **argv)
80 {
81 int sd;
82 int result;
83 char buffer[MAX_INPUT_BUFFER] = "";
84 char *from_str = NULL;
85 char *helocmd = NULL;
87 if (process_arguments (argc, argv) != OK)
88 usage ("Invalid command arguments supplied\n");
90 /* initialize the HELO command with the localhostname */
91 #ifndef HOST_MAX_BYTES
92 #define HOST_MAX_BYTES 255
93 #endif
94 helocmd = malloc (HOST_MAX_BYTES);
95 gethostname(helocmd, HOST_MAX_BYTES);
96 asprintf (&helocmd, "%s%s%s", SMTP_HELO, helocmd, "\r\n");
98 /* initialize the MAIL command with optional FROM command */
99 asprintf (&from_str, "%sFROM: %s%s", SMTP_DUMMYCMD, from_arg, "\r\n");
101 if (verbose == TRUE)
102 printf ("FROMCMD: %s\n", from_str);
104 /* initialize alarm signal handling */
105 signal (SIGALRM, socket_timeout_alarm_handler);
107 /* set socket timeout */
108 alarm (socket_timeout);
110 /* try to connect to the host at the given port number */
111 time (&start_time);
112 result = my_tcp_connect (server_address, server_port, &sd);
114 /* we connected, so close connection before exiting */
115 if (result == STATE_OK) {
117 /* watch for the SMTP connection string */
118 result = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
120 /* strip the buffer of carriage returns */
121 strip (buffer);
123 /* return a WARNING status if we couldn't read any data */
124 if (result == -1) {
125 printf ("recv() failed\n");
126 result = STATE_WARNING;
127 }
129 else {
131 /* make sure we find the response we are looking for */
132 if (!strstr (buffer, server_expect)) {
133 if (server_port == SMTP_PORT)
134 printf ("Invalid SMTP response received from host\n");
135 else
136 printf ("Invalid SMTP response received from host on port %d\n",
137 server_port);
138 result = STATE_WARNING;
139 }
141 else {
143 time (&end_time);
145 result = STATE_OK;
147 if (check_critical_time == TRUE
148 && (end_time - start_time) > critical_time) result =
149 STATE_CRITICAL;
150 else if (check_warning_time == TRUE
151 && (end_time - start_time) > warning_time) result =
152 STATE_WARNING;
154 if (verbose == TRUE)
155 printf ("SMTP %s - %d sec. response time, %s\n",
156 state_text (result), (int) (end_time - start_time), buffer);
157 else
158 printf ("SMTP %s - %d second response time\n", state_text (result),
159 (int) (end_time - start_time));
160 }
161 }
163 /* close the connection */
165 /* first send the HELO command */
166 send(sd, helocmd, strlen(helocmd), 0);
168 /* allow for response to helo command to reach us */
169 recv(sd, buffer, MAX_INPUT_BUFFER-1, 0);
171 #ifdef SMTP_USE_DUMMYCMD
172 send(sd, from_str, strlen(from_str), 0);
174 /* allow for response to DUMMYCMD to reach us */
175 recv(sd, buffer, MAX_INPUT_BUFFER-1, 0);
177 if (verbose == TRUE)
178 printf("DUMMYCMD: %s\n%s\n",from_str,buffer);
179 #endif /* SMTP_USE_DUMMYCMD */
181 /* tell the server we're done */
182 send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0);
184 /* finally close the connection */
185 close (sd);
186 }
188 /* reset the alarm */
189 alarm (0);
191 return result;
192 }
199 /* process command-line arguments */
200 int
201 process_arguments (int argc, char **argv)
202 {
203 int c;
205 #ifdef HAVE_GETOPT_H
206 int option_index = 0;
207 static struct option long_options[] = {
208 {"hostname", required_argument, 0, 'H'},
209 {"expect", required_argument, 0, 'e'},
210 {"critical", required_argument, 0, 'c'},
211 {"warning", required_argument, 0, 'w'},
212 {"port", required_argument, 0, 'p'},
213 {"from", required_argument, 0, 'f'},
214 {"verbose", no_argument, 0, 'v'},
215 {"version", no_argument, 0, 'V'},
216 {"help", no_argument, 0, 'h'},
217 {0, 0, 0, 0}
218 };
219 #endif
221 if (argc < 2)
222 return ERROR;
224 for (c = 1; c < argc; c++) {
225 if (strcmp ("-to", argv[c]) == 0)
226 strcpy (argv[c], "-t");
227 else if (strcmp ("-wt", argv[c]) == 0)
228 strcpy (argv[c], "-w");
229 else if (strcmp ("-ct", argv[c]) == 0)
230 strcpy (argv[c], "-c");
231 }
233 while (1) {
234 #ifdef HAVE_GETOPT_H
235 c =
236 getopt_long (argc, argv, "+hVvt:p:f:e:c:w:H:", long_options,
237 &option_index);
238 #else
239 c = getopt (argc, argv, "+?hVvt:p:f:e:c:w:H:");
240 #endif
241 if (c == -1 || c == EOF)
242 break;
244 switch (c) {
245 case 'H': /* hostname */
246 if (is_host (optarg)) {
247 server_address = optarg;
248 }
249 else {
250 usage ("Invalid host name\n");
251 }
252 break;
253 case 'p': /* port */
254 if (is_intpos (optarg)) {
255 server_port = atoi (optarg);
256 }
257 else {
258 usage ("Server port must be a positive integer\n");
259 }
260 break;
261 case 'f': /* from argument */
262 from_arg = optarg;
263 break;
264 case 'e': /* server expect string on 220 */
265 server_expect = optarg;
266 break;
267 case 'c': /* critical time threshold */
268 if (is_intnonneg (optarg)) {
269 critical_time = atoi (optarg);
270 check_critical_time = TRUE;
271 }
272 else {
273 usage ("Critical time must be a nonnegative integer\n");
274 }
275 break;
276 case 'w': /* warning time threshold */
277 if (is_intnonneg (optarg)) {
278 warning_time = atoi (optarg);
279 check_warning_time = TRUE;
280 }
281 else {
282 usage ("Warning time must be a nonnegative integer\n");
283 }
284 break;
285 case 'v': /* verbose */
286 verbose = TRUE;
287 break;
288 case 't': /* timeout */
289 if (is_intnonneg (optarg)) {
290 socket_timeout = atoi (optarg);
291 }
292 else {
293 usage ("Time interval must be a nonnegative integer\n");
294 }
295 break;
296 case 'V': /* version */
297 print_revision (progname, "$Revision$");
298 exit (STATE_OK);
299 case 'h': /* help */
300 print_help ();
301 exit (STATE_OK);
302 case '?': /* help */
303 usage ("Invalid argument\n");
304 }
305 }
307 c = optind;
308 if (server_address == NULL) {
309 if (argv[c]) {
310 if (is_host (argv[c]))
311 server_address = argv[c];
312 else
313 usage ("Invalid host name");
314 }
315 else {
316 asprintf (&server_address, "127.0.0.1");
317 }
318 }
320 if (server_expect == NULL)
321 asprintf (&server_expect, SMTP_EXPECT);
323 return validate_arguments ();
324 }
330 int
331 validate_arguments (void)
332 {
333 return OK;
334 }
340 void
341 print_help (void)
342 {
343 print_revision (progname, "$Revision$");
344 printf
345 ("Copyright (c) 2000 Ethan Galstad/Karl DeBisschop\n\n"
346 "This plugin test the SMTP service on the specified host.\n\n");
347 print_usage ();
348 printf
349 ("\nOptions:\n"
350 " -H, --hostname=STRING or IPADDRESS\n"
351 " Check server on the indicated host\n"
352 " -p, --port=INTEGER\n"
353 " Make connection on the indicated port (default: %d)\n"
354 " -e, --expect=STRING\n"
355 " String to expect in first line of server response (default: %s)\n"
356 " -f, --from=STRING\n"
357 " from address to include in MAIL command (default NULL, Exchange2000 requires one)\n"
358 " -w, --warning=INTEGER\n"
359 " Seconds necessary to result in a warning status\n"
360 " -c, --critical=INTEGER\n"
361 " Seconds necessary to result in a critical status\n"
362 " -t, --timeout=INTEGER\n"
363 " Seconds before connection attempt times out (default: %d)\n"
364 " -v, --verbose\n"
365 " Print extra information (command-line use only)\n"
366 " -h, --help\n"
367 " Print detailed help screen\n"
368 " -V, --version\n"
369 " Print version information\n\n",
370 SMTP_PORT, SMTP_EXPECT, DEFAULT_SOCKET_TIMEOUT);
371 support ();
372 }
378 void
379 print_usage (void)
380 {
381 printf
382 ("Usage: %s -H host [-e expect] [-p port] [-f from addr] [-w warn] [-c crit] [-t timeout] [-v]\n"
383 " %s --help\n"
384 " %s --version\n", progname, progname, progname);
385 }