]>
Commit | Line | Data |
---|---|---|
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 | 51 | oid 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 | */ | |
63 | struct 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 | */ | |
72 | struct 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 | */ | |
85 | struct session_list { | |
86 | struct session_list *next; | |
87 | struct snmp_session *session; | |
88 | struct snmp_internal_session *internal; | |
89 | }; | |
90 | ||
91 | struct session_list *Sessions = NULL; | |
92 | ||
93 | u_long Reqid = 0; | |
94 | int snmp_errno = 0; | |
95 | ||
96 | char *api_errors[4] = | |
97 | { | |
98 | "Unknown session", | |
99 | "Unknown host", | |
100 | "Invalid local port", | |
101 | "Unknown Error" | |
102 | }; | |
103 | ||
104 | ||
105 | void sync_with_agent(); | |
106 | int parse_app_community_string(); | |
107 | void snmp_synch_setup(); | |
108 | int snmp_synch_response(); | |
109 | void md5Digest(); | |
110 | ||
111 | ||
112 | static char * | |
113 | api_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 | */ | |
127 | static void | |
128 | init_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 | */ | |
143 | static void | |
144 | snmp_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) | |
177 | static void | |
178 | snmp_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 | */ | |
214 | struct snmp_session * | |
215 | snmp_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 | ||
351 | void | |
352 | sync_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 | */ |
394 | static void | |
04a725db | 395 | free_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 | 421 | static void |
04a725db | 422 | free_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 | 441 | int |
627f6d02 | 442 | snmp_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 | */ | |
485 | int | |
486 | snmp_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 | */ | |
602 | static int | |
603 | snmp_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 | */ | |
758 | int | |
759 | snmp_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 | */ | |
860 | void | |
04a725db | 861 | snmp_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 | */ | |
894 | void | |
895 | snmp_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 | */ | |
989 | int | |
990 | snmp_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 | */ | |
1061 | void | |
1062 | snmp_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 | } |