Back to index

nagios-plugins  1.4.16
Defines | Enumerations | Functions | Variables
check_smtp.c File Reference
#include "common.h"
#include "netutils.h"
#include "utils.h"
#include "base64.h"
#include <ctype.h>
#include "regex.h"

Go to the source code of this file.

Defines

#define my_recv(buf, len)   read(sd, buf, len)
#define my_send(buf, len)   send(sd, buf, len, 0)
#define SMTP_EXPECT   "220"
#define SMTP_HELO   "HELO "
#define SMTP_EHLO   "EHLO "
#define SMTP_QUIT   "QUIT\r\n"
#define SMTP_STARTTLS   "STARTTLS\r\n"
#define SMTP_AUTH_LOGIN   "AUTH LOGIN\r\n"
#define HOST_MAX_BYTES   255
#define EHLO_SUPPORTS_STARTTLS   1

Enumerations

enum  { SMTP_PORT = 25 }
enum  { TCP_PROTOCOL = 1, UDP_PROTOCOL = 2 }

Functions

int process_arguments (int, char **)
int validate_arguments (void)
void print_help (void)
void print_usage (void)
void smtp_quit (void)
int recvline (char *, size_t)
int recvlines (char *, size_t)
int my_close (void)
int main (int argc, char **argv)

Variables

const char * progname = "check_smtp"
const char * copyright = "2000-2007"
const char * email = "nagiosplug-devel@lists.sourceforge.net"
char regex_expect [MAX_INPUT_BUFFER] = ""
regex_t preg
regmatch_t pmatch [10]
char timestamp [20] = ""
char errbuf [MAX_INPUT_BUFFER]
int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE
int eflags = 0
int errcode
int excode
int server_port = SMTP_PORT
char * server_address = NULL
char * server_expect = NULL
int smtp_use_dummycmd = 0
char * mail_command = NULL
char * from_arg = NULL
int ncommands = 0
int command_size = 0
int nresponses = 0
int response_size = 0
char ** commands = NULL
char ** responses = NULL
char * authtype = NULL
char * authuser = NULL
char * authpass = NULL
int warning_time = 0
int check_warning_time = FALSE
int critical_time = 0
int check_critical_time = FALSE
int verbose = 0
int use_ssl = FALSE
short use_ehlo = FALSE
short ssl_established = 0
char * localhostname = NULL
int sd
char buffer [MAX_INPUT_BUFFER]
int ignore_send_quit_failure = FALSE

Define Documentation

#define EHLO_SUPPORTS_STARTTLS   1

Definition at line 66 of file check_smtp.c.

#define HOST_MAX_BYTES   255

Definition at line 63 of file check_smtp.c.

#define my_recv (   buf,
  len 
)    read(sd, buf, len)

Definition at line 48 of file check_smtp.c.

#define my_send (   buf,
  len 
)    send(sd, buf, len, 0)

Definition at line 49 of file check_smtp.c.

#define SMTP_AUTH_LOGIN   "AUTH LOGIN\r\n"

Definition at line 60 of file check_smtp.c.

#define SMTP_EHLO   "EHLO "

Definition at line 57 of file check_smtp.c.

#define SMTP_EXPECT   "220"

Definition at line 55 of file check_smtp.c.

#define SMTP_HELO   "HELO "

Definition at line 56 of file check_smtp.c.

#define SMTP_QUIT   "QUIT\r\n"

Definition at line 58 of file check_smtp.c.

#define SMTP_STARTTLS   "STARTTLS\r\n"

Definition at line 59 of file check_smtp.c.


Enumeration Type Documentation

anonymous enum
Enumerator:
SMTP_PORT 

Definition at line 52 of file check_smtp.c.

     {
       SMTP_PORT     = 25
};
anonymous enum
Enumerator:
TCP_PROTOCOL 
UDP_PROTOCOL 

Definition at line 113 of file check_smtp.c.

     {
  TCP_PROTOCOL = 1,
  UDP_PROTOCOL = 2,
};

Function Documentation

int main ( int  argc,
char **  argv 
)

Definition at line 121 of file check_smtp.c.

{
       short supports_tls=FALSE;
       int n = 0;
       double elapsed_time;
       long microsec;
       int result = STATE_UNKNOWN;
       char *cmd_str = NULL;
       char *helocmd = NULL;
       char *error_msg = "";
       struct timeval tv;

       /* Catch pipe errors in read/write - sometimes occurs when writing QUIT */
       (void) signal (SIGPIPE, SIG_IGN);

       setlocale (LC_ALL, "");
       bindtextdomain (PACKAGE, LOCALEDIR);
       textdomain (PACKAGE);

       /* Parse extra opts if any */
       argv=np_extra_opts (&argc, argv, progname);

       if (process_arguments (argc, argv) == ERROR)
              usage4 (_("Could not parse arguments"));

       /* If localhostname not set on command line, use gethostname to set */
       if(! localhostname){
              localhostname = malloc (HOST_MAX_BYTES);
              if(!localhostname){
                     printf(_("malloc() failed!\n"));
                     return STATE_CRITICAL;
              }
              if(gethostname(localhostname, HOST_MAX_BYTES)){
                     printf(_("gethostname() failed!\n"));
                     return STATE_CRITICAL;
              }
       }
       if(use_ehlo)
              asprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n");
       else
              asprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n");

       if (verbose)
              printf("HELOCMD: %s", helocmd);

       /* initialize the MAIL command with optional FROM command  */
       asprintf (&cmd_str, "%sFROM: %s%s", mail_command, from_arg, "\r\n");

       if (verbose && smtp_use_dummycmd)
              printf ("FROM CMD: %s", cmd_str);

       /* initialize alarm signal handling */
       (void) signal (SIGALRM, socket_timeout_alarm_handler);

       /* set socket timeout */
       (void) alarm (socket_timeout);

       /* start timer */
       gettimeofday (&tv, NULL);

       /* try to connect to the host at the given port number */
       result = my_tcp_connect (server_address, server_port, &sd);

       if (result == STATE_OK) { /* we connected */

              /* watch for the SMTP connection string and */
              /* return a WARNING status if we couldn't read any data */
              if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) {
                     printf (_("recv() failed\n"));
                     return STATE_WARNING;
              }
              else {
                     if (verbose)
                            printf ("%s", buffer);
                     /* strip the buffer of carriage returns */
                     strip (buffer);
                     /* make sure we find the response we are looking for */
                     if (!strstr (buffer, server_expect)) {
                            if (server_port == SMTP_PORT)
                                   printf (_("Invalid SMTP response received from host: %s\n"), buffer);
                            else
                                   printf (_("Invalid SMTP response received from host on port %d: %s\n"),
                                                               server_port, buffer);
                            return STATE_WARNING;
                     }
              }

              /* send the HELO/EHLO command */
              send(sd, helocmd, strlen(helocmd), 0);

              /* allow for response to helo command to reach us */
              if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) {
                     printf (_("recv() failed\n"));
                     return STATE_WARNING;
              } else if(use_ehlo){
                     if(strstr(buffer, "250 STARTTLS") != NULL ||
                        strstr(buffer, "250-STARTTLS") != NULL){
                            supports_tls=TRUE;
                     }
              }

              if(use_ssl && ! supports_tls){
                     printf(_("WARNING - TLS not supported by server\n"));
                     smtp_quit();
                     return STATE_WARNING;
              }

#ifdef HAVE_SSL
              if(use_ssl) {
                /* send the STARTTLS command */
                send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);

                recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */
                if (!strstr (buffer, server_expect)) {
                  printf (_("Server does not support STARTTLS\n"));
                  smtp_quit();
                  return STATE_UNKNOWN;
                }
                result = np_net_ssl_init(sd);
                if(result != STATE_OK) {
                  printf (_("CRITICAL - Cannot create SSL context.\n"));
                  np_net_ssl_cleanup();
                  close(sd);
                  return STATE_CRITICAL;
                } else {
                     ssl_established = 1;
                }

              /*
               * Resend the EHLO command.
               *
               * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
               * obtained from the server, such as the list of SMTP service
               * extensions, which was not obtained from the TLS negotiation
               * itself.  The client SHOULD send an EHLO command as the first
               * command after a successful TLS negotiation.''  For this
               * reason, some MTAs will not allow an AUTH LOGIN command before
               * we resent EHLO via TLS.
               */
              if (my_send(helocmd, strlen(helocmd)) <= 0) {
                     printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
                     my_close();
                     return STATE_UNKNOWN;
              }
              if (verbose)
                     printf(_("sent %s"), helocmd);
              if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
                     printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
                     my_close();
                     return STATE_UNKNOWN;
              }
              if (verbose) {
                     printf("%s", buffer);
              }

#  ifdef USE_OPENSSL
                if ( check_cert ) {
                    result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
                  my_close();
                  return result;
                }
#  endif /* USE_OPENSSL */
              }
#endif

              /* sendmail will syslog a "NOQUEUE" error if session does not attempt
               * to do something useful. This can be prevented by giving a command
               * even if syntax is illegal (MAIL requires a FROM:<...> argument)
               *
               * According to rfc821 you can include a null reversepath in the from command
               * - but a log message is generated on the smtp server.
               *
               * Use the -f option to provide a FROM address
               */
              if (smtp_use_dummycmd) {
                my_send(cmd_str, strlen(cmd_str));
                if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose)
                  printf("%s", buffer);
              }

              while (n < ncommands) {
                     asprintf (&cmd_str, "%s%s", commands[n], "\r\n");
                     my_send(cmd_str, strlen(cmd_str));
                     if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose)
                            printf("%s", buffer);
                     strip (buffer);
                     if (n < nresponses) {
                            cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
                            errcode = regcomp (&preg, responses[n], cflags);
                            if (errcode != 0) {
                                   regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
                                   printf (_("Could Not Compile Regular Expression"));
                                   return ERROR;
                            }
                            excode = regexec (&preg, buffer, 10, pmatch, eflags);
                            if (excode == 0) {
                                   result = STATE_OK;
                            }
                            else if (excode == REG_NOMATCH) {
                                   result = STATE_WARNING;
                                   printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result), buffer, commands[n]);
                            }
                            else {
                                   regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
                                   printf (_("Execute Error: %s\n"), errbuf);
                                   result = STATE_UNKNOWN;
                            }
                     }
                     n++;
              }

              if (authtype != NULL) {
                     if (strcmp (authtype, "LOGIN") == 0) {
                            char *abuf;
                            int ret;
                            do {
                                   if (authuser == NULL) {
                                          result = STATE_CRITICAL;
                                          asprintf(&error_msg, _("no authuser specified, "));
                                          break;
                                   }
                                   if (authpass == NULL) {
                                          result = STATE_CRITICAL;
                                          asprintf(&error_msg, _("no authpass specified, "));
                                          break;
                                   }

                                   /* send AUTH LOGIN */
                                   my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN));
                                   if (verbose)
                                          printf (_("sent %s\n"), "AUTH LOGIN");

                                   if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
                                          asprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
                                          result = STATE_WARNING;
                                          break;
                                   }
                                   if (verbose)
                                          printf (_("received %s\n"), buffer);

                                   if (strncmp (buffer, "334", 3) != 0) {
                                          result = STATE_CRITICAL;
                                          asprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
                                          break;
                                   }

                                   /* encode authuser with base64 */
                                   base64_encode_alloc (authuser, strlen(authuser), &abuf);
                                   /* FIXME: abuf shouldn't have enough space to strcat a '\r\n' into it. */
                                   strcat (abuf, "\r\n");
                                   my_send(abuf, strlen(abuf));
                                   if (verbose)
                                          printf (_("sent %s\n"), abuf);

                                   if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
                                          result = STATE_CRITICAL;
                                          asprintf(&error_msg, _("recv() failed after sending authuser, "));
                                          break;
                                   }
                                   if (verbose) {
                                          printf (_("received %s\n"), buffer);
                                   }
                                   if (strncmp (buffer, "334", 3) != 0) {
                                          result = STATE_CRITICAL;
                                          asprintf(&error_msg, _("invalid response received after authuser, "));
                                          break;
                                   }
                                   /* encode authpass with base64 */
                                   base64_encode_alloc (authpass, strlen(authpass), &abuf);
                                   /* FIXME: abuf shouldn't have enough space to strcat a '\r\n' into it. */
                                   strcat (abuf, "\r\n");
                                   my_send(abuf, strlen(abuf));
                                   if (verbose) {
                                          printf (_("sent %s\n"), abuf);
                                   }
                                   if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
                                          result = STATE_CRITICAL;
                                          asprintf(&error_msg, _("recv() failed after sending authpass, "));
                                          break;
                                   }
                                   if (verbose) {
                                          printf (_("received %s\n"), buffer);
                                   }
                                   if (strncmp (buffer, "235", 3) != 0) {
                                          result = STATE_CRITICAL;
                                          asprintf(&error_msg, _("invalid response received after authpass, "));
                                          break;
                                   }
                                   break;
                            } while (0);
                     } else {
                            result = STATE_CRITICAL;
                            asprintf(&error_msg, _("only authtype LOGIN is supported, "));
                     }
              }

              /* tell the server we're done */
              smtp_quit();

              /* finally close the connection */
              close (sd);
       }

       /* reset the alarm */
       alarm (0);

       microsec = deltime (tv);
       elapsed_time = (double)microsec / 1.0e6;

       if (result == STATE_OK) {
              if (check_critical_time && elapsed_time > (double) critical_time)
                     result = STATE_CRITICAL;
              else if (check_warning_time && elapsed_time > (double) warning_time)
                     result = STATE_WARNING;
       }

       printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"),
                     state_text (result),
                     error_msg,
                     elapsed_time,
                     verbose?", ":"", verbose?buffer:"",
                     fperfdata ("time", elapsed_time, "s",
                            (int)check_warning_time, warning_time,
                            (int)check_critical_time, critical_time,
                            TRUE, 0, FALSE, 0));

       return result;
}

Here is the call graph for this function:

int my_close ( void  )

Definition at line 776 of file check_smtp.c.

{
#ifdef HAVE_SSL
  np_net_ssl_cleanup();
#endif
  return close(sd);
}

Here is the caller graph for this function:

void print_help ( void  )
void print_usage ( void  )

Definition at line 389 of file check_cpqarray.c.

{
  printf("cpqarrayd [options]\n");
  printf("   -h         prints this text\n");
  printf("   -d         enables debugging\n");
}
int process_arguments ( int  argc,
char **  argv 
)
int recvline ( char *  buf,
size_t  bufsize 
)

Definition at line 727 of file check_smtp.c.

{
       int result;
       unsigned i;

       for (i = result = 0; i < bufsize - 1; i++) {
              if ((result = my_recv(&buf[i], 1)) != 1)
                     break;
              if (buf[i] == '\n') {
                     buf[++i] = '\0';
                     return i;
              }
       }
       return (result == 1 || i == 0) ? -2 : result;    /* -2 if out of space */
}

Here is the caller graph for this function:

int recvlines ( char *  buf,
size_t  bufsize 
)

Definition at line 759 of file check_smtp.c.

{
       int result, i;

       for (i = 0; /* forever */; i += result)
              if (!((result = recvline(buf + i, bufsize - i)) > 3 &&
                  isdigit((int)buf[i]) &&
                  isdigit((int)buf[i + 1]) &&
                  isdigit((int)buf[i + 2]) &&
                  buf[i + 3] == '-'))
                     break;

       return (result <= 0) ? result : result + i;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void smtp_quit ( void  )

Definition at line 682 of file check_smtp.c.

{
       int bytes;
       int n;

       n = my_send(SMTP_QUIT, strlen(SMTP_QUIT));
       if(n < 0) {
              if(ignore_send_quit_failure) {
                     if(verbose) {
                            printf(_("Connection closed by server before sending QUIT command\n"));
                     }
                     return;
              }
              die (STATE_UNKNOWN,
                     _("Connection closed by server before sending QUIT command\n"));
       }

       if (verbose)
              printf(_("sent %s\n"), "QUIT");

       /* read the response but don't care about problems */
       bytes = recvlines(buffer, MAX_INPUT_BUFFER);
       if (verbose) {
              if (bytes < 0)
                     printf(_("recv() failed after QUIT."));
              else if (bytes == 0)
                     printf(_("Connection reset by peer."));
              else {
                     buffer[bytes] = '\0';
                     printf(_("received %s\n"), buffer);
              }
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

int validate_arguments ( void  )

Variable Documentation

char* authpass = NULL

Definition at line 101 of file check_smtp.c.

char* authtype = NULL

Definition at line 99 of file check_smtp.c.

char* authuser = NULL

Definition at line 100 of file check_smtp.c.

Definition at line 112 of file check_smtp.c.

Definition at line 83 of file check_smtp.c.

Definition at line 105 of file check_smtp.c.

Definition at line 103 of file check_smtp.c.

int command_size = 0

Definition at line 94 of file check_smtp.c.

char** commands = NULL

Definition at line 97 of file check_smtp.c.

const char* copyright = "2000-2007"

Definition at line 32 of file check_smtp.c.

int critical_time = 0

Definition at line 104 of file check_smtp.c.

int eflags = 0

Definition at line 84 of file check_smtp.c.

const char* email = "nagiosplug-devel@lists.sourceforge.net"

Definition at line 33 of file check_smtp.c.

Definition at line 82 of file check_smtp.c.

int errcode

Definition at line 85 of file check_smtp.c.

int excode

Definition at line 85 of file check_smtp.c.

char* from_arg = NULL

Definition at line 92 of file check_smtp.c.

Definition at line 117 of file check_smtp.c.

Definition at line 110 of file check_smtp.c.

char* mail_command = NULL

Definition at line 91 of file check_smtp.c.

int ncommands = 0

Definition at line 93 of file check_smtp.c.

int nresponses = 0

Definition at line 95 of file check_smtp.c.

Definition at line 80 of file check_smtp.c.

regex_t preg

Definition at line 79 of file check_smtp.c.

const char* progname = "check_smtp"

Definition at line 31 of file check_smtp.c.

Definition at line 78 of file check_smtp.c.

int response_size = 0

Definition at line 96 of file check_smtp.c.

char** responses = NULL

Definition at line 98 of file check_smtp.c.

int sd

Definition at line 111 of file check_smtp.c.

Definition at line 88 of file check_smtp.c.

Definition at line 89 of file check_smtp.c.

Definition at line 87 of file check_smtp.c.

Definition at line 90 of file check_smtp.c.

short ssl_established = 0

Definition at line 109 of file check_smtp.c.

char timestamp[20] = ""

Definition at line 81 of file check_smtp.c.

short use_ehlo = FALSE

Definition at line 108 of file check_smtp.c.

int use_ssl = FALSE

Definition at line 107 of file check_smtp.c.

int verbose = 0

Definition at line 106 of file check_smtp.c.

int warning_time = 0

Definition at line 102 of file check_smtp.c.