]>
Commit | Line | Data |
---|---|---|
627f6d02 | 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 | ******************************************************************/ | |
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 | ||
57 | typedef 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 | ||
66 | oid 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 | */ | |
77 | struct 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 | */ | |
86 | struct 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 | */ | |
99 | struct session_list { | |
100 | struct session_list *next; | |
101 | struct snmp_session *session; | |
102 | struct snmp_internal_session *internal; | |
103 | }; | |
104 | ||
105 | struct session_list *Sessions = NULL; | |
106 | ||
107 | u_long Reqid = 0; | |
108 | int snmp_errno = 0; | |
109 | ||
110 | char *api_errors[4] = { | |
111 | "Unknown session", | |
112 | "Unknown host", | |
113 | "Invalid local port", | |
114 | "Unknown Error" | |
115 | }; | |
116 | ||
117 | ||
118 | void sync_with_agent(); | |
119 | int parse_app_community_string(); | |
120 | void snmp_synch_setup(); | |
121 | int snmp_synch_response(); | |
122 | void md5Digest(); | |
123 | ||
124 | ||
125 | static char * | |
126 | api_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 | */ | |
140 | static void | |
141 | init_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 | */ | |
154 | static void | |
155 | snmp_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) | |
189 | static void | |
190 | snmp_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 | */ | |
221 | struct snmp_session * | |
222 | snmp_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 | ||
361 | void | |
362 | sync_with_agent( session ) | |
363 | struct 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 | */ | |
404 | static void | |
405 | free_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 | */ | |
431 | static void | |
432 | free_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 | */ | |
451 | int | |
452 | snmp_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 | */ | |
495 | int | |
496 | snmp_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 | */ | |
612 | static int | |
613 | snmp_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 | */ | |
769 | int | |
770 | snmp_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 | */ | |
878 | void | |
879 | snmp_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 | */ | |
912 | void | |
913 | snmp_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 | */ | |
1012 | int | |
1013 | snmp_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 | */ | |
1085 | void | |
1086 | snmp_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 |