Back to index

nagios-nrpe  2.13
check_nrpe.c
Go to the documentation of this file.
00001 /********************************************************************************************
00002  *
00003  * CHECK_NRPE.C - NRPE Plugin For Nagios
00004  * Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)
00005  * License: GPL
00006  *
00007  * Last Modified: 11-11-2011
00008  *
00009  * Command line: CHECK_NRPE -H <host_address> [-p port] [-c command] [-to to_sec]
00010  *
00011  * Description:
00012  *
00013  * This plugin will attempt to connect to the NRPE daemon on the specified server and port.
00014  * The daemon will attempt to run the command defined as [command].  Program output and
00015  * return code are sent back from the daemon and displayed as this plugin's own output and
00016  * return code.
00017  *
00018  ********************************************************************************************/
00019 
00020 #include "../include/common.h"
00021 #include "../include/config.h"
00022 #include "../include/utils.h"
00023 
00024 
00025 #define DEFAULT_NRPE_COMMAND       "_NRPE_CHECK"  /* check version of NRPE daemon */
00026 
00027 int server_port=DEFAULT_SERVER_PORT;
00028 char *server_name=NULL;
00029 char *command_name=NULL;
00030 int socket_timeout=DEFAULT_SOCKET_TIMEOUT;
00031 int timeout_return_code=STATE_CRITICAL;
00032 int sd;
00033 
00034 char query[MAX_INPUT_BUFFER]="";
00035 
00036 int show_help=FALSE;
00037 int show_license=FALSE;
00038 int show_version=FALSE;
00039 
00040 #ifdef HAVE_SSL
00041 SSL_METHOD *meth;
00042 SSL_CTX *ctx;
00043 SSL *ssl;
00044 int use_ssl=TRUE;
00045 #else
00046 int use_ssl=FALSE;
00047 #endif
00048 
00049 
00050 int process_arguments(int,char **);
00051 void alarm_handler(int);
00052 int graceful_close(int,int);
00053 
00054 
00055 
00056 
00057 int main(int argc, char **argv){
00058         u_int32_t packet_crc32;
00059         u_int32_t calculated_crc32;
00060        int16_t result;
00061        int rc;
00062        packet send_packet;
00063        packet receive_packet;
00064        int bytes_to_send;
00065        int bytes_to_recv;
00066 
00067        result=process_arguments(argc,argv);
00068 
00069         if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE){
00070 
00071               if(result!=OK)
00072                      printf("Incorrect command line arguments supplied\n");
00073                 printf("\n");
00074               printf("NRPE Plugin for Nagios\n");
00075               printf("Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)\n");
00076               printf("Version: %s\n",PROGRAM_VERSION);
00077               printf("Last Modified: %s\n",MODIFICATION_DATE);
00078               printf("License: GPL v2 with exemptions (-l for more info)\n");
00079 #ifdef HAVE_SSL
00080               printf("SSL/TLS Available: Anonymous DH Mode, OpenSSL 0.9.6 or higher required\n");
00081 #endif
00082               printf("\n");
00083                }
00084 
00085        if(result!=OK || show_help==TRUE){
00086 
00087               printf("Usage: check_nrpe -H <host> [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
00088               printf("\n");
00089               printf("Options:\n");
00090               printf(" -n         = Do no use SSL\n");
00091               printf(" -u         = Make socket timeouts return an UNKNOWN state instead of CRITICAL\n");
00092               printf(" <host>     = The address of the host running the NRPE daemon\n");
00093               printf(" [port]     = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
00094               printf(" [timeout]  = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
00095               printf(" [command]  = The name of the command that the remote daemon should run\n");
00096               printf(" [arglist]  = Optional arguments that should be passed to the command.  Multiple\n");
00097               printf("              arguments should be separated by a space.  If provided, this must be\n");
00098               printf("              the last option supplied on the command line.\n");
00099               printf("\n");
00100               printf("Note:\n");
00101               printf("This plugin requires that you have the NRPE daemon running on the remote host.\n");
00102               printf("You must also have configured the daemon to associate a specific plugin command\n");
00103               printf("with the [command] option you are specifying here.  Upon receipt of the\n");
00104               printf("[command] argument, the NRPE daemon will run the appropriate plugin command and\n");
00105               printf("send the plugin output and return code back to *this* plugin.  This allows you\n");
00106               printf("to execute plugins on remote hosts and 'fake' the results to make Nagios think\n");
00107               printf("the plugin is being run locally.\n");
00108               printf("\n");
00109                }
00110 
00111        if(show_license==TRUE)
00112               display_license();
00113 
00114         if(result!=OK || show_help==TRUE || show_license==TRUE || show_version==TRUE)
00115               exit(STATE_UNKNOWN);
00116 
00117 
00118         /* generate the CRC 32 table */
00119         generate_crc32_table();
00120 
00121 #ifdef HAVE_SSL
00122        /* initialize SSL */
00123        if(use_ssl==TRUE){
00124               SSL_library_init();
00125               SSLeay_add_ssl_algorithms();
00126               meth=SSLv23_client_method();
00127               SSL_load_error_strings();
00128               if((ctx=SSL_CTX_new(meth))==NULL){
00129                      printf("CHECK_NRPE: Error - could not create SSL context.\n");
00130                      exit(STATE_CRITICAL);
00131                       }
00132 
00133               /* ADDED 01/19/2004 */
00134               /* use only TLSv1 protocol */
00135               SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
00136                 }
00137 #endif
00138 
00139        /* initialize alarm signal handling */
00140        signal(SIGALRM,alarm_handler);
00141 
00142        /* set socket timeout */
00143        alarm(socket_timeout);
00144 
00145        /* try to connect to the host at the given port number */
00146        result=my_tcp_connect(server_name,server_port,&sd);
00147 
00148 #ifdef HAVE_SSL
00149        /* do SSL handshake */
00150        if(result==STATE_OK && use_ssl==TRUE){
00151               if((ssl=SSL_new(ctx))!=NULL){
00152                      SSL_CTX_set_cipher_list(ctx,"ADH");
00153                      SSL_set_fd(ssl,sd);
00154                      if((rc=SSL_connect(ssl))!=1){
00155                             printf("CHECK_NRPE: Error - Could not complete SSL handshake.\n");
00156 #ifdef DEBUG
00157                             printf("SSL_connect=%d\n",rc);
00158                             /*
00159                             rc=SSL_get_error(ssl,rc);
00160                             printf("SSL_get_error=%d\n",rc);
00161                             printf("ERR_get_error=%lu\n",ERR_get_error());
00162                             printf("%s\n",ERR_error_string(rc,NULL));
00163                             */
00164                             ERR_print_errors_fp(stdout);
00165 #endif
00166                             result=STATE_CRITICAL;
00167                              }
00168                       }
00169               else{
00170                      printf("CHECK_NRPE: Error - Could not create SSL connection structure.\n");
00171                      result=STATE_CRITICAL;
00172                       }
00173 
00174               /* bail if we had errors */
00175               if(result!=STATE_OK){
00176                      SSL_CTX_free(ctx);
00177                      close(sd);
00178                      exit(result);
00179                       }
00180                }
00181 #endif
00182 
00183        /* we're connected and ready to go */
00184        if(result==STATE_OK){
00185 
00186               /* clear the packet buffer */
00187               bzero(&send_packet,sizeof(send_packet));
00188 
00189               /* fill the packet with semi-random data */
00190               randomize_buffer((char *)&send_packet,sizeof(send_packet));
00191 
00192               /* initialize packet data */
00193               send_packet.packet_version=(int16_t)htons(NRPE_PACKET_VERSION_2);
00194               send_packet.packet_type=(int16_t)htons(QUERY_PACKET);
00195               strncpy(&send_packet.buffer[0],query,MAX_PACKETBUFFER_LENGTH);
00196               send_packet.buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
00197 
00198               /* calculate the crc 32 value of the packet */
00199               send_packet.crc32_value=(u_int32_t)0L;
00200               calculated_crc32=calculate_crc32((char *)&send_packet,sizeof(send_packet));
00201               send_packet.crc32_value=(u_int32_t)htonl(calculated_crc32);
00202 
00203 
00204               /***** ENCRYPT REQUEST *****/
00205 
00206 
00207               /* send the packet */
00208               bytes_to_send=sizeof(send_packet);
00209               if(use_ssl==FALSE)
00210                      rc=sendall(sd,(char *)&send_packet,&bytes_to_send);
00211 #ifdef HAVE_SSL
00212               else{
00213                      rc=SSL_write(ssl,&send_packet,bytes_to_send);
00214                      if(rc<0)
00215                             rc=-1;
00216                       }
00217 #endif
00218               if(rc==-1){
00219                      printf("CHECK_NRPE: Error sending query to host.\n");
00220                      close(sd);
00221                      return STATE_UNKNOWN;
00222                       }
00223 
00224               /* wait for the response packet */
00225               bytes_to_recv=sizeof(receive_packet);
00226               if(use_ssl==FALSE)
00227                      rc=recvall(sd,(char *)&receive_packet,&bytes_to_recv,socket_timeout);
00228 #ifdef HAVE_SSL
00229               else
00230                      rc=SSL_read(ssl,&receive_packet,bytes_to_recv);
00231 #endif
00232 
00233               /* reset timeout */
00234               alarm(0);
00235 
00236               /* close the connection */
00237 #ifdef HAVE_SSL
00238               if(use_ssl==TRUE){
00239                      SSL_shutdown(ssl);
00240                      SSL_free(ssl);
00241                      SSL_CTX_free(ctx);
00242                        }
00243 #endif
00244               graceful_close(sd,1000);
00245 
00246               /* recv() error */
00247               if(rc<0){
00248                      printf("CHECK_NRPE: Error receiving data from daemon.\n");
00249                      return STATE_UNKNOWN;
00250                       }
00251 
00252               /* server disconnected */
00253               else if(rc==0){
00254                      printf("CHECK_NRPE: Received 0 bytes from daemon.  Check the remote server logs for error messages.\n");
00255                      return STATE_UNKNOWN;
00256                       }
00257 
00258               /* receive underflow */
00259               else if(bytes_to_recv<sizeof(receive_packet)){
00260                      printf("CHECK_NRPE: Receive underflow - only %d bytes received (%d expected).\n",bytes_to_recv,sizeof(receive_packet));
00261                      return STATE_UNKNOWN;
00262                       }
00263 
00264               
00265               /***** DECRYPT RESPONSE *****/
00266 
00267 
00268               /* check the crc 32 value */
00269               packet_crc32=ntohl(receive_packet.crc32_value);
00270               receive_packet.crc32_value=0L;
00271               calculated_crc32=calculate_crc32((char *)&receive_packet,sizeof(receive_packet));
00272               if(packet_crc32!=calculated_crc32){
00273                      printf("CHECK_NRPE: Response packet had invalid CRC32.\n");
00274                      close(sd);
00275                      return STATE_UNKNOWN;
00276                         }
00277        
00278               /* check packet version */
00279               if(ntohs(receive_packet.packet_version)!=NRPE_PACKET_VERSION_2){
00280                      printf("CHECK_NRPE: Invalid packet version received from server.\n");
00281                      close(sd);
00282                      return STATE_UNKNOWN;
00283                      }
00284 
00285               /* check packet type */
00286               if(ntohs(receive_packet.packet_type)!=RESPONSE_PACKET){
00287                      printf("CHECK_NRPE: Invalid packet type received from server.\n");
00288                      close(sd);
00289                      return STATE_UNKNOWN;
00290                      }
00291 
00292               /* get the return code from the remote plugin */
00293               result=(int16_t)ntohs(receive_packet.result_code);
00294 
00295               /* print the output returned by the daemon */
00296               receive_packet.buffer[MAX_PACKETBUFFER_LENGTH-1]='\x0';
00297               if(!strcmp(receive_packet.buffer,""))
00298                      printf("CHECK_NRPE: No output returned from daemon.\n");
00299               else
00300                      printf("%s\n",receive_packet.buffer);
00301                }
00302 
00303        /* reset the alarm */
00304        else
00305               alarm(0);
00306 
00307        return result;
00308         }
00309 
00310 
00311 
00312 /* process command line arguments */
00313 int process_arguments(int argc, char **argv){
00314        char optchars[MAX_INPUT_BUFFER];
00315        int argindex=0;
00316        int c=1;
00317        int i=1;
00318 
00319 #ifdef HAVE_GETOPT_LONG
00320        int option_index=0;
00321        static struct option long_options[]={
00322               {"host", required_argument, 0, 'H'},
00323               {"command", required_argument, 0, 'c'},
00324               {"args", required_argument, 0, 'a'},
00325               {"no-ssl", no_argument, 0, 'n'},
00326               {"unknown-timeout", no_argument, 0, 'u'},
00327               {"timeout", required_argument, 0, 't'},
00328               {"port", required_argument, 0, 'p'},
00329               {"help", no_argument, 0, 'h'},
00330               {"license", no_argument, 0, 'l'},
00331               {0, 0, 0, 0}
00332                 };
00333 #endif
00334 
00335        /* no options were supplied */
00336        if(argc<2)
00337               return ERROR;
00338 
00339        snprintf(optchars,MAX_INPUT_BUFFER,"H:c:a:t:p:nuhl");
00340 
00341        while(1){
00342 #ifdef HAVE_GETOPT_LONG
00343               c=getopt_long(argc,argv,optchars,long_options,&option_index);
00344 #else
00345               c=getopt(argc,argv,optchars);
00346 #endif
00347               if(c==-1 || c==EOF)
00348                      break;
00349 
00350               /* process all arguments */
00351               switch(c){
00352 
00353               case '?':
00354               case 'h':
00355                      show_help=TRUE;
00356                      break;
00357               case 'V':
00358                      show_version=TRUE;
00359                      break;
00360               case 'l':
00361                      show_license=TRUE;
00362                      break;
00363               case 't':
00364                      socket_timeout=atoi(optarg);
00365                      if(socket_timeout<=0)
00366                             return ERROR;
00367                      break;
00368               case 'p':
00369                      server_port=atoi(optarg);
00370                      if(server_port<=0)
00371                             return ERROR;
00372                      break;
00373               case 'H':
00374                      server_name=strdup(optarg);
00375                      break;
00376               case 'c':
00377                      command_name=strdup(optarg);
00378                      break;
00379               case 'a':
00380                      argindex=optind;
00381                      break;
00382               case 'n':
00383                      use_ssl=FALSE;
00384                      break;
00385               case 'u':
00386                      timeout_return_code=STATE_UNKNOWN;
00387                      break;
00388               default:
00389                      return ERROR;
00390                      break;
00391                       }
00392                }
00393 
00394        /* determine (base) command query */
00395        snprintf(query,sizeof(query),"%s",(command_name==NULL)?DEFAULT_NRPE_COMMAND:command_name);
00396        query[sizeof(query)-1]='\x0';
00397 
00398        /* get the command args */
00399        if(argindex>0){
00400 
00401               for(c=argindex-1;c<argc;c++){
00402 
00403                      i=sizeof(query)-strlen(query)-2;
00404                      if(i<=0)
00405                             break;
00406 
00407                      strcat(query,"!");
00408                      strncat(query,argv[c],i);
00409                      query[sizeof(query)-1]='\x0';
00410                       }
00411                }
00412 
00413        /* make sure required args were supplied */
00414        if(server_name==NULL && show_help==FALSE && show_version==FALSE  && show_license==FALSE)
00415               return ERROR;
00416 
00417 
00418        return OK;
00419         }
00420 
00421 
00422 
00423 void alarm_handler(int sig){
00424 
00425        printf("CHECK_NRPE: Socket timeout after %d seconds.\n",socket_timeout);
00426 
00427        exit(timeout_return_code);
00428         }
00429 
00430 
00431 /* submitted by Mark Plaksin 08/31/2006 */
00432 int graceful_close(int sd, int timeout){
00433         fd_set in;
00434         struct timeval tv;
00435         char buf[1000];
00436 
00437        /* send FIN packet */
00438         shutdown(sd,SHUT_WR);  
00439         for(;;){
00440 
00441                 FD_ZERO(&in);
00442                 FD_SET(sd,&in);
00443                 tv.tv_sec=timeout/1000;
00444                 tv.tv_usec=(timeout % 1000)*1000;
00445 
00446               /* timeout or error */
00447                 if(1!=select(sd+1,&in,NULL,NULL,&tv))
00448                      break;   
00449 
00450               /* no more data (FIN or RST) */
00451                 if(0>=recv(sd,buf,sizeof(buf),0))
00452                      break;
00453               }
00454 
00455 #ifdef HAVE_CLOSESOCKET
00456         closesocket(sd);
00457 #else
00458        close(sd);
00459 #endif
00460 
00461        return OK;
00462        }