snmptrapd_handlers.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. #include "net-snmp/net-snmp-config.h"
  2. #include "net-snmp/net-snmp-features.h"
  3. #if HAVE_STDLIB_H
  4. #include <stdlib.h>
  5. #endif
  6. #if HAVE_UNISTD_H
  7. #include <unistd.h>
  8. #endif
  9. #include <stdio.h>
  10. #if HAVE_STRING_H
  11. #include <string.h>
  12. #else
  13. #include <strings.h>
  14. #endif
  15. #include <ctype.h>
  16. #include <sys/types.h>
  17. #if HAVE_ARPA_INET_H
  18. #include <arpa/inet.h>
  19. #endif
  20. #if HAVE_NETINET_IN_H
  21. #include <netinet/in.h>
  22. #endif
  23. #if HAVE_NETDB_H
  24. #include <netdb.h>
  25. #endif
  26. #if HAVE_SYS_WAIT_H
  27. #include <sys/wait.h>
  28. #endif
  29. extern "C" {
  30. #include "net-snmp/config_api.h"
  31. #include "net-snmp/output_api.h"
  32. #include "net-snmp/mib_api.h"
  33. #include "net-snmp/utilities.h"
  34. #include "net-snmp/net-snmp-includes.h"
  35. #include "net-snmp/agent/net-snmp-agent-includes.h"
  36. #include "net-snmp/agent/mibgroup/utilities/execute.h"
  37. #include "snmptrapd_handlers.h"
  38. #include "snmptrapd_auth.h"
  39. #include "snmptrapd_log.h"
  40. #include "net-snmp/agent/mibgroup/notification-log-mib/notification_log.h"
  41. netsnmp_feature_child_of(add_default_traphandler, snmptrapd);
  42. }
  43. char *syslog_format1 = NULL;
  44. char *syslog_format2 = NULL;
  45. char *print_format1 = NULL;
  46. char *print_format2 = NULL;
  47. char *exec_format1 = NULL;
  48. char *exec_format2 = NULL;
  49. int SyslogTrap = 0;
  50. int dropauth = 0;
  51. 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";
  52. const char *trap2_std_str = "%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b]:\n%v\n";
  53. void snmptrapd_free_traphandle(void);
  54. const char *
  55. trap_description(int trap)
  56. {
  57. switch (trap) {
  58. case SNMP_TRAP_COLDSTART:
  59. return "Cold Start";
  60. case SNMP_TRAP_WARMSTART:
  61. return "Warm Start";
  62. case SNMP_TRAP_LINKDOWN:
  63. return "Link Down";
  64. case SNMP_TRAP_LINKUP:
  65. return "Link Up";
  66. case SNMP_TRAP_AUTHFAIL:
  67. return "Authentication Failure";
  68. case SNMP_TRAP_EGPNEIGHBORLOSS:
  69. return "EGP Neighbor Loss";
  70. case SNMP_TRAP_ENTERPRISESPECIFIC:
  71. return "Enterprise Specific";
  72. default:
  73. return "Unknown Type";
  74. }
  75. }
  76. void
  77. snmptrapd_parse_traphandle(const char *token, char *line)
  78. {
  79. char buf[STRINGMAX];
  80. oid obuf[MAX_OID_LEN];
  81. size_t olen = MAX_OID_LEN;
  82. char *cptr, *cp;
  83. netsnmp_trapd_handler *traph;
  84. int flags = 0;
  85. char *format = NULL;
  86. memset(buf, 0, sizeof(buf));
  87. memset(obuf, 0, sizeof(obuf));
  88. cptr = copy_nword(line, buf, sizeof(buf));
  89. if (buf[0] == '-' && buf[1] == 'F') {
  90. cptr = copy_nword(cptr, buf, sizeof(buf));
  91. format = strdup(buf);
  92. cptr = copy_nword(cptr, buf, sizeof(buf));
  93. }
  94. if (!cptr) {
  95. netsnmp_config_error("Missing traphandle command (%s)", buf);
  96. free(format);
  97. return;
  98. }
  99. DEBUGMSGTL(("read_config:traphandle", "registering handler for: "));
  100. if (!strcmp(buf, "default")) {
  101. DEBUGMSG(("read_config:traphandle", "default"));
  102. traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER,
  103. command_handler);
  104. }
  105. else {
  106. cp = buf + strlen(buf) - 1;
  107. if (*cp == '*') {
  108. flags |= NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE;
  109. *(cp--) = '\0';
  110. if (*cp == '.') {
  111. /*
  112. * Distinguish between 'oid.*' & 'oid*'
  113. */
  114. flags |= NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE;
  115. *(cp--) = '\0';
  116. }
  117. }
  118. if (!read_objid(buf, obuf, &olen)) {
  119. netsnmp_config_error("Bad trap OID in traphandle directive: %s",
  120. buf);
  121. free(format);
  122. return;
  123. }
  124. DEBUGMSGOID(("read_config:traphandle", obuf, olen));
  125. traph = netsnmp_add_traphandler(command_handler, obuf, olen);
  126. }
  127. DEBUGMSG(("read_config:traphandle", "\n"));
  128. if (traph) {
  129. traph->flags = flags;
  130. traph->authtypes = TRAP_AUTH_EXE;
  131. traph->token = strdup(cptr);
  132. if (format) {
  133. traph->format = format;
  134. format = NULL;
  135. }
  136. }
  137. free(format);
  138. }
  139. static void
  140. parse_forward(const char *token, char *line)
  141. {
  142. char buf[STRINGMAX];
  143. oid obuf[MAX_OID_LEN];
  144. size_t olen = MAX_OID_LEN;
  145. char *cptr, *cp;
  146. netsnmp_trapd_handler *traph;
  147. int flags = 0;
  148. char *format = NULL;
  149. memset(buf, 0, sizeof(buf));
  150. memset(obuf, 0, sizeof(obuf));
  151. cptr = copy_nword(line, buf, sizeof(buf));
  152. if (buf[0] == '-' && buf[1] == 'F') {
  153. cptr = copy_nword(cptr, buf, sizeof(buf));
  154. format = strdup(buf);
  155. cptr = copy_nword(cptr, buf, sizeof(buf));
  156. }
  157. DEBUGMSGTL(("read_config:forward", "registering forward for: "));
  158. if (!strcmp(buf, "default")) {
  159. DEBUGMSG(("read_config:forward", "default"));
  160. if (!strcmp(cptr, "agentx"))
  161. traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER,
  162. axforward_handler);
  163. else
  164. traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER,
  165. forward_handler);
  166. }
  167. else {
  168. cp = buf + strlen(buf) - 1;
  169. if (*cp == '*') {
  170. flags |= NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE;
  171. *(cp--) = '\0';
  172. if (*cp == '.') {
  173. /*
  174. * Distinguish between 'oid.*' & 'oid*'
  175. */
  176. flags |= NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE;
  177. *(cp--) = '\0';
  178. }
  179. }
  180. if (!read_objid(buf, obuf, &olen)) {
  181. netsnmp_config_error("Bad trap OID in forward directive: %s", buf);
  182. free(format);
  183. return;
  184. }
  185. DEBUGMSGOID(("read_config:forward", obuf, olen));
  186. if (!strcmp(cptr, "agentx"))
  187. traph = netsnmp_add_traphandler(axforward_handler, obuf, olen);
  188. else
  189. traph = netsnmp_add_traphandler(forward_handler, obuf, olen);
  190. }
  191. DEBUGMSG(("read_config:forward", "\n"));
  192. if (traph) {
  193. traph->flags = flags;
  194. traph->authtypes = TRAP_AUTH_NET;
  195. traph->token = strdup(cptr);
  196. if (format)
  197. traph->format = format;
  198. }
  199. else {
  200. free(format);
  201. }
  202. }
  203. void
  204. parse_format(const char *token, char *line)
  205. {
  206. char *cp, *sep;
  207. /*
  208. * Extract the first token from the value
  209. * which tells us which style of format this is
  210. */
  211. cp = line;
  212. while (*cp && !isspace((unsigned char)(*cp)))
  213. cp++;
  214. if (!(*cp)) {
  215. /*
  216. * If we haven't got anything left,
  217. * then this entry is malformed.
  218. * So report this, and give up
  219. */
  220. return;
  221. }
  222. sep = cp;
  223. *(cp++) = '\0';
  224. while (*cp && isspace((unsigned char)(*cp)))
  225. cp++;
  226. /*
  227. * OK - now "line" contains the format type,
  228. * and cp points to the actual format string.
  229. * So update the appropriate pointer(s).
  230. */
  231. if (!strcmp(line, "print1")) {
  232. SNMP_FREE(print_format1);
  233. print_format1 = strdup(cp);
  234. }
  235. else if (!strcmp(line, "print2")) {
  236. SNMP_FREE(print_format2);
  237. print_format2 = strdup(cp);
  238. }
  239. else if (!strcmp(line, "print")) {
  240. SNMP_FREE(print_format1);
  241. SNMP_FREE(print_format2);
  242. print_format1 = strdup(cp);
  243. print_format2 = strdup(cp);
  244. }
  245. else if (!strcmp(line, "syslog1")) {
  246. SNMP_FREE(syslog_format1);
  247. syslog_format1 = strdup(cp);
  248. }
  249. else if (!strcmp(line, "syslog2")) {
  250. SNMP_FREE(syslog_format2);
  251. syslog_format2 = strdup(cp);
  252. }
  253. else if (!strcmp(line, "syslog")) {
  254. SNMP_FREE(syslog_format1);
  255. SNMP_FREE(syslog_format2);
  256. syslog_format1 = strdup(cp);
  257. syslog_format2 = strdup(cp);
  258. }
  259. else if (!strcmp(line, "execute1")) {
  260. SNMP_FREE(exec_format1);
  261. exec_format1 = strdup(cp);
  262. }
  263. else if (!strcmp(line, "execute2")) {
  264. SNMP_FREE(exec_format2);
  265. exec_format2 = strdup(cp);
  266. }
  267. else if (!strcmp(line, "execute")) {
  268. SNMP_FREE(exec_format1);
  269. SNMP_FREE(exec_format2);
  270. exec_format1 = strdup(cp);
  271. exec_format2 = strdup(cp);
  272. }
  273. *sep = ' ';
  274. }
  275. static void
  276. parse_trap1_fmt(const char *token, char *line)
  277. {
  278. print_format1 = strdup(line);
  279. }
  280. void
  281. free_trap1_fmt(void)
  282. {
  283. if (print_format1 && print_format1 != trap1_std_str)
  284. free((char *)print_format1);
  285. print_format1 = NULL;
  286. }
  287. static void
  288. parse_trap2_fmt(const char *token, char *line)
  289. {
  290. print_format2 = strdup(line);
  291. }
  292. void
  293. free_trap2_fmt(void)
  294. {
  295. if (print_format2 && print_format2 != trap2_std_str)
  296. free((char *)print_format2);
  297. print_format2 = NULL;
  298. }
  299. void
  300. snmptrapd_register_configs(void)
  301. {
  302. register_config_handler("snmptrapd", "traphandle",
  303. snmptrapd_parse_traphandle,
  304. snmptrapd_free_traphandle,
  305. "oid|\"default\" program [args ...] ");
  306. register_config_handler("snmptrapd", "format1",
  307. parse_trap1_fmt, free_trap1_fmt, "format");
  308. register_config_handler("snmptrapd", "format2",
  309. parse_trap2_fmt, free_trap2_fmt, "format");
  310. register_config_handler("snmptrapd", "format",
  311. parse_format, NULL,
  312. "[print{,1,2}|syslog{,1,2}|execute{,1,2}] format");
  313. register_config_handler("snmptrapd", "forward",
  314. parse_forward, NULL, "OID|\"default\" destination");
  315. }
  316. /*-----------------------------
  317. *
  318. * Routines to implement a "registry" of trap handlers
  319. *
  320. *-----------------------------*/
  321. netsnmp_trapd_handler *netsnmp_auth_global_traphandlers = NULL;
  322. netsnmp_trapd_handler *netsnmp_pre_global_traphandlers = NULL;
  323. netsnmp_trapd_handler *netsnmp_post_global_traphandlers = NULL;
  324. netsnmp_trapd_handler *netsnmp_default_traphandlers = NULL;
  325. netsnmp_trapd_handler *netsnmp_specific_traphandlers = NULL;
  326. typedef struct netsnmp_handler_map_t {
  327. netsnmp_trapd_handler **handler;
  328. const char *descr;
  329. } netsnmp_handler_map;
  330. static netsnmp_handler_map handlers[] = {
  331. { &netsnmp_auth_global_traphandlers, "auth trap" },
  332. { &netsnmp_pre_global_traphandlers, "pre-global trap" },
  333. { NULL, "trap specific" },
  334. { &netsnmp_post_global_traphandlers, "global" },
  335. { NULL, NULL }
  336. };
  337. /*
  338. * Register a new "global" traphandler,
  339. * to be applied to *all* incoming traps
  340. */
  341. netsnmp_trapd_handler * netsnmp_add_global_traphandler(int list, Netsnmp_Trap_Handler *handler)
  342. {
  343. netsnmp_trapd_handler *traph;
  344. if (!handler)
  345. return NULL;
  346. traph = SNMP_MALLOC_TYPEDEF(netsnmp_trapd_handler);
  347. if (!traph)
  348. return NULL;
  349. /*
  350. * Add this new handler to the front of the appropriate list
  351. * (or should it go on the end?)
  352. */
  353. traph->handler = handler;
  354. traph->authtypes = TRAP_AUTH_ALL; /* callers will likely change this */
  355. switch (list) {
  356. case NETSNMPTRAPD_AUTH_HANDLER:
  357. traph->nexth = netsnmp_auth_global_traphandlers;
  358. netsnmp_auth_global_traphandlers = traph;
  359. break;
  360. case NETSNMPTRAPD_PRE_HANDLER:
  361. traph->nexth = netsnmp_pre_global_traphandlers;
  362. netsnmp_pre_global_traphandlers = traph;
  363. break;
  364. case NETSNMPTRAPD_POST_HANDLER:
  365. traph->nexth = netsnmp_post_global_traphandlers;
  366. netsnmp_post_global_traphandlers = traph;
  367. break;
  368. case NETSNMPTRAPD_DEFAULT_HANDLER:
  369. traph->nexth = netsnmp_default_traphandlers;
  370. netsnmp_default_traphandlers = traph;
  371. break;
  372. default:
  373. free(traph);
  374. return NULL;
  375. }
  376. return traph;
  377. }
  378. #ifndef NETSNMP_FEATURE_REMOVE_ADD_DEFAULT_TRAPHANDLER
  379. /*
  380. * Register a new "default" traphandler, to be applied to all
  381. * traps with no specific trap handlers of their own.
  382. */
  383. netsnmp_trapd_handler *
  384. netsnmp_add_default_traphandler(Netsnmp_Trap_Handler *handler) {
  385. return netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER,
  386. handler);
  387. }
  388. #endif /* NETSNMP_FEATURE_REMOVE_ADD_DEFAULT_TRAPHANDLER */
  389. /*
  390. * Register a new trap-specific traphandler
  391. */
  392. netsnmp_trapd_handler *
  393. netsnmp_add_traphandler(Netsnmp_Trap_Handler* handler,
  394. oid *trapOid, int trapOidLen) {
  395. netsnmp_trapd_handler *traph, *traph2;
  396. if (!handler)
  397. return NULL;
  398. traph = SNMP_MALLOC_TYPEDEF(netsnmp_trapd_handler);
  399. if (!traph)
  400. return NULL;
  401. /*
  402. * Populate this new handler with the trap information
  403. * (NB: the OID fields were not used in the default/global lists)
  404. */
  405. traph->authtypes = TRAP_AUTH_ALL; /* callers will likely change this */
  406. traph->handler = handler;
  407. traph->trapoid_len = trapOidLen;
  408. traph->trapoid = snmp_duplicate_objid(trapOid, trapOidLen);
  409. /*
  410. * Now try to find the appropriate place in the trap-specific
  411. * list for this particular trap OID. If there's a matching OID
  412. * already, then find it. Otherwise find the one that follows.
  413. * If we run out of entried, the new one should be tacked onto the end.
  414. */
  415. for (traph2 = netsnmp_specific_traphandlers;
  416. traph2; traph2 = traph2->nextt) {
  417. /* XXX - check this! */
  418. if (snmp_oid_compare(traph2->trapoid, traph2->trapoid_len,
  419. trapOid, trapOidLen) <= 0)
  420. break;
  421. }
  422. if (traph2) {
  423. /*
  424. * OK - We've either got an exact match, or we've found the
  425. * entry *after* where the new one should go.
  426. */
  427. if (!snmp_oid_compare(traph->trapoid, traph->trapoid_len,
  428. traph2->trapoid, traph2->trapoid_len)) {
  429. /*
  430. * Exact match, so find the end of the *handler* list
  431. * and tack on this new entry...
  432. */
  433. while (traph2->nexth)
  434. traph2 = traph2->nexth;
  435. traph2->nexth = traph;
  436. traph->nextt = traph2->nextt; /* Might as well... */
  437. traph->prevt = traph2->prevt;
  438. }
  439. else {
  440. /*
  441. * .. or the following entry, so insert the new one before it.
  442. */
  443. traph->prevt = traph2->prevt;
  444. if (traph2->prevt)
  445. traph2->prevt->nextt = traph;
  446. else
  447. netsnmp_specific_traphandlers = traph;
  448. traph2->prevt = traph;
  449. traph->nextt = traph2;
  450. }
  451. }
  452. else {
  453. /*
  454. * If we've run out of entries without finding a suitable spot,
  455. * the new one should be tacked onto the end.....
  456. */
  457. if (netsnmp_specific_traphandlers) {
  458. traph2 = netsnmp_specific_traphandlers;
  459. while (traph2->nextt)
  460. traph2 = traph2->nextt;
  461. traph2->nextt = traph;
  462. traph->prevt = traph2;
  463. }
  464. else {
  465. /*
  466. * .... unless this is the very first entry, of course!
  467. */
  468. netsnmp_specific_traphandlers = traph;
  469. }
  470. }
  471. return traph;
  472. }
  473. void
  474. snmptrapd_free_traphandle(void)
  475. {
  476. netsnmp_trapd_handler *traph = NULL, *nextt = NULL, *nexth = NULL;
  477. DEBUGMSGTL(("snmptrapd", "Freeing trap handler lists\n"));
  478. /*
  479. * Free default trap handlers
  480. */
  481. traph = netsnmp_default_traphandlers;
  482. /* loop over handlers */
  483. while (traph) {
  484. DEBUGMSG(("snmptrapd", "Freeing default trap handler\n"));
  485. nexth = traph->nexth;
  486. SNMP_FREE(traph->token);
  487. SNMP_FREE(traph);
  488. traph = nexth;
  489. }
  490. netsnmp_default_traphandlers = NULL;
  491. /*
  492. * Free specific trap handlers
  493. */
  494. traph = netsnmp_specific_traphandlers;
  495. /* loop over traps */
  496. while (traph) {
  497. nextt = traph->nextt;
  498. /* loop over handlers for this trap */
  499. while (traph) {
  500. DEBUGMSG(("snmptrapd", "Freeing specific trap handler\n"));
  501. nexth = traph->nexth;
  502. SNMP_FREE(traph->token);
  503. SNMP_FREE(traph->trapoid);
  504. SNMP_FREE(traph);
  505. traph = nexth;
  506. }
  507. traph = nextt;
  508. }
  509. netsnmp_specific_traphandlers = NULL;
  510. }
  511. /*
  512. * Locate the list of handlers for this particular Trap OID
  513. * Returns NULL if there are no relevant traps
  514. */
  515. netsnmp_trapd_handler *
  516. netsnmp_get_traphandler(oid *trapOid, int trapOidLen) {
  517. netsnmp_trapd_handler *traph;
  518. if (!trapOid || !trapOidLen) {
  519. DEBUGMSGTL(("snmptrapd:lookup", "get_traphandler no OID!\n"));
  520. return NULL;
  521. }
  522. DEBUGMSGTL(("snmptrapd:lookup", "Looking up Trap OID: "));
  523. DEBUGMSGOID(("snmptrapd:lookup", trapOid, trapOidLen));
  524. DEBUGMSG(("snmptrapd:lookup", "\n"));
  525. /*
  526. * Look for a matching OID, and return that list...
  527. */
  528. for (traph = netsnmp_specific_traphandlers;
  529. traph; traph = traph->nextt) {
  530. /*
  531. * If the trap handler wasn't wildcarded, then the trapOID
  532. * should match the registered OID exactly.
  533. */
  534. if (!(traph->flags & NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE)) {
  535. if (snmp_oid_compare(traph->trapoid, traph->trapoid_len,
  536. trapOid, trapOidLen) == 0) {
  537. DEBUGMSGTL(("snmptrapd:lookup",
  538. "get_traphandler exact match (%p)\n", traph));
  539. return traph;
  540. }
  541. }
  542. else {
  543. /*
  544. * If the trap handler *was* wildcarded, then the trapOID
  545. * should have the registered OID as a prefix...
  546. */
  547. if (snmp_oidsubtree_compare(traph->trapoid,
  548. traph->trapoid_len,
  549. trapOid, trapOidLen) == 0) {
  550. if (traph->flags & NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE) {
  551. /*
  552. * ... and (optionally) *strictly* as a prefix
  553. * i.e. not including an exact match.
  554. */
  555. if (snmp_oid_compare(traph->trapoid, traph->trapoid_len,
  556. trapOid, trapOidLen) != 0) {
  557. DEBUGMSGTL(("snmptrapd:lookup", "get_traphandler strict subtree match (%p)\n", traph));
  558. return traph;
  559. }
  560. }
  561. else {
  562. DEBUGMSGTL(("snmptrapd:lookup", "get_traphandler subtree match (%p)\n", traph));
  563. return traph;
  564. }
  565. }
  566. }
  567. }
  568. /*
  569. * .... or failing that, return the "default" list (which may be NULL)
  570. */
  571. DEBUGMSGTL(("snmptrapd:lookup", "get_traphandler default (%p)\n",
  572. netsnmp_default_traphandlers));
  573. return netsnmp_default_traphandlers;
  574. }
  575. /*-----------------------------
  576. *
  577. * Standard traphandlers for the common requirements
  578. *
  579. *-----------------------------*/
  580. #define SYSLOG_V1_STANDARD_FORMAT "%a: %W Trap (%q) Uptime: %#T%#v\n"
  581. #define SYSLOG_V1_ENTERPRISE_FORMAT "%a: %W Trap (%q) Uptime: %#T%#v\n" /* XXX - (%q) become (.N) ??? */
  582. #define SYSLOG_V23_NOTIFICATION_FORMAT "%B [%b]: Trap %#v\n" /* XXX - introduces a leading " ," */
  583. /*
  584. * Trap handler for logging via syslog
  585. */
  586. int syslog_handler(netsnmp_pdu *pdu,
  587. netsnmp_transport *transport,
  588. netsnmp_trapd_handler *handler)
  589. {
  590. u_char *rbuf = NULL;
  591. size_t r_len = 64, o_len = 0;
  592. int trunc = 0;
  593. DEBUGMSGTL(("snmptrapd", "syslog_handler\n"));
  594. if (SyslogTrap)
  595. return NETSNMPTRAPD_HANDLER_OK;
  596. if ((rbuf = (u_char *)calloc(r_len, 1)) == NULL) {
  597. snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n");
  598. return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */
  599. }
  600. /*
  601. * If there's a format string registered for this trap, then use it.
  602. */
  603. if (handler && handler->format) {
  604. DEBUGMSGTL(("snmptrapd", "format = '%s'\n", handler->format));
  605. if (*handler->format) {
  606. trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  607. handler->format, pdu, transport);
  608. }
  609. else {
  610. free(rbuf);
  611. return NETSNMPTRAPD_HANDLER_OK; /* A 0-length format string means don't log */
  612. }
  613. /*
  614. * Otherwise (i.e. a NULL handler format string),
  615. * use a standard output format setting
  616. * either configurable, or hardwired
  617. *
  618. * XXX - v1 traps use a different hardwired formats for
  619. * standard and enterprise specific traps
  620. * Do we actually need this?
  621. */
  622. }
  623. else {
  624. if (pdu->command == SNMP_MSG_TRAP) {
  625. if (syslog_format1) {
  626. DEBUGMSGTL(("snmptrapd", "syslog_format v1 = '%s'\n", syslog_format1));
  627. trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  628. syslog_format1, pdu, transport);
  629. }
  630. else if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
  631. DEBUGMSGTL(("snmptrapd", "v1 enterprise format\n"));
  632. trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  633. SYSLOG_V1_ENTERPRISE_FORMAT,
  634. pdu, transport);
  635. }
  636. else {
  637. DEBUGMSGTL(("snmptrapd", "v1 standard trap format\n"));
  638. trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  639. SYSLOG_V1_STANDARD_FORMAT,
  640. pdu, transport);
  641. }
  642. }
  643. else { /* SNMPv2/3 notifications */
  644. if (syslog_format2) {
  645. DEBUGMSGTL(("snmptrapd", "syslog_format v1 = '%s'\n", syslog_format2));
  646. trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  647. syslog_format2, pdu, transport);
  648. }
  649. else {
  650. DEBUGMSGTL(("snmptrapd", "v2/3 format\n"));
  651. trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  652. SYSLOG_V23_NOTIFICATION_FORMAT,
  653. pdu, transport);
  654. }
  655. }
  656. }
  657. snmp_log(LOG_WARNING, "%s%s", rbuf, (trunc ? " [TRUNCATED]\n" : ""));
  658. free(rbuf);
  659. return NETSNMPTRAPD_HANDLER_OK;
  660. }
  661. #define EXECUTE_FORMAT "%B\n%b\n%V\n%v\n"
  662. /*
  663. * Trap handler for invoking a suitable script
  664. */
  665. int command_handler(netsnmp_pdu *pdu,
  666. netsnmp_transport *transport,
  667. netsnmp_trapd_handler *handler)
  668. {
  669. #ifndef USING_UTILITIES_EXECUTE_MODULE
  670. NETSNMP_LOGONCE((LOG_WARNING,
  671. "support for run_shell_command not available\n"));
  672. return NETSNMPTRAPD_HANDLER_FAIL;
  673. #else
  674. u_char *rbuf = NULL;
  675. size_t r_len = 64, o_len = 0;
  676. int oldquick;
  677. DEBUGMSGTL(("snmptrapd", "command_handler\n"));
  678. DEBUGMSGTL(("snmptrapd", "token = '%s'\n", handler->token));
  679. if (handler && handler->token && *handler->token) {
  680. netsnmp_pdu *v2_pdu = NULL;
  681. if (pdu->command == SNMP_MSG_TRAP)
  682. v2_pdu = convert_v1pdu_to_v2(pdu);
  683. else
  684. v2_pdu = pdu;
  685. oldquick = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
  686. NETSNMP_DS_LIB_QUICK_PRINT);
  687. netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
  688. NETSNMP_DS_LIB_QUICK_PRINT, 1);
  689. /*
  690. * Format the trap and pass this string to the external command
  691. */
  692. if ((rbuf = (u_char *)calloc(r_len, 1)) == NULL) {
  693. snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n");
  694. return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */
  695. }
  696. /*
  697. * If there's a format string registered for this trap, then use it.
  698. * Otherwise use the standard execution format setting.
  699. */
  700. if (handler && handler->format && *handler->format) {
  701. DEBUGMSGTL(("snmptrapd", "format = '%s'\n", handler->format));
  702. realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  703. handler->format,
  704. v2_pdu, transport);
  705. }
  706. else {
  707. if (pdu->command == SNMP_MSG_TRAP && exec_format1) {
  708. DEBUGMSGTL(("snmptrapd", "exec v1 = '%s'\n", exec_format1));
  709. realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  710. exec_format1, pdu, transport);
  711. }
  712. else if (pdu->command != SNMP_MSG_TRAP && exec_format2) {
  713. DEBUGMSGTL(("snmptrapd", "exec v2/3 = '%s'\n", exec_format2));
  714. realloc_format_trap(&rbuf, &r_len, &o_len, 1,
  715. exec_format2, pdu, transport);
  716. }
  717. else {
  718. DEBUGMSGTL(("snmptrapd", "execute format\n"));
  719. realloc_format_trap(&rbuf, &r_len, &o_len, 1, EXECUTE_FORMAT,
  720. v2_pdu, transport);
  721. }
  722. }
  723. /*
  724. * and pass this formatted string to the command specified
  725. */
  726. run_shell_command(handler->token, (char*)rbuf, NULL, NULL); /* Not interested in output */
  727. netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
  728. NETSNMP_DS_LIB_QUICK_PRINT, oldquick);
  729. if (pdu->command == SNMP_MSG_TRAP)
  730. snmp_free_pdu(v2_pdu);
  731. free(rbuf);
  732. }
  733. return NETSNMPTRAPD_HANDLER_OK;
  734. #endif /* !def USING_UTILITIES_EXECUTE_MODULE */
  735. }
  736. /*
  737. * Trap handler for forwarding to the AgentX master agent
  738. */
  739. int axforward_handler(netsnmp_pdu *pdu,
  740. netsnmp_transport *transport,
  741. netsnmp_trapd_handler *handler)
  742. {
  743. send_v2trap(pdu->variables);
  744. return NETSNMPTRAPD_HANDLER_OK;
  745. }
  746. static int add_forwarder_info(netsnmp_pdu *pdu, netsnmp_pdu *pdu2)
  747. {
  748. netsnmp_indexed_addr_pair *addr_pair1;
  749. struct sockaddr_in *to1 = NULL;
  750. struct sockaddr_in *to2 = NULL;
  751. int last_snmpTrapAddress_index = -1;
  752. /* snmpTrapAddress_oid.0 is also defined as agentaddr_oid */
  753. const oid snmpTrapAddress_oid[] = { 1,3,6,1,6,3,18,1,3 };
  754. /* each forwarder will add this OID with changed last index */
  755. oid forwarder_oid[] = { 1,3,6,1,6,3,18,1,3,0 };
  756. const size_t snmpTrapAddress_oid_size = OID_LENGTH(snmpTrapAddress_oid);
  757. const size_t forwarder_oid_len = OID_LENGTH(forwarder_oid);
  758. struct in_addr agent_addr;
  759. struct in_addr my_ip_addr;
  760. memset(&agent_addr, 0, sizeof(agent_addr));
  761. memset(&my_ip_addr, 0, sizeof(my_ip_addr));
  762. if (pdu && pdu->transport_data &&
  763. pdu->transport_data_length == sizeof(*addr_pair1)) {
  764. addr_pair1 = (netsnmp_indexed_addr_pair *)pdu->transport_data;
  765. /*
  766. * Get the IPv4 address of the host that this trap was sent from =
  767. * last forwarder's IP address.
  768. */
  769. if (addr_pair1->remote_addr.sa.sa_family == AF_INET) {
  770. to1 = (struct sockaddr_in *)&(addr_pair1->remote_addr);
  771. agent_addr = to1->sin_addr;
  772. }
  773. /*
  774. * Get the IPv4 address of the host that this trap was sent to =
  775. * this forwarder's IP address.
  776. */
  777. if (addr_pair1->local_addr.sa.sa_family == AF_INET) {
  778. to2 = (struct sockaddr_in *)&(addr_pair1->local_addr);
  779. my_ip_addr = to2->sin_addr;
  780. }
  781. }
  782. if (to1) {
  783. netsnmp_variable_list *vblist = NULL;
  784. netsnmp_variable_list *var = NULL;
  785. if (*(in_addr_t *)pdu2->agent_addr == INADDR_ANY) {
  786. /*
  787. * there was no agent address defined in PDU. copy the forwarding
  788. * agent IP address from the transport socket.
  789. */
  790. *(struct in_addr *)pdu2->agent_addr = agent_addr;
  791. }
  792. vblist = pdu2->variables;
  793. /*
  794. * Iterate over all varbinds in the PDU to see if it already has any
  795. * forwarder information.
  796. */
  797. for (var = vblist; var; var = var->next_variable) {
  798. if (snmp_oid_ncompare(var->name, var->name_length,
  799. snmpTrapAddress_oid,
  800. snmpTrapAddress_oid_size,
  801. snmpTrapAddress_oid_size) == 0) {
  802. int my_snmpTrapAddress_index =
  803. var->name[var->name_length - 1];
  804. DEBUGMSGTL(("snmptrapd", " my_snmpTrapAddress_index=%d, last_snmpTrapAddress_index=%d, my_ip_addr=%s\n",
  805. my_snmpTrapAddress_index,
  806. last_snmpTrapAddress_index,
  807. inet_ntoa(my_ip_addr)));
  808. if (last_snmpTrapAddress_index < my_snmpTrapAddress_index)
  809. last_snmpTrapAddress_index = my_snmpTrapAddress_index;
  810. /* Detect forwarding loop. */
  811. if (var->val_len < 4) {
  812. snmp_log(LOG_ERR, "Length of IP address of OID .1.3.6.1.6.3.18.1.3.%d in PDU is less than %d bytes = %d\n",
  813. my_snmpTrapAddress_index, 4,
  814. (int)var->val_len);
  815. }
  816. else {
  817. if (to2 &&
  818. memcmp(var->val.string, &my_ip_addr, 4) == 0) {
  819. snmp_log(LOG_ERR, "Forwarding loop detected, OID .1.3.6.1.6.3.18.1.3.%d already has this forwarder's IP address=%s, not forwarding this trap\n",
  820. my_snmpTrapAddress_index,
  821. inet_ntoa(my_ip_addr));
  822. return 0;
  823. }
  824. if (memcmp(var->val.string, &agent_addr, 4) == 0) {
  825. snmp_log(LOG_ERR, "Forwarding loop detected, OID .1.3.6.1.6.3.18.1.3.%d already has the sender's IP address=%s, not forwarding this trap\n",
  826. my_snmpTrapAddress_index,
  827. inet_ntoa(agent_addr));
  828. return 0;
  829. }
  830. }
  831. }
  832. } /* for var in vblist */
  833. DEBUGMSGTL(("snmptrapd",
  834. " last_snmpTrapAddress_index=%d, adding index=%d\n",
  835. last_snmpTrapAddress_index, last_snmpTrapAddress_index + 1));
  836. /* Change the last index of this OID to the next avaiable number. */
  837. forwarder_oid[forwarder_oid_len - 1] = last_snmpTrapAddress_index + 1;
  838. /*
  839. * Add forwarder IP address as OID to trap payload. Use the value
  840. * from the transport, so if a v1 PDU is sent, the same IP is not
  841. * duplicated you want every forwarder to add this OID with its
  842. * own IP address.
  843. */
  844. snmp_pdu_add_variable(pdu2, forwarder_oid, forwarder_oid_len,
  845. ASN_IPADDRESS, (u_char *)&agent_addr, 4);
  846. }
  847. return 1;
  848. }
  849. /*
  850. * Trap handler for forwarding to another destination
  851. */
  852. int forward_handler(netsnmp_pdu *pdu,
  853. netsnmp_transport *transport,
  854. netsnmp_trapd_handler *handler)
  855. {
  856. netsnmp_session session, *ss;
  857. netsnmp_pdu *pdu2;
  858. char buf[BUFSIZ], *cp;
  859. DEBUGMSGTL(("snmptrapd", "forward_handler (%s)\n", handler->token));
  860. snmp_sess_init(&session);
  861. if (strchr(handler->token, ':') == NULL) {
  862. snprintf(buf, BUFSIZ, "%s:%d", handler->token, SNMP_TRAP_PORT);
  863. cp = buf;
  864. }
  865. else {
  866. cp = handler->token;
  867. }
  868. session.peername = cp;
  869. session.version = pdu->version;
  870. ss = snmp_open(&session);
  871. if (!ss)
  872. return NETSNMPTRAPD_HANDLER_FAIL;
  873. /* XXX: wjh we should be caching sessions here and not always
  874. reopening a session. It's very ineffecient, especially with v3
  875. INFORMS which may require engineID probing */
  876. pdu2 = snmp_clone_pdu(pdu);
  877. if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
  878. NETSNMP_DS_LIB_ADD_FORWARDER_INFO) &&
  879. !add_forwarder_info(pdu, pdu2)) {
  880. snmp_close(ss);
  881. return NETSNMPTRAPD_HANDLER_FAIL;
  882. }
  883. if (pdu2->transport_data) {
  884. free(pdu2->transport_data);
  885. pdu2->transport_data = NULL;
  886. pdu2->transport_data_length = 0;
  887. }
  888. ss->s_snmp_errno = SNMPERR_SUCCESS;
  889. if (!snmp_send(ss, pdu2) &&
  890. ss->s_snmp_errno != SNMPERR_SUCCESS) {
  891. snmp_sess_perror("Forward failed", ss);
  892. snmp_free_pdu(pdu2);
  893. }
  894. snmp_close(ss);
  895. return NETSNMPTRAPD_HANDLER_OK;
  896. }
  897. #if defined(USING_NOTIFICATION_LOG_MIB_NOTIFICATION_LOG_MODULE) && defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(NETSNMP_SNMPTRAPD_DISABLE_AGENTX)
  898. /*
  899. * "Notification" handler for implementing NOTIFICATION-MIB
  900. * (presumably)
  901. */
  902. int notification_handler(netsnmp_pdu *pdu,
  903. netsnmp_transport *transport,
  904. netsnmp_trapd_handler *handler)
  905. {
  906. DEBUGMSGTL(("snmptrapd", "notification_handler\n"));
  907. log_notification(pdu, transport);
  908. return NETSNMPTRAPD_HANDLER_OK;
  909. }
  910. #endif
  911. /*-----------------------------
  912. *
  913. * Main driving code, to process an incoming trap
  914. *
  915. *-----------------------------*/
  916. int
  917. snmp_input(int op, netsnmp_session *session,
  918. int reqid, netsnmp_pdu *pdu, void *magic)
  919. {
  920. oid stdTrapOidRoot[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 };
  921. oid snmpTrapOid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
  922. oid trapOid[MAX_OID_LEN + 2] = { 0 };
  923. int trapOidLen;
  924. netsnmp_variable_list *vars;
  925. netsnmp_trapd_handler *traph;
  926. netsnmp_transport *transport = (netsnmp_transport *)magic;
  927. int ret, idx;
  928. //printf("snmp_input \n");
  929. switch (op) {
  930. case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
  931. /*
  932. * Drops packets with reception problems
  933. */
  934. if (session->s_snmp_errno) {
  935. /* drop problem packets */
  936. return 1;
  937. }
  938. /*
  939. * Determine the OID that identifies the trap being handled
  940. */
  941. DEBUGMSGTL(("snmptrapd", "input: %x\n", pdu->command));
  942. switch (pdu->command) {
  943. case SNMP_MSG_TRAP:
  944. /*
  945. * Convert v1 traps into a v2-style trap OID
  946. * (following RFC 2576)
  947. */
  948. if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
  949. trapOidLen = pdu->enterprise_length;
  950. memcpy(trapOid, pdu->enterprise, sizeof(oid) * trapOidLen);
  951. if (trapOid[trapOidLen - 1] != 0) {
  952. trapOid[trapOidLen++] = 0;
  953. }
  954. trapOid[trapOidLen++] = pdu->specific_type;
  955. }
  956. else {
  957. memcpy(trapOid, stdTrapOidRoot, sizeof(stdTrapOidRoot));
  958. trapOidLen = OID_LENGTH(stdTrapOidRoot); /* 9 */
  959. trapOid[trapOidLen++] = pdu->trap_type + 1;
  960. }
  961. break;
  962. case SNMP_MSG_TRAP2:
  963. case SNMP_MSG_INFORM:
  964. /*
  965. * v2c/v3 notifications *should* have snmpTrapOID as the
  966. * second varbind, so we can go straight there.
  967. * But check, just to make sure
  968. */
  969. vars = pdu->variables;
  970. if (vars)
  971. vars = vars->next_variable;
  972. if (!vars || snmp_oid_compare(vars->name, vars->name_length,
  973. snmpTrapOid, OID_LENGTH(snmpTrapOid))) {
  974. /*
  975. * Didn't find it!
  976. * Let's look through the full list....
  977. */
  978. for (vars = pdu->variables; vars; vars = vars->next_variable) {
  979. if (!snmp_oid_compare(vars->name, vars->name_length,
  980. snmpTrapOid, OID_LENGTH(snmpTrapOid)))
  981. break;
  982. }
  983. if (!vars) {
  984. /*
  985. * Still can't find it! Give up.
  986. */
  987. snmp_log(LOG_ERR, "Cannot find TrapOID in TRAP2 PDU\n");
  988. return 1; /* ??? */
  989. }
  990. }
  991. memcpy(trapOid, vars->val.objid, vars->val_len);
  992. trapOidLen = vars->val_len / sizeof(oid);
  993. break;
  994. default:
  995. /* SHOULDN'T HAPPEN! */
  996. return 1; /* ??? */
  997. }
  998. DEBUGMSGTL(("snmptrapd", "Trap OID: "));
  999. DEBUGMSGOID(("snmptrapd", trapOid, trapOidLen));
  1000. DEBUGMSG(("snmptrapd", "\n"));
  1001. /*
  1002. * OK - We've found the Trap OID used to identify this trap.
  1003. * Call each of the various lists of handlers:
  1004. * a) authentication-related handlers,
  1005. * b) other handlers to be applied to all traps
  1006. * (*before* trap-specific handlers)
  1007. * c) the handler(s) specific to this trap
  1008. t * d) any other global handlers
  1009. *
  1010. * In each case, a particular trap handler can abort further
  1011. * processing - either just for that particular list,
  1012. * or for the trap completely.
  1013. *
  1014. * This is particularly designed for authentication-related
  1015. * handlers, but can also be used elsewhere.
  1016. *
  1017. * OK - Enough waffling, let's get to work.....
  1018. */
  1019. for (idx = 0; handlers[idx].descr; ++idx) {
  1020. DEBUGMSGTL(("snmptrapd", "Running %s handlers\n",
  1021. handlers[idx].descr));
  1022. if (NULL == handlers[idx].handler) /* specific */
  1023. traph = netsnmp_get_traphandler(trapOid, trapOidLen);
  1024. else
  1025. traph = *handlers[idx].handler;
  1026. for (; traph; traph = traph->nexth) {
  1027. if (!netsnmp_trapd_check_auth(traph->authtypes))
  1028. continue; /* we continue on and skip this one */
  1029. ret = (*(traph->handler))(pdu, transport, traph);
  1030. if (NETSNMPTRAPD_HANDLER_FINISH == ret)
  1031. return 1;
  1032. if (ret == NETSNMPTRAPD_HANDLER_BREAK)
  1033. break; /* move on to next type */
  1034. } /* traph */
  1035. } /* handlers */
  1036. if (pdu->command == SNMP_MSG_INFORM) {
  1037. netsnmp_pdu *reply = snmp_clone_pdu(pdu);
  1038. if (!reply) {
  1039. snmp_log(LOG_ERR, "couldn't clone PDU for INFORM response\n");
  1040. }
  1041. else {
  1042. reply->command = SNMP_MSG_RESPONSE;
  1043. reply->errstat = 0;
  1044. reply->errindex = 0;
  1045. if (!snmp_send(session, reply)) {
  1046. snmp_sess_perror("snmptrapd: Couldn't respond to inform pdu",
  1047. session);
  1048. snmp_free_pdu(reply);
  1049. }
  1050. }
  1051. }
  1052. break;
  1053. case NETSNMP_CALLBACK_OP_TIMED_OUT:
  1054. snmp_log(LOG_ERR, "Timeout: This shouldn't happen!\n");
  1055. break;
  1056. case NETSNMP_CALLBACK_OP_SEND_FAILED:
  1057. snmp_log(LOG_ERR, "Send Failed: This shouldn't happen either!\n");
  1058. break;
  1059. case NETSNMP_CALLBACK_OP_CONNECT:
  1060. case NETSNMP_CALLBACK_OP_DISCONNECT:
  1061. /* Ignore silently */
  1062. break;
  1063. default:
  1064. snmp_log(LOG_ERR, "Unknown operation (%d): This shouldn't happen!\n", op);
  1065. break;
  1066. }
  1067. return 0;
  1068. }