1 /******************************************************************************
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 *****************************************************************************/
19 const char *progname = "check_smtp";
20 const char *revision = "$Revision$";
21 const char *copyright = "1999-2003";
22 const char *authors = "Nagios Plugin Development Team";
23 const char *email = "nagiosplug-devel@lists.sourceforge.net";
25 const char *summary = "\
26 This plugin will attempt to open an SMTP connection with the host.\n";
28 const char *description = "\
29 Successul connects return STATE_OK, refusals and timeouts return\n\
30 STATE_CRITICAL, other errors return STATE_UNKNOWN. Successful\n\
31 connects, but incorrect reponse messages from the host result in\n\
32 STATE_WARNING return values.\n";
34 const char *option_summary = "\
35 -H host [-p port] [-e expect] [-C command] [-f from addr]\n\
36 [-w warn] [-c crit] [-t timeout] [-n] [-v] [-4|-6]";
38 const char *options = "\
39 -H, --hostname=STRING or IPADDRESS\n\
40 Check server on the indicated host\n\
41 -4, --use-ipv4\n\
42 Use IPv4 protocol\n\
43 -6, --use-ipv6\n\
44 Use IPv6 protocol\n\
45 -p, --port=INTEGER\n\
46 Make connection on the indicated port (default: %d)\n\
47 -e, --expect=STRING\n\
48 String to expect in first line of server response (default: '%s')\n\
49 -n, nocommand\n\
50 Suppress SMTP command\n\
51 -C, --command=STRING\n\
52 SMTP command (default: '%s')\n\
53 -f, --from=STRING\n\
54 FROM-address to include in MAIL command, required by Exchange 2000\n\
55 (default: '%s')\n\
56 -w, --warning=INTEGER\n\
57 Seconds necessary to result in a warning status\n\
58 -c, --critical=INTEGER\n\
59 Seconds necessary to result in a critical status\n\
60 -t, --timeout=INTEGER\n\
61 Seconds before connection attempt times out (default: %d)\n\
62 -v, --verbose\n\
63 Print extra information (command-line use only)\n\
64 -h, --help\n\
65 Print detailed help screen\n\
66 -V, --version\n\
67 Print version information\n\n";
69 enum {
70 SMTP_PORT = 25
71 };
72 const char *SMTP_EXPECT = "220";
73 const char *SMTP_HELO = "HELO ";
74 const char *SMTP_QUIT = "QUIT\r\n";
76 #include "config.h"
77 #include "common.h"
78 #include "netutils.h"
79 #include "utils.h"
81 int process_arguments (int, char **);
82 int validate_arguments (void);
83 void print_help (void);
84 void print_usage (void);
86 int server_port = SMTP_PORT;
87 char *server_address = NULL;
88 char *server_expect = NULL;
89 int smtp_use_dummycmd = 1;
90 char *mail_command = "MAIL ";
91 char *from_arg = " ";
92 int warning_time = 0;
93 int check_warning_time = FALSE;
94 int critical_time = 0;
95 int check_critical_time = FALSE;
96 int verbose = 0;
98 int
99 main (int argc, char **argv)
100 {
101 int sd;
102 double elapsed_time;
103 int result = STATE_UNKNOWN;
104 char buffer[MAX_INPUT_BUFFER] = "";
105 char *from_str = NULL;
106 char *helocmd = NULL;
107 struct timeval tv;
109 if (process_arguments (argc, argv) != OK)
110 usage ("Invalid command arguments supplied\n");
112 /* initialize the HELO command with the localhostname */
113 #ifndef HOST_MAX_BYTES
114 #define HOST_MAX_BYTES 255
115 #endif
116 helocmd = malloc (HOST_MAX_BYTES);
117 gethostname(helocmd, HOST_MAX_BYTES);
118 asprintf (&helocmd, "%s%s%s", SMTP_HELO, helocmd, "\r\n");
120 /* initialize the MAIL command with optional FROM command */
121 asprintf (&from_str, "%sFROM: %s%s", mail_command, from_arg, "\r\n");
123 if (verbose)
124 printf ("FROMCMD: %s\n", from_str);
126 /* initialize alarm signal handling */
127 (void) signal (SIGALRM, socket_timeout_alarm_handler);
129 /* set socket timeout */
130 (void) alarm (socket_timeout);
132 /* start timer */
133 gettimeofday (&tv, NULL);
135 /* try to connect to the host at the given port number */
136 result = my_tcp_connect (server_address, server_port, &sd);
138 /* we connected, so close connection before exiting */
139 if (result == STATE_OK) {
141 /* watch for the SMTP connection string and */
142 /* return a WARNING status if we couldn't read any data */
143 if (recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0) == -1) {
144 printf ("recv() failed\n");
145 result = STATE_WARNING;
146 }
147 else {
148 /* strip the buffer of carriage returns */
149 strip (buffer);
150 /* make sure we find the response we are looking for */
151 if (!strstr (buffer, server_expect)) {
152 if (server_port == SMTP_PORT)
153 printf ("Invalid SMTP response received from host\n");
154 else
155 printf ("Invalid SMTP response received from host on port %d\n",
156 server_port);
157 result = STATE_WARNING;
158 }
159 }
161 /* send the HELO command */
162 send(sd, helocmd, strlen(helocmd), 0);
164 /* allow for response to helo command to reach us */
165 recv(sd, buffer, MAX_INPUT_BUFFER-1, 0);
167 /* sendmail will syslog a "NOQUEUE" error if session does not attempt
168 * to do something useful. This can be prevented by giving a command
169 * even if syntax is illegal (MAIL requires a FROM:<...> argument)
170 *
171 * According to rfc821 you can include a null reversepath in the from command
172 * - but a log message is generated on the smtp server.
173 *
174 * You can disable sending mail_command with '--nocommand'
175 * Use the -f option to provide a FROM address
176 */
177 if (smtp_use_dummycmd) {
179 send(sd, from_str, strlen(from_str), 0);
181 /* allow for response to mail_command to reach us */
182 recv(sd, buffer, MAX_INPUT_BUFFER-1, 0);
184 if (verbose)
185 printf("DUMMYCMD: %s\n%s\n",from_str,buffer);
187 } /* smtp_use_dummycmd */
189 /* tell the server we're done */
190 send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0);
192 /* finally close the connection */
193 close (sd);
194 }
196 /* reset the alarm */
197 alarm (0);
199 elapsed_time = delta_time (tv);
201 if (check_critical_time && elapsed_time > (double) critical_time)
202 result = STATE_CRITICAL;
203 else if (check_warning_time && elapsed_time > (double) warning_time)
204 result = STATE_WARNING;
206 if (verbose)
207 printf ("SMTP %s - %.3f sec. response time, %s|time=%.3f\n",
208 state_text (result), elapsed_time, buffer, elapsed_time);
209 else
210 printf ("SMTP %s - %.3f second response time|time=%.3f\n",
211 state_text (result), elapsed_time, elapsed_time);
213 return result;
214 }
221 /* process command-line arguments */
222 int
223 process_arguments (int argc, char **argv)
224 {
225 int c;
227 int option_index = 0;
228 static struct option long_options[] = {
229 {"hostname", required_argument, 0, 'H'},
230 {"expect", required_argument, 0, 'e'},
231 {"critical", required_argument, 0, 'c'},
232 {"warning", required_argument, 0, 'w'},
233 {"timeout", required_argument, 0, 't'},
234 {"port", required_argument, 0, 'p'},
235 {"from", required_argument, 0, 'f'},
236 {"command", required_argument, 0, 'C'},
237 {"nocommand", required_argument, 0, 'n'},
238 {"verbose", no_argument, 0, 'v'},
239 {"version", no_argument, 0, 'V'},
240 {"use-ipv4", no_argument, 0, '4'},
241 {"use-ipv6", no_argument, 0, '6'},
242 {"help", no_argument, 0, 'h'},
243 {0, 0, 0, 0}
244 };
246 if (argc < 2)
247 return ERROR;
249 for (c = 1; c < argc; c++) {
250 if (strcmp ("-to", argv[c]) == 0)
251 strcpy (argv[c], "-t");
252 else if (strcmp ("-wt", argv[c]) == 0)
253 strcpy (argv[c], "-w");
254 else if (strcmp ("-ct", argv[c]) == 0)
255 strcpy (argv[c], "-c");
256 }
258 while (1) {
259 c = getopt_long (argc, argv, "+hVv46t:p:f:e:c:w:H:C:",
260 long_options, &option_index);
262 if (c == -1 || c == EOF)
263 break;
265 switch (c) {
266 case 'H': /* hostname */
267 if (is_host (optarg)) {
268 server_address = optarg;
269 }
270 else {
271 usage ("Invalid host name\n");
272 }
273 break;
274 case 'p': /* port */
275 if (is_intpos (optarg)) {
276 server_port = atoi (optarg);
277 }
278 else {
279 usage ("Server port must be a positive integer\n");
280 }
281 break;
282 case 'f': /* from argument */
283 from_arg = optarg;
284 break;
285 case 'e': /* server expect string on 220 */
286 server_expect = optarg;
287 break;
288 case 'C': /* server expect string on 220 */
289 mail_command = optarg;
290 smtp_use_dummycmd = 1;
291 break;
292 case 'n': /* server expect string on 220 */
293 smtp_use_dummycmd = 0;
294 break;
295 case 'c': /* critical time threshold */
296 if (is_intnonneg (optarg)) {
297 critical_time = atoi (optarg);
298 check_critical_time = TRUE;
299 }
300 else {
301 usage ("Critical time must be a nonnegative integer\n");
302 }
303 break;
304 case 'w': /* warning time threshold */
305 if (is_intnonneg (optarg)) {
306 warning_time = atoi (optarg);
307 check_warning_time = TRUE;
308 }
309 else {
310 usage ("Warning time must be a nonnegative integer\n");
311 }
312 break;
313 case 'v': /* verbose */
314 verbose++;
315 break;
316 case 't': /* timeout */
317 if (is_intnonneg (optarg)) {
318 socket_timeout = atoi (optarg);
319 }
320 else {
321 usage ("Time interval must be a nonnegative integer\n");
322 }
323 break;
324 case '4':
325 address_family = AF_INET;
326 break;
327 case '6':
328 #ifdef USE_IPV6
329 address_family = AF_INET6;
330 #else
331 usage ("IPv6 support not available\n");
332 #endif
333 break;
334 case 'V': /* version */
335 print_revision (progname, revision);
336 exit (STATE_OK);
337 case 'h': /* help */
338 print_help ();
339 exit (STATE_OK);
340 case '?': /* help */
341 usage ("Invalid argument\n");
342 }
343 }
345 c = optind;
346 if (server_address == NULL) {
347 if (argv[c]) {
348 if (is_host (argv[c]))
349 server_address = argv[c];
350 else
351 usage ("Invalid host name");
352 }
353 else {
354 asprintf (&server_address, "127.0.0.1");
355 }
356 }
358 if (server_expect == NULL)
359 asprintf (&server_expect, SMTP_EXPECT);
361 return validate_arguments ();
362 }
368 int
369 validate_arguments (void)
370 {
371 return OK;
372 }
378 void
379 print_help (void)
380 {
381 print_revision (progname, revision);
382 printf ("Copyright (c) %s %s\n\t<%s>\n\n%s\n",
383 copyright, authors, email, summary);
384 print_usage ();
385 printf ("\nOptions:\n");
386 printf (options, SMTP_PORT, SMTP_EXPECT, mail_command, from_arg,
387 DEFAULT_SOCKET_TIMEOUT);
388 support ();
389 }
395 void
396 print_usage (void)
397 {
398 printf ("Usage: %s %s\n"
399 " %s --help\n"
400 " %s --version\n",
401 progname, option_summary, progname, progname);
402 }