blob: 480cdd1da1ba5578ea2b311f1e32805fcdeea3d0 [file] [log] [blame]
/*
* snmptrapd.c - receive and log snmp traps
*
*/
/*****************************************************************
Copyright 1989, 1991, 1992 by Carnegie Mellon University
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/
#include <net-snmp/net-snmp-config.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <sys/types.h>
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#if HAVE_WINSOCK_H
#include <winsock.h>
#else
#include <sys/socket.h>
#endif
#if HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <stdio.h>
#if HAVE_SYS_TIME_H
# include <sys/time.h>
# if TIME_WITH_SYS_TIME
# include <time.h>
# endif
#else
# include <time.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_SYSLOG_H
#include <syslog.h>
#endif
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#if HAVE_NET_IF_H
#include <net/if.h>
#endif
#if HAVE_NETDB_H
#include <netdb.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_PROCESS_H /* Win32-getpid */
#include <process.h>
#endif
#include <signal.h>
#include <errno.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "snmptrapd_handlers.h"
#include "snmptrapd_log.h"
#include "notification_log.h"
#if USE_LIBWRAP
#include <tcpd.h>
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#endif
#define NETSNMP_DS_APP_NUMERIC_IP 8 /* must not conflict with agent's DS booleans */
/*
* #define NETSNMP_DS_APP_DONT_LOG 9 defined in notification_log.h
*/
#ifndef BSD4_3
#define BSD4_2
#endif
#ifndef FD_SET
typedef long fd_mask;
#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p) memset((p), 0, sizeof(*(p)))
#endif
char *logfile = 0;
int Print = 0;
int Syslog = 0;
int Event = 0;
int dropauth = 0;
int running = 1;
int reconfig = 0;
u_long num_received = 0;
static char *default_port = "udp:162";
const char *trap1_std_str =
"%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b] (via %A [%a]): %N\n\t%W Trap (%q) Uptime: %#T\n%v\n",
*trap2_std_str = "%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b]:\n%v\n";
char *trap1_fmt_str = NULL, *trap2_fmt_str = NULL; /* how to format logging to stderr */
/*
* These definitions handle 4.2 systems without additional syslog facilities.
*/
#ifndef LOG_CONS
#define LOG_CONS 0 /* Don't bother if not defined... */
#endif
#ifndef LOG_PID
#define LOG_PID 0 /* Don't bother if not defined... */
#endif
#ifndef LOG_LOCAL0
#define LOG_LOCAL0 0
#endif
#ifndef LOG_LOCAL1
#define LOG_LOCAL1 0
#endif
#ifndef LOG_LOCAL2
#define LOG_LOCAL2 0
#endif
#ifndef LOG_LOCAL3
#define LOG_LOCAL3 0
#endif
#ifndef LOG_LOCAL4
#define LOG_LOCAL4 0
#endif
#ifndef LOG_LOCAL5
#define LOG_LOCAL5 0
#endif
#ifndef LOG_LOCAL6
#define LOG_LOCAL6 0
#endif
#ifndef LOG_LOCAL7
#define LOG_LOCAL7 0
#endif
#ifndef LOG_DAEMON
#define LOG_DAEMON 0
#endif
/*
* Include an extra Facility variable to allow command line adjustment of
* syslog destination
*/
int Facility = LOG_INFO;
struct timeval Now;
void trapd_update_config(void);
const char *
trap_description(int trap)
{
switch (trap) {
case SNMP_TRAP_COLDSTART:
return "Cold Start";
case SNMP_TRAP_WARMSTART:
return "Warm Start";
case SNMP_TRAP_LINKDOWN:
return "Link Down";
case SNMP_TRAP_LINKUP:
return "Link Up";
case SNMP_TRAP_AUTHFAIL:
return "Authentication Failure";
case SNMP_TRAP_EGPNEIGHBORLOSS:
return "EGP Neighbor Loss";
case SNMP_TRAP_ENTERPRISESPECIFIC:
return "Enterprise Specific";
default:
return "Unknown Type";
}
}
netsnmp_pdu *
snmp_clone_pdu2(netsnmp_pdu *pdu, int command)
{
netsnmp_pdu *newpdu = snmp_clone_pdu(pdu);
if (newpdu)
newpdu->command = command;
return newpdu;
}
static oid risingAlarm[] = { 1, 3, 6, 1, 6, 3, 2, 1, 1, 3, 1 };
static oid fallingAlarm[] = { 1, 3, 6, 1, 6, 3, 2, 1, 1, 3, 2 };
static oid unavailableAlarm[] = { 1, 3, 6, 1, 6, 3, 2, 1, 1, 3, 3 };
void
event_input(netsnmp_variable_list * vp)
{
int eventid;
oid variable[MAX_OID_LEN];
int variablelen;
u_long destip;
int sampletype;
int value;
int threshold;
oid *op;
vp = vp->next_variable; /* skip sysUptime */
if (vp->val_len != sizeof(risingAlarm)
|| !memcmp(vp->val.objid, risingAlarm, sizeof(risingAlarm)))
eventid = 1;
else if (vp->val_len != sizeof(risingAlarm)
|| !memcmp(vp->val.objid, fallingAlarm, sizeof(fallingAlarm)))
eventid = 2;
else if (vp->val_len != sizeof(risingAlarm)
|| !memcmp(vp->val.objid, unavailableAlarm,
sizeof(unavailableAlarm)))
eventid = 3;
else {
fprintf(stderr, "unknown event\n");
eventid = 0;
}
vp = vp->next_variable;
memmove(variable, vp->val.objid, vp->val_len * sizeof(oid));
variablelen = vp->val_len;
op = vp->name + 22;
destip = 0;
destip |= (*op++) << 24;
destip |= (*op++) << 16;
destip |= (*op++) << 8;
destip |= *op++;
vp = vp->next_variable;
sampletype = *vp->val.integer;
vp = vp->next_variable;
value = *vp->val.integer;
vp = vp->next_variable;
threshold = *vp->val.integer;
printf("%d: 0x%02lX %d %d %d\n", eventid, destip, sampletype, value,
threshold);
}
void
send_handler_data(FILE * file, struct hostent *host,
netsnmp_pdu *pdu, netsnmp_transport *transport)
{
netsnmp_variable_list tmpvar, *vars;
static oid trapoids[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5, 0 };
static oid snmpsysuptime[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
static oid snmptrapoid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
static oid snmptrapent[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 3, 0 };
static oid snmptrapaddr[] = { 1, 3, 6, 1, 6, 3, 18, 1, 3, 0 };
static oid snmptrapcom[] = { 1, 3, 6, 1, 6, 3, 18, 1, 4, 0 };
oid enttrapoid[MAX_OID_LEN];
int enttraplen = pdu->enterprise_length;
char *tstr = NULL;
if (transport != NULL && transport->f_fmtaddr != NULL) {
tstr = transport->f_fmtaddr(transport, pdu->transport_data,
pdu->transport_data_length);
fprintf(file, "%s\n%s\n", host ? host->h_name : tstr, tstr);
free(tstr);
} else {
fprintf(file, "%s\n<UNKNOWN>\n",
host ? host->h_name : "<UNKNOWN>");
}
if (pdu->command == SNMP_MSG_TRAP) {
/*
* convert a v1 trap to a v2 variable binding list:
* The uptime and trapOID go first in the list.
*/
tmpvar.val.integer = (long *) &pdu->time;
tmpvar.val_len = sizeof(pdu->time);
tmpvar.type = ASN_TIMETICKS;
fprint_variable(file, snmpsysuptime,
sizeof(snmpsysuptime) / sizeof(oid), &tmpvar);
tmpvar.type = ASN_OBJECT_ID;
if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
memcpy(enttrapoid, pdu->enterprise, sizeof(oid) * enttraplen);
if (enttrapoid[enttraplen - 1] != 0)
enttrapoid[enttraplen++] = 0;
enttrapoid[enttraplen++] = pdu->specific_type;
tmpvar.val.objid = enttrapoid;
tmpvar.val_len = enttraplen * sizeof(oid);
} else {
trapoids[9] = pdu->trap_type + 1;
tmpvar.val.objid = trapoids;
tmpvar.val_len = 10 * sizeof(oid);
}
fprint_variable(file, snmptrapoid,
sizeof(snmptrapoid) / sizeof(oid), &tmpvar);
}
/*
* do the variables in the pdu
*/
for (vars = pdu->variables; vars; vars = vars->next_variable) {
fprint_variable(file, vars->name, vars->name_length, vars);
}
if (pdu->command == SNMP_MSG_TRAP) {
/*
* convert a v1 trap to a v2 variable binding list:
* The enterprise goes last.
*/
tmpvar.val.string = pdu->agent_addr;
tmpvar.val_len = 4;
tmpvar.type = ASN_IPADDRESS;
fprint_variable(file, snmptrapaddr,
sizeof(snmptrapaddr) / sizeof(oid), &tmpvar);
tmpvar.val.string = pdu->community;
tmpvar.val_len = pdu->community_len;
tmpvar.type = ASN_OCTET_STR;
fprint_variable(file, snmptrapcom,
sizeof(snmptrapcom) / sizeof(oid), &tmpvar);
tmpvar.val.objid = pdu->enterprise;
tmpvar.val_len = pdu->enterprise_length * sizeof(oid);
tmpvar.type = ASN_OBJECT_ID;
fprint_variable(file, snmptrapent,
sizeof(snmptrapent) / sizeof(oid), &tmpvar);
}
}
void
do_external(char *cmd,
struct hostent *host,
netsnmp_pdu *pdu, netsnmp_transport *transport)
{
FILE *file;
int oldquick, result;
DEBUGMSGTL(("snmptrapd", "Running: %s\n", cmd));
oldquick = snmp_get_quick_print();
snmp_set_quick_print(1);
if (cmd) {
#ifndef WIN32
int fd[2];
int pid;
if (pipe(fd)) {
snmp_log_perror("pipe");
}
if ((pid = fork()) == 0) {
/*
* child
*/
close(0);
if (dup(fd[0]) != 0) {
snmp_log_perror("dup");
}
close(fd[1]);
close(fd[0]);
system(cmd);
exit(0);
} else if (pid > 0) {
file = fdopen(fd[1], "w");
send_handler_data(file, host, pdu, transport);
fclose(file);
close(fd[0]);
close(fd[1]);
if (waitpid(pid, &result, 0) < 0) {
snmp_log_perror("waitpid");
}
} else {
snmp_log_perror("fork");
}
#else
char command_buf[128];
char file_buf[L_tmpnam];
tmpnam(file_buf);
file = fopen(file_buf, "w");
if (!file) {
fprintf(stderr, "fopen: %s: %s\n", file_buf, strerror(errno));
} else {
send_handler_data(file, host, pdu, transport);
fclose(file);
sprintf(command_buf, "%s < %s", cmd, file_buf);
result = system(command_buf);
if (result == -1)
fprintf(stderr, "system: %s: %s\n", command_buf,
strerror(errno));
else if (result)
fprintf(stderr, "system: %s: %d\n", command_buf, result);
remove(file_buf);
}
#endif /* WIN32 */
}
snmp_set_quick_print(oldquick);
}
int
snmp_input(int op,
netsnmp_session * session,
int reqid, netsnmp_pdu *pdu, void *magic)
{
netsnmp_variable_list *vars = NULL;
netsnmp_transport *transport = (netsnmp_transport *) magic;
char buf[64], *cp;
netsnmp_pdu *reply;
struct hostent *host = NULL;
static oid trapoids[10] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 };
static oid snmptrapoid2[11] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
netsnmp_variable_list tmpvar;
char *Command = NULL, *tstr = NULL;
u_char *rbuf = NULL;
size_t r_len = 64, o_len = 0;
int trunc = 0;
tmpvar.type = ASN_OBJECT_ID;
if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
if ((rbuf = (u_char *) calloc(r_len, 1)) == NULL) {
snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n");
return 1;
}
if (pdu->command == SNMP_MSG_TRAP) {
oid trapOid[MAX_OID_LEN + 2] = { 0 };
int trapOidLen = pdu->enterprise_length;
num_received++;
if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_APP_NUMERIC_IP)) {
host = gethostbyaddr((char *) pdu->agent_addr, 4, AF_INET);
}
if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
memcpy(trapOid, pdu->enterprise, sizeof(oid) * trapOidLen);
if (trapOid[trapOidLen - 1] != 0) {
trapOid[trapOidLen++] = 0;
}
trapOid[trapOidLen++] = pdu->specific_type;
}
if (Print
&& (pdu->trap_type != SNMP_TRAP_AUTHFAIL
|| dropauth == 0)) {
if ((trap1_fmt_str == NULL) || (trap1_fmt_str[0] == '\0')) {
trunc =
!realloc_format_plain_trap(&rbuf, &r_len, &o_len,
1, pdu, transport);
} else {
trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
trap1_fmt_str, pdu,
transport);
}
snmp_log(LOG_INFO, "%s%s", rbuf,
(trunc ? " [TRUNCATED]\n" : ""));
}
if (Syslog
&& (pdu->trap_type != SNMP_TRAP_AUTHFAIL
|| dropauth == 0)) {
memset(rbuf, 0, o_len);
o_len = 0;
rbuf[o_len++] = ',';
rbuf[o_len++] = ' ';
for (vars = pdu->variables; vars;
vars = vars->next_variable) {
trunc =
!sprint_realloc_variable(&rbuf, &r_len, &o_len, 1,
vars->name,
vars->name_length, vars);
if (!trunc) {
/*
* Add a trailing , ...
*/
trunc =
!snmp_strcat(&rbuf, &r_len, &o_len, 1, ", ");
}
if (trunc) {
break;
}
}
if (o_len > 0 && !trunc) {
o_len -= 2;
rbuf[o_len] = '\0';
}
if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
u_char *oidbuf = NULL;
size_t ob_len = 64, oo_len = 0;
int otrunc = 0;
if ((oidbuf = (u_char *) calloc(ob_len, 1)) == NULL) {
snmp_log(LOG_ERR,
"couldn't display trap -- malloc failed\n");
free(rbuf);
return 1;
}
otrunc =
!sprint_realloc_objid(&oidbuf, &ob_len, &oo_len, 1,
trapOid, trapOidLen);
if (!otrunc) {
cp = strrchr((char *) oidbuf, '.');
if (cp != NULL) {
cp++;
} else {
cp = (char *) oidbuf;
}
} else {
cp = (char *) oidbuf;
}
snmp_log(LOG_WARNING,
"%s: %s Trap (%s%s) Uptime: %s%s%s",
inet_ntoa(*
((struct in_addr *) pdu->
agent_addr)),
trap_description(pdu->trap_type), cp,
(otrunc ? " [TRUNCATED]" : ""),
uptime_string(pdu->time, buf), rbuf,
(trunc ? " [TRUNCATED]\n" : ""));
free(oidbuf);
} else {
snmp_log(LOG_WARNING,
"%s: %s Trap (%ld) Uptime: %s%s%s",
inet_ntoa(*
((struct in_addr *) pdu->
agent_addr)),
trap_description(pdu->trap_type),
pdu->specific_type, uptime_string(pdu->time,
buf), rbuf,
(trunc ? " [TRUNCATED]\n" : ""));
}
}
if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
Command = snmptrapd_get_traphandler(trapOid, trapOidLen);
} else {
trapoids[9] = pdu->trap_type + 1;
Command = snmptrapd_get_traphandler(trapoids, 10);
}
if (Command) {
do_external(Command, host, pdu, transport);
}
log_notification(host, pdu, transport);
} else if (pdu->command == SNMP_MSG_TRAP2 ||
pdu->command == SNMP_MSG_INFORM) {
num_received++;
if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_APP_NUMERIC_IP)) {
/*
* Right, apparently a name lookup is wanted. This is only
* reasonable for the UDP and TCP transport domains (we
* don't want to try to be too clever here).
*/
#ifdef SNMP_TRANSPORT_TCP_DOMAIN
if (transport != NULL
&& (transport->domain == netsnmpUDPDomain
|| transport->domain == netsnmp_snmpTCPDomain)) {
#else
if (transport != NULL
&& transport->domain == netsnmpUDPDomain) {
#endif
/*
* This is kind of bletcherous -- it breaks the opacity of
* transport_data but never mind -- the alternative is a lot of
* munging strings from f_fmtaddr.
*/
struct sockaddr_in *addr =
(struct sockaddr_in *) pdu->transport_data;
if (addr != NULL &&
pdu->transport_data_length ==
sizeof(struct sockaddr_in)) {
host =
gethostbyaddr((char *) &(addr->sin_addr),
sizeof(struct in_addr), AF_INET);
}
}
}
if (Print) {
if (trap2_fmt_str == NULL || trap2_fmt_str[0] == '\0') {
trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
trap2_std_str, pdu,
transport);
} else {
trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
trap2_fmt_str, pdu,
transport);
}
snmp_log(LOG_INFO, "%s%s", rbuf,
(trunc ? " [TRUNCATED]" : ""));
}
if (Syslog) {
memset(rbuf, 0, o_len);
o_len = 0;
for (vars = pdu->variables; vars;
vars = vars->next_variable) {
trunc =
!sprint_realloc_variable(&rbuf, &r_len, &o_len, 1,
vars->name,
vars->name_length, vars);
if (!trunc) {
trunc =
!snmp_strcat(&rbuf, &r_len, &o_len, 1, ", ");
}
if (trunc) {
break;
}
}
if (o_len > 0 && !trunc) {
o_len -= 2;
rbuf[o_len] = '\0';
}
if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_APP_NUMERIC_IP)) {
/*
* Right, apparently a name lookup is wanted. This is only
* reasonable for the UDP and TCP transport domains (we
* don't want to try to be too clever here).
*/
#ifdef SNMP_TRANSPORT_TCP_DOMAIN
if (transport != NULL
&& (transport->domain == netsnmpUDPDomain
|| transport->domain ==
netsnmp_snmpTCPDomain)) {
#else
if (transport != NULL
&& transport->domain == netsnmpUDPDomain) {
#endif
/*
* This is kind of bletcherous -- it breaks the opacity
* of transport_data but never mind -- the alternative is
* a lot of munging strings from f_fmtaddr.
*/
struct sockaddr_in *addr =
(struct sockaddr_in *) pdu->transport_data;
if (addr != NULL && pdu->transport_data_length ==
sizeof(struct sockaddr_in)) {
host =
gethostbyaddr((char *) &(addr->sin_addr),
sizeof(struct in_addr),
AF_INET);
}
}
}
if (transport != NULL && transport->f_fmtaddr != NULL) {
tstr =
transport->f_fmtaddr(transport,
pdu->transport_data,
pdu->transport_data_length);
snmp_log(LOG_WARNING, "%s [%s]: Trap %s%s\n",
host ? host->h_name : tstr, tstr, rbuf,
(trunc ? "[TRUNCATED]" : ""));
free(tstr);
} else {
snmp_log(LOG_WARNING, "<UNKNOWN>: Trap %s%s", rbuf,
(trunc ? "[TRUNCATED]\n" : ""));
}
}
if (Event) {
event_input(pdu->variables);
}
for (vars = pdu->variables; (vars != NULL) &&
snmp_oid_compare(vars->name, vars->name_length,
snmptrapoid2,
sizeof(snmptrapoid2) / sizeof(oid));
vars = vars->next_variable);
if (vars && vars->type == ASN_OBJECT_ID) {
Command = snmptrapd_get_traphandler(vars->val.objid,
vars->val_len /
sizeof(oid));
if (Command) {
do_external(Command, host, pdu, transport);
}
}
log_notification(host, pdu, transport);
if (pdu->command == SNMP_MSG_INFORM) {
if ((reply =
snmp_clone_pdu2(pdu, SNMP_MSG_RESPONSE)) == NULL) {
snmp_log(LOG_ERR,
"couldn't clone PDU for INFORM response\n");
} else {
reply->errstat = 0;
reply->errindex = 0;
if (!snmp_send(session, reply)) {
snmp_sess_perror
("snmptrapd: Couldn't respond to inform pdu",
session);
snmp_free_pdu(reply);
}
}
}
}
if (rbuf != NULL) {
free(rbuf);
}
} else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) {
fprintf(stderr, "Timeout: This shouldn't happen!\n");
}
return 1;
}
static void
parse_trap1_fmt(const char *token, char *line)
{
trap1_fmt_str = strdup(line);
}
static void
free_trap1_fmt(void)
{
if (trap1_fmt_str && trap1_fmt_str != trap1_std_str)
free((char *) trap1_fmt_str);
trap1_fmt_str = NULL;
}
static void
parse_trap2_fmt(const char *token, char *line)
{
trap2_fmt_str = strdup(line);
}
static void
free_trap2_fmt(void)
{
if (trap2_fmt_str && trap2_fmt_str != trap2_std_str)
free((char *) trap2_fmt_str);
trap2_fmt_str = NULL;
}
void
usage(void)
{
fprintf(stderr, "Usage: snmptrapd [OPTIONS] [LISTENING ADDRESSES]\n");
fprintf(stderr, "\n\tNET-SNMP Version: %s\n", netsnmp_get_version());
fprintf(stderr, "\tWeb: http://www.net-snmp.org/\n");
fprintf(stderr, "\tEmail: net-snmp-coders@lists.sourceforge.net\n");
fprintf(stderr, "\n");
fprintf(stderr, " -a\t\t\tignore authentication failture traps\n");
fprintf(stderr, " -c FILE\t\tread FILE as a configuration file\n");
fprintf(stderr,
" -C\t\t\tdo not read the default configuration files\n");
fprintf(stderr, " -d\t\t\tdump sent and received SNMP packets\n");
fprintf(stderr, " -D\t\t\tturn on debugging output\n");
fprintf(stderr,
" -e\t\t\tprint event # (rising/falling alarm, etc.)\n");
fprintf(stderr, " -f\t\t\tdo not fork from the shell\n");
fprintf(stderr,
" -F FORMAT\t\tuse specified format for logging to standard error\n");
fprintf(stderr, " -h, --help\t\tdisplay this usage message\n");
fprintf(stderr,
" -H\t\t\tdisplay configuration file directives understood\n");
fprintf(stderr,
" -l d|0-7\t\tset syslog facility to LOG_DAEMON (d)\n\t\t\t or LOG_LOCAL[0-7] (default LOG_LOCAL0)\n");
fprintf(stderr,
" -m MIBLIST\t\tuse MIBLIST instead of the default MIB list\n");
fprintf(stderr,
" -M DIRLIST\t\tuse DIRLIST as the list of locations\n\t\t\t to look for MIBs\n");
fprintf(stderr,
" -n\t\t\tuse numeric addresses instead of attempting\n\t\t\t hostname lookups (no DNS)\n");
fprintf(stderr, " -o FILE\t\toutput to FILE\n");
fprintf(stderr, " -P\t\t\tprint to standard error\n");
fprintf(stderr, " -s\t\t\tlog to syslog\n");
#if HAVE_GETPID
fprintf(stderr, " -u FILE\t\tstore process id in FILE\n");
#endif
fprintf(stderr, " -v, --version\t\tdisplay version information\n");
fprintf(stderr,
" -O <OUTOPTS>\t\ttoggle options controlling output display\n");
snmp_out_toggle_options_usage("\t\t\t", stderr);
}
static void
version(void)
{
printf("\nNET-SNMP Version: %s\n", netsnmp_get_version());
printf("Web: http://www.net-snmp.org/\n");
printf("Email: net-snmp-coders@lists.sourceforge.net\n\n");
exit(0);
}
RETSIGTYPE
term_handler(int sig)
{
running = 0;
}
#ifdef SIGHUP
RETSIGTYPE
hup_handler(int sig)
{
reconfig = 1;
signal(SIGHUP, hup_handler);
}
#endif
static
int
pre_parse(netsnmp_session * session, netsnmp_transport *transport,
void *transport_data, int transport_data_length)
{
#if USE_LIBWRAP
char *addr_string = NULL;
if (transport != NULL && transport->f_fmtaddr != NULL) {
/*
* Okay I do know how to format this address for logging.
*/
addr_string = transport->f_fmtaddr(transport, transport_data,
transport_data_length);
/*
* Don't forget to free() it.
*/
}
if (addr_string != NULL) {
if (hosts_ctl("snmptrapd", STRING_UNKNOWN,
addr_string, STRING_UNKNOWN) == 0) {
free(addr_string);
return 0;
}
free(addr_string);
} else {
if (hosts_ctl("snmptrapd", STRING_UNKNOWN,
STRING_UNKNOWN, STRING_UNKNOWN) == 0) {
return 0;
}
}
#endif /* USE_LIBWRAP */
return 1;
}
static netsnmp_session *
snmptrapd_add_session(netsnmp_transport *t)
{
netsnmp_session sess, *session = &sess, *rc = NULL;
snmp_sess_init(session);
session->peername = SNMP_DEFAULT_PEERNAME; /* Original code had NULL here */
session->version = SNMP_DEFAULT_VERSION;
session->community_len = SNMP_DEFAULT_COMMUNITY_LEN;
session->retries = SNMP_DEFAULT_RETRIES;
session->timeout = SNMP_DEFAULT_TIMEOUT;
session->callback = snmp_input;
session->callback_magic = (void *) t;
session->authenticator = NULL;
sess.isAuthoritative = SNMP_SESS_UNKNOWNAUTH;
rc = snmp_add(session, t, pre_parse, NULL);
if (rc == NULL) {
snmp_sess_perror("snmptrapd", session);
}
return rc;
}
static void
snmptrapd_close_sessions(netsnmp_session * sess_list)
{
netsnmp_session *s = NULL, *next = NULL;
for (s = sess_list; s != NULL; s = next) {
next = s->next;
snmp_close(s);
}
}
int
main(int argc, char *argv[])
{
char options[128] = "ac:CdD::efF:hHl:m:M:no:PqsSvO:-:";
netsnmp_session *sess_list = NULL, *ss = NULL;
netsnmp_transport *transport = NULL;
int arg, i = 0;
int count, numfds, block;
fd_set fdset;
struct timeval timeout, *tvp;
int dofork = 1;
char *cp, *listen_ports = NULL;
char *trap1_fmt_str_remember = NULL;
int agentx_subagent = 1;
#if HAVE_GETPID
FILE *PID;
char *pid_file = NULL;
#endif
/*
* register our configuration handlers now so -H properly displays them
*/
register_config_handler("snmptrapd", "traphandle",
snmptrapd_traphandle, NULL,
"oid|\"default\" program [args ...] ");
register_config_handler("snmptrapd", "createUser",
usm_parse_create_usmUser, NULL,
"username (MD5|SHA) passphrase [DES passphrase]");
register_config_handler("snmptrapd", "usmUser",
usm_parse_config_usmUser, NULL, NULL);
register_config_handler("snmptrapd", "format1",
parse_trap1_fmt, free_trap1_fmt, "format");
register_config_handler("snmptrapd", "format2",
parse_trap2_fmt, free_trap2_fmt, "format");
/*
* we need to be called back later
*/
snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
usm_store_users, NULL);
#ifdef WIN32
setvbuf(stdout, NULL, _IONBF, BUFSIZ);
#else
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
#endif
/*
* Add some options if they are available.
*/
#if HAVE_GETPID
strcat(options, "u:");
#endif
/*
* Now process options normally.
*/
while ((arg = getopt(argc, argv, options)) != EOF) {
switch (arg) {
case '-':
if (strcasecmp(optarg, "help") == 0) {
usage();
exit(0);
}
if (strcasecmp(optarg, "version") == 0) {
version();
exit(0);
}
handle_long_opt(optarg);
break;
case 'a':
dropauth = 1;
break;
case 'c':
if (optarg != NULL) {
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_OPTIONALCONFIG, optarg);
} else {
usage();
exit(1);
}
break;
case 'C':
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1);
break;
case 'd':
snmp_set_dump_packet(1);
break;
case 'D':
debug_register_tokens(optarg);
snmp_set_do_debugging(1);
break;
case 'e':
Event++;
break;
case 'f':
dofork = 0;
break;
case 'F':
if (optarg != NULL) {
trap1_fmt_str_remember = optarg;
} else {
usage();
exit(1);
}
break;
case 'h':
usage();
exit(0);
case 'H':
init_notification_log();
init_snmp("snmptrapd");
fprintf(stderr, "Configuration directives understood:\n");
read_config_print_usage(" ");
exit(0);
case 'l':
if (optarg != NULL) {
switch (*optarg) {
case 'd':
case 'D':
Facility = LOG_DAEMON;
break;
case '0':
Facility = LOG_LOCAL0;
break;
case '1':
Facility = LOG_LOCAL1;
break;
case '2':
Facility = LOG_LOCAL2;
break;
case '3':
Facility = LOG_LOCAL3;
break;
case '4':
Facility = LOG_LOCAL4;
break;
case '5':
Facility = LOG_LOCAL5;
break;
case '6':
Facility = LOG_LOCAL6;
break;
case '7':
Facility = LOG_LOCAL7;
break;
default:
fprintf(stderr, "invalid syslog facility: -l %c\n",
*optarg);
usage();
exit(1);
}
} else {
fprintf(stderr, "no syslog facility specified\n");
usage();
exit(1);
}
break;
case 'm':
if (optarg != NULL) {
setenv("MIBS", optarg, 1);
} else {
usage();
exit(1);
}
break;
case 'M':
if (optarg != NULL) {
setenv("MIBDIRS", optarg, 1);
} else {
usage();
exit(1);
}
break;
case 'n':
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_APP_NUMERIC_IP, 1);
break;
case 'o':
Print++;
if (optarg != NULL) {
logfile = optarg;
snmp_enable_filelog(optarg, 0);
} else {
usage();
exit(1);
}
break;
case 'O':
cp = snmp_out_toggle_options(optarg);
if (cp != NULL) {
fprintf(stderr, "Unknown output option passed to -O: %c\n",
*cp);
usage();
exit(1);
}
break;
case 'P':
dofork = 0;
snmp_enable_stderrlog();
Print++;
break;
case 'q':
fprintf(stderr,
"Warning: -q option is deprecated - use -Oq\n");
snmp_set_quick_print(1);
break;
case 's':
Syslog++;
break;
case 'S':
fprintf(stderr,
"Warning: -S option is deprecated - use -OS\n");
snmp_set_suffix_only(2);
break;
#if HAVE_GETPID
case 'u':
if (optarg != NULL) {
pid_file = optarg;
} else {
usage();
exit(1);
}
break;
#endif
break;
case 'v':
version();
exit(0);
break;
default:
fprintf(stderr, "invalid option: -%c\n", arg);
usage();
exit(1);
break;
}
}
if (optind < argc) {
/*
* There are optional transport addresses on the command line.
*/
for (i = optind; i < argc; i++) {
char *astring;
if (listen_ports != NULL) {
astring =
malloc(strlen(listen_ports) + 2 + strlen(argv[i]));
if (astring == NULL) {
fprintf(stderr, "malloc failure processing argv[%d]\n",
i);
exit(1);
}
sprintf(astring, "%s,%s", listen_ports, argv[i]);
free(listen_ports);
listen_ports = astring;
} else {
listen_ports = strdup(argv[i]);
if (listen_ports == NULL) {
fprintf(stderr, "malloc failure processing argv[%d]\n",
i);
exit(1);
}
}
}
} else {
listen_ports = default_port;
}
if (!Print) {
Syslog = 1;
}
#ifdef USING_AGENTX_SUBAGENT_MODULE
/*
* we're an agentx subagent?
*/
if (agentx_subagent) {
/*
* make us a agentx client.
*/
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_ROLE, 1);
}
#endif
/*
* don't fail if we can't do agentx (ie, socket not there, or not root)
*/
netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_NO_ROOT_ACCESS);
/*
* initialize the agent library
*/
init_agent("snmptrapd");
/*
* initialize local modules
*/
if (agentx_subagent) {
#ifdef USING_AGENTX_SUBAGENT_MODULE
init_subagent();
#endif
init_notification_log();
}
/*
* Initialize the world. Create initial user
*/
init_snmp("snmptrapd");
if (trap1_fmt_str_remember) {
free_trap1_fmt();
free_trap2_fmt();
trap1_fmt_str = strdup(trap1_fmt_str_remember);
trap2_fmt_str = strdup(trap1_fmt_str_remember);
}
if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_QUIT_IMMEDIATELY)) {
/*
* just starting up to process specific configuration and then
* shutting down immediately.
*/
running = 0;
}
#ifndef WIN32
/*
* fork the process to the background if we are not printing to stderr
*/
if (dofork && running) {
int fd;
switch (fork()) {
case -1:
fprintf(stderr, "bad fork - %s\n", strerror(errno));
_exit(1);
case 0:
/*
* become process group leader
*/
if (setsid() == -1) {
fprintf(stderr, "bad setsid - %s\n", strerror(errno));
_exit(1);
}
/*
* if we are forked, we don't want to print out to stdout or stderr
*/
fd = open("/dev/null", O_RDWR);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
break;
default:
_exit(0);
}
}
#endif /* WIN32 */
#if HAVE_GETPID
if (pid_file != NULL) {
if ((PID = fopen(pid_file, "w")) == NULL) {
snmp_log_perror("fopen");
exit(1);
}
fprintf(PID, "%d\n", (int) getpid());
fclose(PID);
}
#endif
if (Syslog) {
snmp_enable_syslog_ident("snmptrapd");
snmp_log(LOG_INFO, "Starting snmptrapd %s\n",
netsnmp_get_version());
}
if (Print) {
struct tm *tm;
time_t timer;
time(&timer);
tm = localtime(&timer);
snmp_log(LOG_INFO,
"%.4d-%.2d-%.2d %.2d:%.2d:%.2d NET-SNMP version %s Started.\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
netsnmp_get_version());
}
SOCK_STARTUP;
cp = listen_ports;
while (cp != NULL) {
char *sep = strchr(cp, ',');
if (sep != NULL) {
*sep = 0;
}
transport = netsnmp_tdomain_transport(cp, 1, "udp");
if (transport == NULL) {
snmp_log(LOG_ERR, "couldn't open %s -- errno %d (\"%s\")\n",
cp, errno, strerror(errno));
snmptrapd_close_sessions(sess_list);
SOCK_CLEANUP;
exit(1);
} else {
ss = snmptrapd_add_session(transport);
if (ss == NULL) {
/*
* Shouldn't happen? We have already opened the transport
* successfully so what could have gone wrong?
*/
snmptrapd_close_sessions(sess_list);
netsnmp_transport_free(transport);
if (Syslog) {
snmp_log(LOG_ERR, "couldn't open snmp - %m");
}
SOCK_CLEANUP;
exit(1);
} else {
ss->next = sess_list;
sess_list = ss;
}
}
/*
* Process next listen address, if there is one.
*/
if (sep != NULL) {
*sep = ',';
cp = sep + 1;
} else {
cp = NULL;
}
}
signal(SIGTERM, term_handler);
#ifdef SIGHUP
signal(SIGHUP, hup_handler);
#endif
signal(SIGINT, term_handler);
while (running) {
if (reconfig) {
if (Print) {
struct tm *tm;
time_t timer;
time(&timer);
tm = localtime(&timer);
printf
("%.4d-%.2d-%.2d %.2d:%.2d:%.2d NET-SNMP version %s Reconfigured.\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
netsnmp_get_version());
}
if (Syslog)
snmp_log(LOG_INFO, "Snmptrapd reconfiguring");
trapd_update_config();
if (trap1_fmt_str_remember) {
free_trap1_fmt();
trap1_fmt_str = strdup(trap1_fmt_str_remember);
}
reconfig = 0;
}
numfds = 0;
FD_ZERO(&fdset);
block = 0;
tvp = &timeout;
timerclear(tvp);
tvp->tv_sec = 5;
snmp_select_info(&numfds, &fdset, tvp, &block);
if (block == 1)
tvp = NULL; /* block without timeout */
count = select(numfds, &fdset, 0, 0, tvp);
gettimeofday(&Now, 0);
if (count > 0) {
snmp_read(&fdset);
} else
switch (count) {
case 0:
snmp_timeout();
break;
case -1:
if (errno == EINTR)
continue;
snmp_log_perror("select");
running = 0;
break;
default:
fprintf(stderr, "select returned %d\n", count);
running = 0;
}
}
if (Print) {
struct tm *tm;
time_t timer;
time(&timer);
tm = localtime(&timer);
printf
("%.4d-%.2d-%.2d %.2d:%.2d:%.2d NET-SNMP version %s Stopped.\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
tm->tm_min, tm->tm_sec, netsnmp_get_version());
}
if (Syslog) {
snmp_log(LOG_INFO, "Stopping snmptrapd");
}
snmptrapd_close_sessions(sess_list);
snmp_shutdown("snmptrapd");
snmp_disable_log();
SOCK_CLEANUP;
return 0;
}
/*
* Read the configuration files. Implemented as a signal handler so that
* receipt of SIGHUP will cause configuration to be re-read when the
* trap deamon is running detatched from the console.
*
*/
void
trapd_update_config(void)
{
free_config();
read_configs();
}
#if !defined(HAVE_GETDTABLESIZE) && !defined(WIN32)
#include <sys/resource.h>
int
getdtablesize(void)
{
struct rlimit rl;
getrlimit(RLIMIT_NOFILE, &rl);
return (rl.rlim_cur);
}
#endif