]> git.ipfire.org Git - thirdparty/squid.git/blame - snmplib/snmp_api.c
cbdata leak, mostly from comm_remove_close_handler failing to
[thirdparty/squid.git] / snmplib / snmp_api.c
CommitLineData
04a725db 1/*
2 * Copyright 1989 by Carnegie Mellon University
3 *
4 * All Rights Reserved
5 *
6 * Permission to use, copy, modify, and distribute this software and its
7 * documentation for any purpose and without fee is hereby granted,
8 * provided that the above copyright notice appear in all copies and that
9 * both that copyright notice and this permission notice appear in
10 * supporting documentation, and that the name of CMU not be
11 * used in advertising or publicity pertaining to distribution of the
12 * software without specific, written prior permission.
13 *
14 * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 * SOFTWARE.
21 */
da2d50d1 22/*
23 * snmp_api.c - API for access to snmp.
24 */
25#define DEBUG_SNMPTRACE 0 /* set to 1 to print all SNMP actions */
26#define DEBUG_SNMPFULLDUMP 0 /* set to 1 to dump all SNMP packets */
27
28#include <stdio.h>
29#include <sys/types.h>
30#include <sys/param.h>
31#include <sys/time.h>
32#include <netinet/in.h>
33#include <sys/socket.h>
34#include <netdb.h>
35#ifdef linux
36#include <arpa/inet.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#endif
41
42
43#include "asn1.h"
44#include "snmp.h"
45#include "snmp_impl.h"
46#include "snmp_api.h"
47#include "snmp_client.h"
48
49#define PACKET_LENGTH 4500
50
da2d50d1 51oid default_enterprise[] =
52{1, 3, 6, 1, 4, 1, 3, 1, 1}; /* enterprises.cmu.systems.cmuSNMP */
53
54#define DEFAULT_COMMUNITY "public"
55#define DEFAULT_REMPORT SNMP_PORT
56#define DEFAULT_ENTERPRISE default_enterprise
57#define DEFAULT_TIME 0
58#define DEFAULT_MMS 1389 /* large, randomly picked for testing purposes */
59
60/*
61 * Internal information about the state of the snmp session.
62 */
63struct snmp_internal_session {
64 int sd; /* socket descriptor for this connection */
65 ipaddr addr; /* address of connected peer */
66 struct request_list *requests; /* Info about outstanding requests */
67};
68
69/*
70 * A list of all the outstanding requests for a particular session.
71 */
72struct request_list {
73 struct request_list *next_request;
74 u_long request_id; /* request id */
75 int retries; /* Number of retries */
76 u_long timeout; /* length to wait for timeout */
77 struct timeval time; /* Time this request was made */
78 struct timeval expire; /* time this request is due to expire */
79 struct snmp_pdu *pdu; /* The pdu for this request (saved so it can be retransmitted */
80};
81
82/*
83 * The list of active/open sessions.
84 */
85struct session_list {
86 struct session_list *next;
87 struct snmp_session *session;
88 struct snmp_internal_session *internal;
89};
90
91struct session_list *Sessions = NULL;
92
93u_long Reqid = 0;
94int snmp_errno = 0;
95
96char *api_errors[4] =
97{
98 "Unknown session",
99 "Unknown host",
100 "Invalid local port",
101 "Unknown Error"
102};
103
104
105void sync_with_agent();
106int parse_app_community_string();
107void snmp_synch_setup();
108int snmp_synch_response();
109void md5Digest();
110
111
112static char *
113api_errstring(snmp_errnumber)
114 int snmp_errnumber;
115{
116 if (snmp_errnumber <= SNMPERR_BAD_SESSION && snmp_errnumber >= SNMPERR_GENERR) {
117 return api_errors[snmp_errnumber + 4];
118 } else {
119 return "Unknown Error";
120 }
121}
122
04a725db 123#if UNUSED_CODE
da2d50d1 124/*
125 * Gets initial request ID for all transactions.
126 */
127static void
128init_snmp()
129{
130 struct timeval tv;
131
132 gettimeofday(&tv, (struct timezone *) 0);
133 srandom(tv.tv_sec ^ tv.tv_usec);
134 Reqid = random();
135}
136
137#endif
138
139
140/*
141 * Dump snmp packet to stdout:
142 */
143static void
144snmp_print_packet(packet, length, addr, code)
145 char *packet;
146 int length;
147 ipaddr addr;
148 int code;
149{
150 if (length < 0) {
151 return;
152 }
153 if (code <= 0) { /* received */
154 printf("\nReceived %4d bytes from ", length);
155 } else { /* sending */
156 printf("\nSending %4d bytes to ", length);
157 }
158 printf("%s:", inet_ntoa(addr.sin_addr));
159#if DEBUG_SNMPFULLDUMP
160 for (count = 0; count < length; count++) {
161 if ((count & 15) == 0) {
162 printf("\n ");
163 }
164 printf("%02X ", (int) (packet[count] & 255));
165 }
166#endif
167 fflush(stdout);
168}
169
170#if DEBUG_SNMPTRACE
171/*
172 * Print request
173 */
174#define TRACE_SEND (0)
175#define TRACE_RECV (1)
176#define TRACE_TIMEOUT (2)
177static void
178snmp_print_trace(slp, rp, code)
179 struct session_list *slp;
180 struct request_list *rp;
181 int code;
182{
183 int reqid = 0, retries = 1;
184 if (rp) {
185 reqid = rp->request_id;
186 retries = rp->retries;
187 }
188 printf("\n Session %2d ReqId %4d ", slp->internal->sd, reqid);
189 switch (code) {
190 case TRACE_SEND:
191 printf("send pdu (%d)", retries);
192 break;
193 case TRACE_RECV:
194 printf("recv pdu");
195 break;
196 case TRACE_TIMEOUT:
197 printf("time out");
198 break;
199 }
200 fflush(stdout);
201}
202#endif /* DEBUG_SNMPTRACE */
203
204
205
206
207/*
208 * Sets up the session with the snmp_session information provided
209 * by the user. Then opens and binds the necessary UDP port.
210 * A handle to the created session is returned (this is different than
211 * the pointer passed to snmp_open()). On any error, NULL is returned
212 * and snmp_errno is set to the appropriate error code.
213 */
214struct snmp_session *
215snmp_open(session)
216 struct snmp_session *session;
217{
218 struct session_list *slp;
219 struct snmp_internal_session *isp;
220 u_char *cp;
221 int sd;
222 u_long addr;
223 struct sockaddr_in me;
224 struct hostent *hp;
225 struct servent *servp;
226 extern int check_received_pkt();
227
228 /* Copy session structure and link into list */
229 slp = (struct session_list *) calloc(1, sizeof(struct session_list));
230 slp->internal = isp = (struct snmp_internal_session *) calloc(1, sizeof(struct snmp_internal_session));
231 bzero((char *) isp, sizeof(struct snmp_internal_session));
232 slp->internal->sd = -1; /* mark it not set */
233 slp->session = (struct snmp_session *) calloc(1, sizeof(struct snmp_session));
234 bcopy((char *) session, (char *) slp->session, sizeof(struct snmp_session));
235 session = slp->session;
236 /* now link it in. */
237 slp->next = Sessions;
238 Sessions = slp;
239
240 /*
241 * session now points to the new structure that still contains pointers to
242 * data allocated elsewhere. Some of this data is copied to space malloc'd
243 * here, and the pointer replaced with the new one.
244 */
245
246 if (session->peername != NULL) {
247 cp = (u_char *) calloc(1, (unsigned) strlen(session->peername) + 1);
248 strcpy((char *) cp, session->peername);
249 session->peername = (char *) cp;
250 }
251 if (session->retries == SNMP_DEFAULT_RETRIES)
252 session->retries = SNMP_API_DEFAULT_RETRIES;
253 if (session->timeout == SNMP_DEFAULT_TIMEOUT)
254 session->timeout = SNMP_API_DEFAULT_TIMEOUT;
255 if (session->MMS == 0)
256 session->MMS = DEFAULT_MMS;
257 isp->requests = NULL;
258
259
260 /* Fill in defaults if necessary */
261 if (session->community_len != SNMP_DEFAULT_COMMUNITY_LEN) {
262 if (*session->community == '+') {
263 session->community_len--;
264 cp = (u_char *) calloc(1, (unsigned) session->community_len);
265 bcopy((char *) session->community + 1, (char *) cp,
266 session->community_len);
267 session->version = SNMP_VERSION_2C;
268 } else {
269 cp = (u_char *) calloc(1, (unsigned) session->community_len);
270 bcopy((char *) session->community, (char *) cp,
271 session->community_len);
272 }
273
274 } else {
275 session->community_len = strlen(DEFAULT_COMMUNITY);
276 cp = (u_char *) calloc(1, (unsigned) session->community_len);
277 bcopy((char *) DEFAULT_COMMUNITY, (char *) cp, session->community_len);
278 }
279
280 /* Set up connections */
281 sd = socket(AF_INET, SOCK_DGRAM, 0);
282 if (sd < 0) {
283 perror("socket");
284 snmp_errno = SNMPERR_GENERR;
285 if (!snmp_close(session)) {
286 fprintf(stderr, "Couldn't abort session: %s. Exiting\n", api_errstring(snmp_errno));
287 exit(1);
288 }
289 return 0;
290 }
291 isp->sd = sd;
292
293 if (session->peername != SNMP_DEFAULT_PEERNAME) {
294 if ((addr = inet_addr(session->peername)) != -1) {
295 bcopy((char *) &addr, (char *) &isp->addr.sin_addr, sizeof(isp->addr.sin_addr));
296 } else {
297 hp = gethostbyname(session->peername);
298 if (hp == NULL) {
299 fprintf(stderr, "unknown host: %s\n", session->peername);
300 snmp_errno = SNMPERR_BAD_ADDRESS;
301 if (!snmp_close(session)) {
302 fprintf(stderr, "Couldn't abort session: %s. Exiting\n", api_errstring(snmp_errno));
303 exit(2);
304 }
305 return 0;
306 } else {
307 bcopy((char *) hp->h_addr, (char *) &isp->addr.sin_addr, hp->h_length);
308 }
309 }
310 isp->addr.sin_family = AF_INET;
311 if (session->remote_port == SNMP_DEFAULT_REMPORT) {
312 /*servp = getservbyname("snmp", "udp"); */
313 servp = NULL;
314 if (servp != NULL) {
315 isp->addr.sin_port = servp->s_port;
316 } else {
317 isp->addr.sin_port = htons(SNMP_PORT);
318 }
319 } else {
320 isp->addr.sin_port = htons(session->remote_port);
321 }
322 } else {
323 isp->addr.sin_addr.s_addr = SNMP_DEFAULT_ADDRESS;
324 }
325
326 me.sin_family = AF_INET;
327 me.sin_addr.s_addr = INADDR_ANY;
328 me.sin_port = htons(session->local_port);
329 if (bind(sd, (struct sockaddr *) &me, sizeof(me)) != 0) {
330 perror("bind");
331 snmp_errno = SNMPERR_BAD_LOCPORT;
332 if (!snmp_close(session)) {
333 fprintf(stderr, "Couldn't abort session: %s. Exiting\n",
334 api_errstring(snmp_errno));
335 exit(3);
336 }
337 return 0;
338 }
339 if (*cp == '/') {
340 session->authenticator = check_received_pkt;
341 sync_with_agent(session);
342 parse_app_community_string(session);
343 session->qoS |= USEC_QOS_GENREPORT;
344 }
345 /* replace comm pointer with pointer to new data: */
346 session->community = cp;
347
348 return session;
349}
350
351void
352sync_with_agent(session)
353 struct snmp_session *session;
354{
355 struct snmp_pdu *pdu, *response = 0;
356 int status;
357
358 session->qoS = USEC_QOS_GENREPORT;
359 session->userLen = 6;
360 session->version = SNMP_VERSION_2;
361 strcpy(session->userName, "public");
362
363 snmp_synch_setup(session);
364 pdu = snmp_pdu_create(GET_REQ_MSG);
365 status = snmp_synch_response(session, pdu, &response);
366
367 if (status == STAT_SUCCESS) {
368 memcpy(session->agentID, response->params.agentID, 12);
369
370 /* save the clocks -- even though they are not authentic */
371 session->agentBoots = response->params.agentBoots;
372 session->agentTime = response->params.agentTime;
373 session->agentClock = response->params.agentTime - time(NULL);
374
375 } else {
376 if (status == STAT_TIMEOUT) {
377 printf("No Response from %s\n", session->peername);
378 } else { /* status == STAT_ERROR */
379 printf("An error occurred, Quitting\n");
380 }
381 exit(-1);
382 }
383
384 /** freed to early:
385 snmp_free_pdu(pdu);
386 if (response) snmp_free_pdu(response);
387 **/
388}
da2d50d1 389
390/*
04a725db 391 * Unlink one element from input request list,
392 * then free it and it's pdu.
da2d50d1 393 */
394static void
04a725db 395free_one_request(isp, orp)
396 struct snmp_internal_session *isp;
397 struct request_list *orp;
da2d50d1 398{
04a725db 399 struct request_list *rp;
400 if (!orp)
da2d50d1 401 return;
04a725db 402 if (isp->requests == orp) {
403 isp->requests = orp->next_request; /* unlink head */
404 } else {
405 for (rp = isp->requests; rp; rp = rp->next_request) {
406 if (rp->next_request == orp) {
407 rp->next_request = orp->next_request; /* unlink element */
408 break;
409 }
da2d50d1 410 }
da2d50d1 411 }
04a725db 412 if (orp->pdu != NULL) {
413 snmp_free_pdu(orp->pdu);
414 }
415 free((char *) orp);
da2d50d1 416}
417
da2d50d1 418/*
04a725db 419 * Free each element in the input request list.
da2d50d1 420 */
da2d50d1 421static void
04a725db 422free_request_list(rp)
da2d50d1 423 struct request_list *rp;
627f6d02 424{
425 struct request_list *orp;
426
04a725db 427 while (rp) {
627f6d02 428 orp = rp;
429 rp = rp->next_request;
430 if (orp->pdu != NULL)
431 snmp_free_pdu(orp->pdu);
04a725db 432 free((char *) orp);
627f6d02 433 }
434}
435
436/*
437 * Close the input session. Frees all data allocated for the session,
438 * dequeues any pending requests, and closes any sockets allocated for
439 * the session. Returns 0 on error, 1 otherwise.
440 */
04a725db 441int
627f6d02 442snmp_close(session)
04a725db 443 struct snmp_session *session;
627f6d02 444{
445 struct session_list *slp = NULL, *oslp = NULL;
446
04a725db 447 if (Sessions->session == session) { /* If first entry */
627f6d02 448 slp = Sessions;
449 Sessions = slp->next;
450 } else {
04a725db 451 for (slp = Sessions; slp; slp = slp->next) {
452 if (slp->session == session) {
453 if (oslp) /* if we found entry that points here */
627f6d02 454 oslp->next = slp->next; /* link around this entry */
455 break;
456 }
457 oslp = slp;
458 }
459 }
460 /* If we found the session, free all data associated with it */
04a725db 461 if (slp) {
627f6d02 462 if (slp->session->community != NULL)
04a725db 463 free((char *) slp->session->community);
464 if (slp->session->peername != NULL)
465 free((char *) slp->session->peername);
466 free((char *) slp->session);
627f6d02 467 if (slp->internal->sd != -1)
468 close(slp->internal->sd);
469 free_request_list(slp->internal->requests);
04a725db 470 free((char *) slp->internal);
471 free((char *) slp);
627f6d02 472 } else {
473 snmp_errno = SNMPERR_BAD_SESSION;
474 return 0;
475 }
476 return 1;
477}
478
479/*
480 * Takes a session and a pdu and serializes the ASN PDU into the area
481 * pointed to by packet. out_length is the size of the data area available.
482 * Returns the length of the completed packet in out_length. If any errors
483 * occur, -1 is returned. If all goes well, 0 is returned.
484 */
485int
486snmp_build(session, pdu, packet, out_length, is_agent)
04a725db 487 struct snmp_session *session;
488 struct snmp_pdu *pdu;
489 u_char *packet;
490 int *out_length;
491 int is_agent;
627f6d02 492{
04a725db 493 u_char buf[PACKET_LENGTH];
494 u_char *cp;
627f6d02 495 struct variable_list *vp;
04a725db 496 int length;
497 int totallength;
627f6d02 498
499 length = *out_length;
500 cp = packet;
04a725db 501 for (vp = pdu->variables; vp; vp = vp->next_variable) {
502 cp = snmp_build_var_op(cp, vp->name, &vp->name_length, vp->type, vp->val_len, (u_char *) vp->val.string, &length);
627f6d02 503 if (cp == NULL)
504 return -1;
505 }
506 totallength = cp - packet;
507
508 length = PACKET_LENGTH;
04a725db 509 cp = asn_build_header(buf, &length, (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), totallength);
627f6d02 510 if (cp == NULL)
511 return -1;
04a725db 512 bcopy((char *) packet, (char *) cp, totallength);
627f6d02 513 totallength += cp - buf;
514
515 length = *out_length;
04a725db 516 if (pdu->command != TRP_REQ_MSG) {
627f6d02 517 /* request id */
518 cp = asn_build_int(packet, &length,
04a725db 519 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
520 (long *) &pdu->reqid, sizeof(pdu->reqid));
627f6d02 521 if (cp == NULL)
522 return -1;
523 /* error status */
524 cp = asn_build_int(cp, &length,
04a725db 525 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
526 (long *) &pdu->errstat, sizeof(pdu->errstat));
627f6d02 527 if (cp == NULL)
528 return -1;
529 /* error index */
530 cp = asn_build_int(cp, &length,
04a725db 531 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
532 (long *) &pdu->errindex, sizeof(pdu->errindex));
627f6d02 533 if (cp == NULL)
534 return -1;
04a725db 535 } else { /* this is a trap message */
627f6d02 536 /* enterprise */
537 cp = asn_build_objid(packet, &length,
04a725db 538 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
539 (oid *) pdu->enterprise, pdu->enterprise_length);
627f6d02 540 if (cp == NULL)
541 return -1;
542 /* agent-addr */
04a725db 543 cp = asn_build_string(cp, &length, (u_char) IPADDRESS,
544 (u_char *) & pdu->agent_addr.sin_addr.s_addr, sizeof(pdu->agent_addr.sin_addr.s_addr));
627f6d02 545 if (cp == NULL)
546 return -1;
547 /* generic trap */
548 cp = asn_build_int(cp, &length,
04a725db 549 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
550 (long *) &pdu->trap_type, sizeof(pdu->trap_type));
627f6d02 551 if (cp == NULL)
552 return -1;
553 /* specific trap */
554 cp = asn_build_int(cp, &length,
04a725db 555 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
556 (long *) &pdu->specific_type, sizeof(pdu->specific_type));
627f6d02 557 if (cp == NULL)
558 return -1;
559 /* timestamp */
04a725db 560 cp = asn_build_int(cp, &length, (u_char) TIMETICKS,
561 (long *) &pdu->time, sizeof(pdu->time));
627f6d02 562 if (cp == NULL)
563 return -1;
564 }
565 if (length < totallength)
566 return -1;
04a725db 567 bcopy((char *) buf, (char *) cp, totallength);
627f6d02 568 totallength += cp - packet;
569
570 length = PACKET_LENGTH;
04a725db 571 cp = asn_build_header(buf, &length, (u_char) pdu->command, totallength);
627f6d02 572 if (cp == NULL)
573 return -1;
574 if (length < totallength)
575 return -1;
04a725db 576 bcopy((char *) packet, (char *) cp, totallength);
627f6d02 577 totallength += cp - buf;
578
579 length = *out_length;
580
04a725db 581 cp = snmp_auth_build(packet, &length, session, is_agent, totallength);
627f6d02 582 if (cp == NULL)
583 return -1;
584 if ((*out_length - (cp - packet)) < totallength)
585 return -1;
04a725db 586 bcopy((char *) buf, (char *) cp, totallength);
627f6d02 587 totallength += cp - packet;
588 *out_length = totallength;
589
04a725db 590 if (session->qoS & USEC_QOS_AUTH)
591 md5Digest(packet, totallength, cp - (session->contextLen + 16),
592 cp - (session->contextLen + 16));
627f6d02 593
594 return 0;
595}
596
597/*
598 * Parses the packet received on the input session, and places the data into
599 * the input pdu. length is the length of the input packet. If any errors
600 * are encountered, -1 is returned. Otherwise, a 0 is returned.
601 */
602static int
603snmp_parse(session, pdu, data, length)
04a725db 604 struct snmp_session *session;
605 struct snmp_pdu *pdu;
606 u_char *data;
607 int length;
627f6d02 608{
04a725db 609 u_char msg_type;
610 u_char type;
611 u_char *var_val;
612 long version;
613 int len, four;
627f6d02 614 u_char community[256];
615 int community_length = 256;
616 struct variable_list *vp = 0;
04a725db 617 oid objid[MAX_NAME_LEN], *op;
618 u_char *origdata = data;
619 int origlength = length;
620 int ret = 0;
621 u_char *save_data;
627f6d02 622
623 /* authenticates message and returns length if valid */
624 data = snmp_auth_parse(data, &length, community, &community_length, &version);
625 if (data == NULL)
626 return -1;
627
04a725db 628 if (version != SNMP_VERSION_1 && version != SNMP_VERSION_2C && version != SNMP_VERSION_2) {
629 fprintf(stderr, "Wrong version: %ld\n", version);
630 return -1;
627f6d02 631 }
627f6d02 632 save_data = data;
633
634 data = asn_parse_header(data, &length, &msg_type);
635 if (data == NULL)
636 return -1;
637 pdu->command = msg_type;
638
04a725db 639 if (session->authenticator) {
640 ret = session->authenticator(origdata, origlength, save_data - community_length,
641 community_length, session, pdu);
642 if (ret < 0)
643 return ret;
627f6d02 644 }
04a725db 645 if (pdu->command != TRP_REQ_MSG) {
646 data = asn_parse_int(data, &length, &type, (long *) &pdu->reqid, sizeof(pdu->reqid));
627f6d02 647 if (data == NULL)
648 return -1;
04a725db 649 data = asn_parse_int(data, &length, &type, (long *) &pdu->errstat, sizeof(pdu->errstat));
627f6d02 650 if (data == NULL)
651 return -1;
04a725db 652 data = asn_parse_int(data, &length, &type, (long *) &pdu->errindex, sizeof(pdu->errindex));
627f6d02 653 if (data == NULL)
654 return -1;
655 } else {
656 pdu->enterprise_length = MAX_NAME_LEN;
657 data = asn_parse_objid(data, &length, &type, objid, &pdu->enterprise_length);
658 if (data == NULL)
659 return -1;
04a725db 660 pdu->enterprise = (oid *) calloc(1, pdu->enterprise_length * sizeof(oid));
661 bcopy((char *) objid, (char *) pdu->enterprise, pdu->enterprise_length * sizeof(oid));
627f6d02 662
663 four = 4;
04a725db 664 data = asn_parse_string(data, &length, &type, (u_char *) & pdu->agent_addr.sin_addr.s_addr, &four);
627f6d02 665 if (data == NULL)
666 return -1;
04a725db 667 data = asn_parse_int(data, &length, &type, (long *) &pdu->trap_type, sizeof(pdu->trap_type));
627f6d02 668 if (data == NULL)
669 return -1;
04a725db 670 data = asn_parse_int(data, &length, &type, (long *) &pdu->specific_type, sizeof(pdu->specific_type));
627f6d02 671 if (data == NULL)
672 return -1;
04a725db 673 data = asn_parse_int(data, &length, &type, (long *) &pdu->time, sizeof(pdu->time));
627f6d02 674 if (data == NULL)
675 return -1;
676 }
677 data = asn_parse_header(data, &length, &type);
678 if (data == NULL)
679 return -1;
04a725db 680 if (type != (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR))
627f6d02 681 return -1;
04a725db 682 while ((int) length > 0) {
683 if (pdu->variables == NULL) {
684 pdu->variables = vp = (struct variable_list *) calloc(1, sizeof(struct variable_list));
627f6d02 685 } else {
04a725db 686 vp->next_variable = (struct variable_list *) calloc(1, sizeof(struct variable_list));
627f6d02 687 vp = vp->next_variable;
688 }
689 vp->next_variable = NULL;
690 vp->val.string = NULL;
691 vp->name = NULL;
692 vp->name_length = MAX_NAME_LEN;
04a725db 693 data = snmp_parse_var_op(data, objid, &vp->name_length, &vp->type, &vp->val_len, &var_val, (int *) &length);
627f6d02 694 if (data == NULL)
695 return -1;
04a725db 696 op = (oid *) calloc(1, (unsigned) vp->name_length * sizeof(oid));
697 bcopy((char *) objid, (char *) op, vp->name_length * sizeof(oid));
627f6d02 698 vp->name = op;
699
700 len = PACKET_LENGTH;
04a725db 701 switch ((short) vp->type) {
702 case ASN_INTEGER:
703 vp->val.integer = (long *) calloc(1, sizeof(long));
704 vp->val_len = sizeof(long);
705 asn_parse_int(var_val, &len, &vp->type, (long *) vp->val.integer, sizeof(vp->val.integer));
706 break;
707 case COUNTER:
708 case GAUGE:
709 case TIMETICKS:
710 case UINTEGER:
711 vp->val.integer = (long *) calloc(1, sizeof(unsigned long));
712 vp->val_len = sizeof(unsigned long);
713 asn_parse_unsigned_int(var_val, &len, &vp->type, (unsigned long *) vp->val.integer, sizeof(vp->val.integer));
714 break;
715 case COUNTER64:
716 vp->val.counter64 = (struct counter64 *) calloc(1, sizeof(struct counter64));
717 vp->val_len = sizeof(struct counter64);
718 asn_parse_unsigned_int64(var_val, &len, &vp->type,
719 (struct counter64 *) vp->val.counter64,
720 sizeof(*vp->val.counter64));
721 break;
722 case ASN_OCTET_STR:
723 case IPADDRESS:
724 case OPAQUE:
725 case NSAP:
726 vp->val.string = (u_char *) calloc(1, (unsigned) vp->val_len);
727 asn_parse_string(var_val, &len, &vp->type, vp->val.string, &vp->val_len);
728 break;
729 case ASN_OBJECT_ID:
730 vp->val_len = MAX_NAME_LEN;
731 asn_parse_objid(var_val, &len, &vp->type, objid, &vp->val_len);
732 vp->val_len *= sizeof(oid);
733 vp->val.objid = (oid *) calloc(1, (unsigned) vp->val_len);
734 bcopy((char *) objid, (char *) vp->val.objid, vp->val_len);
735 break;
736 case SNMP_NOSUCHOBJECT:
737 case SNMP_NOSUCHINSTANCE:
738 case SNMP_ENDOFMIBVIEW:
739 case ASN_NULL:
740 break;
741 default:
742 fprintf(stderr, "bad type returned (%x)\n", vp->type);
743 break;
627f6d02 744 }
745 }
746 return ret;
747}
748
749/*
750 * Sends the input pdu on the session after calling snmp_build to create
751 * a serialized packet. If necessary, set some of the pdu data from the
752 * session defaults. Add a request corresponding to this pdu to the list
753 * of outstanding requests on this session, then send the pdu.
754 * Returns the request id of the generated packet if applicable, otherwise 1.
755 * On any error, 0 is returned.
756 * The pdu is freed by snmp_send() unless a failure occured.
757 */
758int
759snmp_send(session, pdu)
04a725db 760 struct snmp_session *session;
761 struct snmp_pdu *pdu;
627f6d02 762{
763 struct session_list *slp;
764 struct snmp_internal_session *isp = NULL;
04a725db 765 u_char packet[PACKET_LENGTH];
627f6d02 766 int length = PACKET_LENGTH;
767 struct request_list *rp;
768 struct timeval tv;
769
04a725db 770 for (slp = Sessions; slp; slp = slp->next) {
771 if (slp->session == session) {
627f6d02 772 isp = slp->internal;
773 break;
774 }
775 }
776
04a725db 777 if (!pdu) {
627f6d02 778 snmp_errno = SNMPERR_GENERR;
779 return 0;
780 }
04a725db 781 if (isp == NULL) {
627f6d02 782 snmp_errno = SNMPERR_BAD_SESSION;
783 return 0;
784 }
785 if (pdu->command == GET_REQ_MSG || pdu->command == GETNEXT_REQ_MSG
04a725db 786 || pdu->command == GET_RSP_MSG || pdu->command == SET_REQ_MSG
787 || pdu->command == GETBULK_REQ_MSG) {
627f6d02 788 if (pdu->reqid == SNMP_DEFAULT_REQID)
789 pdu->reqid = ++Reqid;
790 if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
791 pdu->errstat = 0;
792 if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
793 pdu->errindex = 0;
794 } else {
795 /* fill in trap defaults */
04a725db 796 pdu->reqid = 1; /* give a bogus non-error reqid for traps */
797 if (pdu->enterprise_length == SNMP_DEFAULT_ENTERPRISE_LENGTH) {
798 pdu->enterprise = (oid *) calloc(1, sizeof(DEFAULT_ENTERPRISE));
799 bcopy((char *) DEFAULT_ENTERPRISE, (char *) pdu->enterprise, sizeof(DEFAULT_ENTERPRISE));
800 pdu->enterprise_length = sizeof(DEFAULT_ENTERPRISE) / sizeof(oid);
627f6d02 801 }
802 if (pdu->time == SNMP_DEFAULT_TIME)
803 pdu->time = DEFAULT_TIME;
804 }
04a725db 805 if (pdu->address.sin_addr.s_addr == SNMP_DEFAULT_ADDRESS) {
806 if (isp->addr.sin_addr.s_addr != SNMP_DEFAULT_ADDRESS) {
807 bcopy((char *) &isp->addr, (char *) &pdu->address, sizeof(pdu->address));
627f6d02 808 } else {
809 fprintf(stderr, "No remote IP address specified\n");
810 snmp_errno = SNMPERR_BAD_ADDRESS;
811 return 0;
812 }
813 }
04a725db 814 if (snmp_build(session, pdu, packet, &length, 0) < 0) {
627f6d02 815 fprintf(stderr, "Error building packet\n");
816 snmp_errno = SNMPERR_GENERR;
817 return 0;
818 }
04a725db 819 if (snmp_dump_packet) {
820 snmp_print_packet(packet, length, pdu->address, 1);
627f6d02 821 }
04a725db 822 gettimeofday(&tv, (struct timezone *) 0);
823 if (sendto(isp->sd, (char *) packet, length, 0, (struct sockaddr *) &pdu->address, sizeof(pdu->address)) < 0) {
627f6d02 824 perror("sendto");
825 snmp_errno = SNMPERR_GENERR;
826 return 0;
827 }
04a725db 828 if (pdu->command == GET_REQ_MSG || pdu->command == GETNEXT_REQ_MSG
829 || pdu->command == SET_REQ_MSG || pdu->command == GETBULK_REQ_MSG) {
627f6d02 830 /* set up to expect a response */
04a725db 831 rp = (struct request_list *) calloc(1, sizeof(struct request_list));
832
833 if (!rp) {
834 fprintf(stderr, "Out of memory!\n");
835 snmp_errno = SNMPERR_GENERR;
836 return 0;
837 }
627f6d02 838 rp->next_request = isp->requests;
839 isp->requests = rp;
840 rp->pdu = pdu;
841 rp->request_id = pdu->reqid;
842
843 rp->retries = 1;
844 rp->timeout = session->timeout;
845 rp->time = tv;
846 tv.tv_usec += rp->timeout;
847 tv.tv_sec += tv.tv_usec / 1000000L;
848 tv.tv_usec %= 1000000L;
849 rp->expire = tv;
850#if DEBUG_SNMPTRACE
851 snmp_print_trace(slp, rp, TRACE_SEND);
852#endif
853 }
854 return pdu->reqid;
855}
856
857/*
858 * Frees the pdu and any malloc'd data associated with it.
859 */
860void
04a725db 861snmp_free_pdu(struct snmp_pdu *pdu)
627f6d02 862{
863 struct variable_list *vp, *ovp;
864
04a725db 865 if (!pdu)
866 return;
627f6d02 867
868 vp = pdu->variables;
04a725db 869 while (vp) {
870 if (vp->name) {
871 free((char *) vp->name);
872 }
873 if (vp->val.string) {
874 free((char *) vp->val.string);
875 }
876 ovp = vp;
877 vp = vp->next_variable;
878 free((char *) ovp);
627f6d02 879 }
880 if (pdu->enterprise) {
04a725db 881 free((char *) pdu->enterprise);
627f6d02 882 }
04a725db 883 free((char *) pdu);
627f6d02 884}
885
886
887/*
888 * Checks to see if any of the fd's set in the fdset belong to
889 * snmp. Each socket with it's fd set has a packet read from it
890 * and snmp_parse is called on the packet received. The resulting pdu
891 * is passed to the callback routine for that session. If the callback
892 * routine returns successfully, the pdu and it's request are deleted.
893 */
894void
895snmp_read(fdset)
04a725db 896 fd_set *fdset;
627f6d02 897{
898 struct session_list *slp;
899 struct snmp_session *sp;
900 struct snmp_internal_session *isp;
901 u_char packet[PACKET_LENGTH];
04a725db 902 struct sockaddr_in from;
627f6d02 903 ssize_t length, fromlength;
904 struct snmp_pdu *pdu;
905 struct request_list *rp /**, *orp **/ ;
906
04a725db 907 for (slp = Sessions; slp; slp = slp->next) {
908 if (FD_ISSET(slp->internal->sd, fdset)) {
627f6d02 909 sp = slp->session;
910 isp = slp->internal;
911 fromlength = sizeof(from);
04a725db 912 length = recvfrom(isp->sd, (char *) packet, PACKET_LENGTH, 0, (struct sockaddr *) &from, &fromlength);
627f6d02 913 if (length == -1) {
914 perror("recvfrom");
915 return;
916 }
04a725db 917 if (snmp_dump_packet) {
918 snmp_print_packet(packet, length, from, 0);
627f6d02 919 }
04a725db 920 pdu = (struct snmp_pdu *) calloc(1, sizeof(struct snmp_pdu));
627f6d02 921
04a725db 922 if (!pdu) {
923 fprintf(stderr, "Out of memory!\n");
924 snmp_errno = SNMPERR_GENERR;
925 return;
627f6d02 926 }
627f6d02 927 pdu->address = from;
928 pdu->reqid = 0;
929 pdu->variables = NULL;
930 pdu->enterprise = NULL;
931 pdu->enterprise_length = 0;
04a725db 932 if (snmp_parse(sp, pdu, packet, length) < 0) {
627f6d02 933 fprintf(stderr, "Mangled packet\n");
934 snmp_free_pdu(pdu);
935 return;
936 }
04a725db 937 if (pdu->command == GET_RSP_MSG || pdu->command == REPORT_MSG) {
627f6d02 938
04a725db 939 struct request_list *rp_next = 0;
940 for (rp = isp->requests; rp; rp = rp_next) {
941 rp_next = rp->next_request;
942 if (rp->request_id == pdu->reqid) {
627f6d02 943#if DEBUG_SNMPTRACE
04a725db 944 snmp_print_trace(slp, rp, TRACE_RECV);
627f6d02 945#endif
04a725db 946 if (sp->callback(RECEIVED_MESSAGE, sp, pdu->reqid, pdu, sp->callback_magic) == 1) {
947 /* successful, so delete request */
948 free_one_request(isp, rp);
949 break; /* no more request with the same reqid */
950 }
951 }
627f6d02 952 }
04a725db 953 } else if (pdu->command == GET_REQ_MSG
627f6d02 954 || pdu->command == GETNEXT_REQ_MSG
955 || pdu->command == GETBULK_REQ_MSG
04a725db 956 || pdu->command == TRP_REQ_MSG || pdu->command == SET_REQ_MSG) {
627f6d02 957#if DEBUG_SNMPTRACE
04a725db 958 snmp_print_trace(slp, NULL, TRACE_RECV);
627f6d02 959#endif
04a725db 960 sp->callback(RECEIVED_MESSAGE, sp, pdu->reqid, pdu, sp->callback_magic);
627f6d02 961 }
962 snmp_free_pdu(pdu);
963 }
964 }
965}
966
967/*
968 * Returns info about what snmp requires from a select statement.
969 * numfds is the number of fds in the list that are significant.
970 * All file descriptors opened for SNMP are OR'd into the fdset.
971 * If activity occurs on any of these file descriptors, snmp_read
972 * should be called with that file descriptor set
973 *
974 * The timeout is the latest time that SNMP can wait for a timeout. The
975 * select should be done with the minimum time between timeout and any other
976 * timeouts necessary. This should be checked upon each invocation of select.
977 * If a timeout is received, snmp_timeout should be called to check if the
978 * timeout was for SNMP. (snmp_timeout is idempotent)
979 *
980 * Block is 1 if the select is requested to block indefinitely, rather
981 * than time out. If block is input as 1, the timeout value will be
982 * treated as undefined, but it must be available for setting in
983 * snmp_select_info. On return, if block is true, the value of
984 * timeout will be undefined.
985 *
986 * snmp_select_info returns the number of open sockets. (i.e. The
987 * number of sessions open)
988 */
989int
990snmp_select_info(numfds, fdset, timeout, block)
04a725db 991 int *numfds;
992 fd_set *fdset;
993 struct timeval *timeout;
994 int *block; /* should the select block until input arrives (i.e. no input) */
627f6d02 995{
996 struct session_list *slp;
997 struct snmp_internal_session *isp;
998 struct request_list *rp;
999 struct timeval now, earliest;
1000 int active = 0, requests = 0;
1001
1002 timerclear(&earliest);
1003 /*
1004 * For each request outstanding, add it's socket to the fdset,
1005 * and if it is the earliest timeout to expire, mark it as lowest.
1006 */
04a725db 1007 for (slp = Sessions; slp; slp = slp->next) {
627f6d02 1008 active++;
1009 isp = slp->internal;
1010 if ((isp->sd + 1) > *numfds)
1011 *numfds = (isp->sd + 1);
1012 FD_SET(isp->sd, fdset);
04a725db 1013 if (isp->requests) {
627f6d02 1014 /* found another session with outstanding requests */
1015 requests++;
04a725db 1016 for (rp = isp->requests; rp; rp = rp->next_request) {
627f6d02 1017 if (!timerisset(&earliest) || timercmp(&rp->expire, &earliest, <))
1018 earliest = rp->expire;
1019 }
1020 }
1021 }
04a725db 1022 if (requests == 0) /* if none are active, skip arithmetic */
627f6d02 1023 return active;
1024
1025 /*
1026 * Now find out how much time until the earliest timeout. This
1027 * transforms earliest from an absolute time into a delta time, the
1028 * time left until the select should timeout.
1029 */
04a725db 1030 gettimeofday(&now, (struct timezone *) 0);
1031 earliest.tv_sec--; /* adjust time to make arithmetic easier */
627f6d02 1032 earliest.tv_usec += 1000000L;
1033 earliest.tv_sec -= now.tv_sec;
1034 earliest.tv_usec -= now.tv_usec;
04a725db 1035 while (earliest.tv_usec >= 1000000L) {
627f6d02 1036 earliest.tv_usec -= 1000000L;
1037 earliest.tv_sec += 1;
1038 }
04a725db 1039 if (earliest.tv_sec < 0) {
627f6d02 1040 earliest.tv_sec = 0;
1041 earliest.tv_usec = 0;
1042 }
627f6d02 1043 /* if it was blocking before or our delta time is less, reset timeout */
04a725db 1044 if (*block == 1 || timercmp(&earliest, timeout, <)) {
627f6d02 1045 *timeout = earliest;
1046 *block = 0;
1047 }
1048 return active;
1049}
1050
1051/*
1052 * snmp_timeout should be called whenever the timeout from
1053 * snmp_select_info expires, but it is idempotent, so snmp_timeout can
1054 * be polled (probably a cpu expensive proposition). snmp_timeout
1055 * checks to see if any of the sessions have an outstanding request
1056 * that has timed out. If it finds one (or more), and that pdu has
1057 * more retries available, a new packet is formed from the pdu and is
1058 * resent. If there are no more retries available, the callback for
1059 * the session is used to alert the user of the timeout.
1060 */
1061void
1062snmp_timeout()
1063{
1064 struct session_list *slp;
1065 struct snmp_session *sp;
1066 struct snmp_internal_session *isp;
627f6d02 1067 struct request_list *rp, *rp_next = 0;
627f6d02 1068 struct timeval now;
1069
04a725db 1070 gettimeofday(&now, (struct timezone *) 0);
627f6d02 1071 /*
1072 * For each request outstanding, check to see if it has expired.
1073 */
04a725db 1074 for (slp = Sessions; slp; slp = slp->next) {
627f6d02 1075 sp = slp->session;
1076 isp = slp->internal;
04a725db 1077 for (rp = isp->requests; rp; rp = rp_next) {
627f6d02 1078 rp_next = rp->next_request;
04a725db 1079 if (timercmp(&rp->expire, &now, <)) {
627f6d02 1080 /* this timer has expired */
04a725db 1081 if (rp->retries >= sp->retries) {
627f6d02 1082#if DEBUG_SNMPTRACE
04a725db 1083 snmp_print_trace(slp, rp, TRACE_TIMEOUT);
627f6d02 1084#endif
04a725db 1085 /* No more chances, delete this entry */
1086 sp->callback(TIMED_OUT, sp, rp->pdu->reqid, rp->pdu, sp->callback_magic);
1087 free_one_request(isp, rp);
1088 continue;
627f6d02 1089 } else {
04a725db 1090 u_char packet[PACKET_LENGTH];
627f6d02 1091 int length = PACKET_LENGTH;
1092 struct timeval tv;
1093
1094 /* retransmit this pdu */
1095 rp->retries++;
1096 rp->timeout <<= 1;
04a725db 1097 if (snmp_build(sp, rp->pdu, packet, &length, 0) < 0) {
627f6d02 1098 fprintf(stderr, "Error building packet\n");
1099 }
04a725db 1100 if (snmp_dump_packet) {
1101 snmp_print_packet(packet, length, rp->pdu->address, 1);
627f6d02 1102 }
04a725db 1103 gettimeofday(&tv, (struct timezone *) 0);
1104 if (sendto(isp->sd, (char *) packet, length, 0, (struct sockaddr *) &rp->pdu->address, sizeof(rp->pdu->address)) < 0) {
627f6d02 1105 perror("sendto");
1106 }
1107 rp->time = tv;
1108 tv.tv_usec += rp->timeout;
1109 tv.tv_sec += tv.tv_usec / 1000000L;
1110 tv.tv_usec %= 1000000L;
1111 rp->expire = tv;
1112#if DEBUG_SNMPTRACE
1113 snmp_print_trace(slp, rp, TRACE_SEND);
1114#endif
1115 }
1116 }
627f6d02 1117 }
627f6d02 1118 }
1119}