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