and added files
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / snmp_client.c
1 /*
2  * snmp_client.c - a toolkit of common functions for an SNMP client.
3  *
4  */
5 /**********************************************************************
6         Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
7
8                       All Rights Reserved
9
10 Permission to use, copy, modify, and distribute this software and its
11 documentation for any purpose and without fee is hereby granted,
12 provided that the above copyright notice appear in all copies and that
13 both that copyright notice and this permission notice appear in
14 supporting documentation, and that the name of CMU not be
15 used in advertising or publicity pertaining to distribution of the
16 software without specific, written prior permission.
17
18 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
19 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
20 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
21 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
23 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
24 SOFTWARE.
25 ******************************************************************/
26
27 #include <net-snmp/net-snmp-config.h>
28
29 #include <stdio.h>
30 #include <errno.h>
31 #if HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #if HAVE_STRING_H
35 #include <string.h>
36 #else
37 #include <strings.h>
38 #endif
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #include <sys/types.h>
43 #if TIME_WITH_SYS_TIME
44 # ifdef WIN32
45 #  include <sys/timeb.h>
46 # else
47 #  include <sys/time.h>
48 # endif
49 # include <time.h>
50 #else
51 # if HAVE_SYS_TIME_H
52 #  include <sys/time.h>
53 # else
54 #  include <time.h>
55 # endif
56 #endif
57 #if HAVE_SYS_PARAM_H
58 #include <sys/param.h>
59 #endif
60 #if HAVE_NETINET_IN_H
61 #include <netinet/in.h>
62 #endif
63 #if HAVE_ARPA_INET_H
64 #include <arpa/inet.h>
65 #endif
66 #if HAVE_SYS_SELECT_H
67 #include <sys/select.h>
68 #endif
69
70 #if HAVE_DMALLOC_H
71 #include <dmalloc.h>
72 #endif
73
74 #if HAVE_WINSOCK_H
75 #include <winsock.h>
76 #endif
77
78 #include <net-snmp/types.h>
79
80 #include <net-snmp/library/snmp_api.h>
81 #include <net-snmp/library/snmp_client.h>
82 #include <net-snmp/library/snmp_secmod.h>
83 #include <net-snmp/library/mib.h>
84
85
86 #ifndef BSD4_3
87 #define BSD4_2
88 #endif
89
90 #ifndef FD_SET
91
92 typedef long    fd_mask;
93 #define NFDBITS (sizeof(fd_mask) * NBBY)        /* bits per mask */
94
95 #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
96 #define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
97 #define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
98 #define FD_ZERO(p)      memset((p), 0, sizeof(*(p)))
99 #endif
100
101 /*
102  * Prototype definitions 
103  */
104 static int      snmp_synch_input(int op, netsnmp_session * session,
105                                  int reqid, netsnmp_pdu *pdu, void *magic);
106
107 netsnmp_pdu    *
108 snmp_pdu_create(int command)
109 {
110     netsnmp_pdu    *pdu;
111
112     pdu = (netsnmp_pdu *) calloc(1, sizeof(netsnmp_pdu));
113     if (pdu) {
114         pdu->version = SNMP_DEFAULT_VERSION;
115         pdu->command = command;
116         pdu->errstat = SNMP_DEFAULT_ERRSTAT;
117         pdu->errindex = SNMP_DEFAULT_ERRINDEX;
118         pdu->securityModel = SNMP_DEFAULT_SECMODEL;
119         pdu->transport_data = NULL;
120         pdu->transport_data_length = 0;
121         pdu->securityNameLen = 0;
122         pdu->contextNameLen = 0;
123         pdu->time = 0;
124         pdu->reqid = snmp_get_next_reqid();
125         pdu->msgid = snmp_get_next_msgid();
126     }
127     return pdu;
128
129 }
130
131
132 /*
133  * Add a null variable with the requested name to the end of the list of
134  * variables for this pdu.
135  */
136 netsnmp_variable_list *
137 snmp_add_null_var(netsnmp_pdu *pdu, oid * name, size_t name_length)
138 {
139     return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
140 }
141
142
143 static int
144 snmp_synch_input(int op,
145                  netsnmp_session * session,
146                  int reqid, netsnmp_pdu *pdu, void *magic)
147 {
148     struct synch_state *state = (struct synch_state *) magic;
149     int             rpt_type;
150
151     if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) {
152         return 0;
153     }
154
155     state->waiting = 0;
156
157     if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
158         if (pdu->command == SNMP_MSG_REPORT) {
159             rpt_type = snmpv3_get_report_type(pdu);
160             if (SNMPV3_IGNORE_UNAUTH_REPORTS ||
161                 rpt_type == SNMPERR_NOT_IN_TIME_WINDOW) {
162                 state->waiting = 1;
163             }
164             state->pdu = NULL;
165             state->status = STAT_ERROR;
166             session->s_snmp_errno = rpt_type;
167             SET_SNMP_ERROR(rpt_type);
168         } else if (pdu->command == SNMP_MSG_RESPONSE) {
169             /*
170              * clone the pdu to return to snmp_synch_response 
171              */
172             state->pdu = snmp_clone_pdu(pdu);
173             state->status = STAT_SUCCESS;
174             session->s_snmp_errno = SNMPERR_SUCCESS;
175         }
176     } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) {
177         state->pdu = NULL;
178         state->status = STAT_TIMEOUT;
179         session->s_snmp_errno = SNMPERR_TIMEOUT;
180         SET_SNMP_ERROR(SNMPERR_TIMEOUT);
181     } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) {
182         state->pdu = NULL;
183         state->status = STAT_ERROR;
184         session->s_snmp_errno = SNMPERR_ABORT;
185         SET_SNMP_ERROR(SNMPERR_ABORT);
186     }
187
188     return 1;
189 }
190
191
192 /*
193  * Clone an SNMP variable data structure.
194  * Sets pointers to structure private storage, or
195  * allocates larger object identifiers and values as needed.
196  *
197  * Caller must make list association for cloned variable.
198  *
199  * Returns 0 if successful.
200  */
201 int
202 snmp_clone_var(netsnmp_variable_list * var, netsnmp_variable_list * newvar)
203 {
204     if (!newvar || !var)
205         return 1;
206
207     memmove(newvar, var, sizeof(netsnmp_variable_list));
208     newvar->next_variable = 0;
209     newvar->name = 0;
210     newvar->val.string = 0;
211     newvar->data = 0;
212     newvar->dataFreeHook = 0;
213     newvar->index = 0;
214
215     /*
216      * Clone the object identifier and the value.
217      * Allocate memory iff original will not fit into local storage.
218      */
219     if (snmp_set_var_objid(newvar, var->name, var->name_length))
220         return 1;
221
222     /*
223      * need a pointer and a length to copy a string value. 
224      */
225     if (var->val.string && var->val_len) {
226         if (var->val.string != &var->buf[0]) {
227             if (var->val_len <= sizeof(var->buf))
228                 newvar->val.string = newvar->buf;
229             else {
230                 newvar->val.string = (u_char *) malloc(var->val_len);
231                 if (!newvar->val.string)
232                     return 1;
233             }
234             memmove(newvar->val.string, var->val.string, var->val_len);
235         } else {                /* fix the pointer to new local store */
236             newvar->val.string = newvar->buf;
237         }
238     } else {
239         newvar->val.string = 0;
240         newvar->val_len = 0;
241     }
242
243     return 0;
244 }
245
246
247 /*
248  * Possibly make a copy of source memory buffer.
249  * Will reset destination pointer if source pointer is NULL.
250  * Returns 0 if successful, 1 if memory allocation fails.
251  */
252 int
253 snmp_clone_mem(void **dstPtr, void *srcPtr, unsigned len)
254 {
255     *dstPtr = 0;
256     if (srcPtr) {
257         *dstPtr = malloc(len + 1);
258         if (!*dstPtr) {
259             return 1;
260         }
261         memmove(*dstPtr, srcPtr, len);
262         /*
263          * this is for those routines that expect 0-terminated strings!!!
264          * someone should rather have called strdup
265          */
266         ((char *) *dstPtr)[len] = 0;
267     }
268     return 0;
269 }
270
271
272 /*
273  * Walks through a list of varbinds and frees and allocated memory,
274  * restoring pointers to local buffers
275  */
276 void
277 snmp_reset_var_buffers(netsnmp_variable_list * var)
278 {
279     while (var) {
280         if (var->name != var->name_loc) {
281             free(var->name);
282             var->name = var->name_loc;
283             var->name_length = 0;
284         }
285         if (var->val.string != var->buf) {
286             free(var->val.string);
287             var->val.string = var->buf;
288             var->val_len = 0;
289         }
290         var = var->next_variable;
291     }
292 }
293
294 /*
295  * Creates and allocates a clone of the input PDU,
296  * but does NOT copy the variables.
297  * This function should be used with another function,
298  * such as _copy_pdu_vars.
299  *
300  * Returns a pointer to the cloned PDU if successful.
301  * Returns 0 if failure.
302  */
303 static
304 netsnmp_pdu    *
305 _clone_pdu_header(netsnmp_pdu *pdu)
306 {
307     netsnmp_pdu    *newpdu;
308     struct snmp_secmod_def *sptr;
309
310     newpdu = (netsnmp_pdu *) malloc(sizeof(netsnmp_pdu));
311     if (!newpdu)
312         return 0;
313     memmove(newpdu, pdu, sizeof(netsnmp_pdu));
314
315     /*
316      * reset copied pointers if copy fails 
317      */
318     newpdu->variables = 0;
319     newpdu->enterprise = 0;
320     newpdu->community = 0;
321     newpdu->securityEngineID = 0;
322     newpdu->securityName = 0;
323     newpdu->contextEngineID = 0;
324     newpdu->contextName = 0;
325     newpdu->transport_data = 0;
326
327     /*
328      * copy buffers individually. If any copy fails, all are freed. 
329      */
330     if (snmp_clone_mem((void **) &newpdu->enterprise, pdu->enterprise,
331                        sizeof(oid) * pdu->enterprise_length) ||
332         snmp_clone_mem((void **) &newpdu->community, pdu->community,
333                        pdu->community_len) ||
334         snmp_clone_mem((void **) &newpdu->contextEngineID,
335                        pdu->contextEngineID, pdu->contextEngineIDLen)
336         || snmp_clone_mem((void **) &newpdu->securityEngineID,
337                           pdu->securityEngineID, pdu->securityEngineIDLen)
338         || snmp_clone_mem((void **) &newpdu->contextName, pdu->contextName,
339                           pdu->contextNameLen)
340         || snmp_clone_mem((void **) &newpdu->securityName,
341                           pdu->securityName, pdu->securityNameLen)
342         || snmp_clone_mem((void **) &newpdu->transport_data,
343                           pdu->transport_data,
344                           pdu->transport_data_length)) {
345         snmp_free_pdu(newpdu);
346         return 0;
347     }
348 #ifdef BRCM_SNMPV3_SUPPORT
349     if ((sptr = find_sec_mod(newpdu->securityModel)) != NULL &&
350         sptr->pdu_clone != NULL) {
351         /*
352          * call security model if it needs to know about this 
353          */
354         (*sptr->pdu_clone) (pdu, newpdu);
355     }
356 #endif /* BRCM_SNMPV3_SUPPORT */
357     return newpdu;
358 }
359
360 static
361 netsnmp_variable_list *
362 _copy_varlist(netsnmp_variable_list * var,      /* source varList */
363               int errindex,     /* index of variable to drop (if any) */
364               int copy_count)
365 {                               /* !=0 number variables to copy */
366     netsnmp_variable_list *newhead, *newvar, *oldvar;
367     int             ii = 0;
368
369     newhead = NULL;
370     oldvar = NULL;
371
372     while (var && (copy_count-- > 0)) {
373         /*
374          * Drop the specified variable (if applicable) 
375          */
376         if (++ii == errindex) {
377             var = var->next_variable;
378             continue;
379         }
380
381         /*
382          * clone the next variable. Cleanup if alloc fails 
383          */
384         newvar = (netsnmp_variable_list *)
385             malloc(sizeof(netsnmp_variable_list));
386         if (snmp_clone_var(var, newvar)) {
387             if (newvar)
388                 free((char *) newvar);
389             snmp_free_varbind(newhead);
390             return 0;
391         }
392
393         /*
394          * add cloned variable to new list  
395          */
396         if (0 == newhead)
397             newhead = newvar;
398         if (oldvar)
399             oldvar->next_variable = newvar;
400         oldvar = newvar;
401
402         var = var->next_variable;
403     }
404     return newhead;
405 }
406
407
408 /*
409  * Copy some or all variables from source PDU to target PDU.
410  * This function consolidates many of the needs of PDU variables:
411  * Clone PDU : copy all the variables.
412  * Split PDU : skip over some variables to copy other variables.
413  * Fix PDU   : remove variable associated with error index.
414  *
415  * Designed to work with _clone_pdu_header.
416  *
417  * If drop_err is set, drop any variable associated with errindex.
418  * If skip_count is set, skip the number of variable in pdu's list.
419  * While copy_count is greater than zero, copy pdu variables to newpdu.
420  *
421  * If an error occurs, newpdu is freed and pointer is set to 0.
422  *
423  * Returns a pointer to the cloned PDU if successful.
424  * Returns 0 if failure.
425  */
426 static
427 netsnmp_pdu    *
428 _copy_pdu_vars(netsnmp_pdu *pdu,        /* source PDU */
429                netsnmp_pdu *newpdu,     /* target PDU */
430                int drop_err,    /* !=0 drop errored variable */
431                int skip_count,  /* !=0 number of variables to skip */
432                int copy_count)
433 {                               /* !=0 number of variables to copy */
434     netsnmp_variable_list *var, *oldvar;
435     int             ii, copied, drop_idx;
436
437     if (!newpdu)
438         return 0;               /* where is PDU to copy to ? */
439
440     if (drop_err)
441         drop_idx = pdu->errindex - skip_count;
442     else
443         drop_idx = 0;
444
445     var = pdu->variables;
446     while (var && (skip_count-- > 0))   /* skip over pdu variables */
447         var = var->next_variable;
448
449     oldvar = 0;
450     ii = 0;
451     copied = 0;
452     if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY)
453         copied = 1;             /* We're interested in 'empty' responses too */
454
455     newpdu->variables = _copy_varlist(var, drop_idx, copy_count);
456     if (newpdu->variables)
457         copied = 1;
458
459 #if ALSO_TEMPORARILY_DISABLED
460     /*
461      * Error if bad errindex or if target PDU has no variables copied 
462      */
463     if ((drop_err && (ii < pdu->errindex))
464 #if TEMPORARILY_DISABLED
465         /*
466          * SNMPv3 engineID probes are allowed to be empty.
467          * See the comment in snmp_api.c for further details 
468          */
469         || copied == 0
470 #endif
471         ) {
472         snmp_free_pdu(newpdu);
473         return 0;
474     }
475 #endif
476     return newpdu;
477 }
478
479
480 /*
481  * Creates (allocates and copies) a clone of the input PDU.
482  * If drop_err is set, don't copy any variable associated with errindex.
483  * This function is called by snmp_clone_pdu and snmp_fix_pdu.
484  *
485  * Returns a pointer to the cloned PDU if successful.
486  * Returns 0 if failure.
487  */
488 static
489 netsnmp_pdu    *
490 _clone_pdu(netsnmp_pdu *pdu, int drop_err)
491 {
492     netsnmp_pdu    *newpdu;
493     newpdu = _clone_pdu_header(pdu);
494     newpdu = _copy_pdu_vars(pdu, newpdu, drop_err, 0, 10000);   /* skip none, copy all */
495
496     return newpdu;
497 }
498
499
500 /*
501  * This function will clone a full varbind list
502  *
503  * Returns a pointer to the cloned PDU if successful.
504  * Returns 0 if failure
505  */
506 netsnmp_variable_list *
507 snmp_clone_varbind(netsnmp_variable_list * varlist)
508 {
509     return _copy_varlist(varlist, 0, 10000);    /* skip none, copy all */
510 }
511
512 /*
513  * This function will clone a PDU including all of its variables.
514  *
515  * Returns a pointer to the cloned PDU if successful.
516  * Returns 0 if failure
517  */
518 netsnmp_pdu    *
519 snmp_clone_pdu(netsnmp_pdu *pdu)
520 {
521     return _clone_pdu(pdu, 0);  /* copies all variables */
522 }
523
524
525 /*
526  * This function will clone a PDU including some of its variables.
527  *
528  * If skip_count is not zero, it defines the number of variables to skip.
529  * If copy_count is not zero, it defines the number of variables to copy.
530  *
531  * Returns a pointer to the cloned PDU if successful.
532  * Returns 0 if failure.
533  */
534 netsnmp_pdu    *
535 snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count)
536 {
537     netsnmp_pdu    *newpdu;
538     newpdu = _clone_pdu_header(pdu);
539     newpdu = _copy_pdu_vars(pdu, newpdu, 0,     /* don't drop any variables */
540                             skip_count, copy_count);
541
542     return newpdu;
543 }
544
545
546 /*
547  * If there was an error in the input pdu, creates a clone of the pdu
548  * that includes all the variables except the one marked by the errindex.
549  * The command is set to the input command and the reqid, errstat, and
550  * errindex are set to default values.
551  * If the error status didn't indicate an error, the error index didn't
552  * indicate a variable, the pdu wasn't a get response message, or there
553  * would be no remaining variables, this function will return 0.
554  * If everything was successful, a pointer to the fixed cloned pdu will
555  * be returned.
556  */
557 netsnmp_pdu    *
558 snmp_fix_pdu(netsnmp_pdu *pdu, int command)
559 {
560     netsnmp_pdu    *newpdu;
561
562     if ((pdu->command != SNMP_MSG_RESPONSE)
563         || (pdu->errstat == SNMP_ERR_NOERROR)
564         || (0 == pdu->variables)
565         || (pdu->errindex <= 0)) {
566         return 0;               /* pre-condition tests fail */
567     }
568
569     newpdu = _clone_pdu(pdu, 1);        /* copies all except errored variable */
570     if (!newpdu)
571         return 0;
572     if (!newpdu->variables) {
573         snmp_free_pdu(newpdu);
574         return 0;               /* no variables. "should not happen" */
575     }
576     newpdu->command = command;
577     newpdu->reqid = snmp_get_next_reqid();
578     newpdu->msgid = snmp_get_next_msgid();
579     newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
580     newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
581
582     return newpdu;
583 }
584
585
586 /*
587  * Returns the number of variables bound to a PDU structure
588  */
589 unsigned long
590 snmp_varbind_len(netsnmp_pdu *pdu)
591 {
592     register netsnmp_variable_list *vars;
593     unsigned long   retVal = 0;
594     if (pdu)
595         for (vars = pdu->variables; vars; vars = vars->next_variable) {
596             retVal++;
597         }
598
599     return retVal;
600 }
601
602 /*
603  * Add object identifier name to SNMP variable.
604  * If the name is large, additional memory is allocated.
605  * Returns 0 if successful.
606  */
607
608 int
609 snmp_set_var_objid(netsnmp_variable_list * vp,
610                    const oid * objid, size_t name_length)
611 {
612     size_t          len = sizeof(oid) * name_length;
613
614     if (vp->name != vp->name_loc && vp->name != NULL &&
615         vp->name_length > (sizeof(vp->name_loc) / sizeof(oid))) {
616         /*
617          * Probably previously-allocated "big storage".  Better free it
618          * else memory leaks possible.  
619          */
620         free(vp->name);
621     }
622
623     /*
624      * use built-in storage for smaller values 
625      */
626     if (len <= sizeof(vp->name_loc)) {
627         vp->name = vp->name_loc;
628     } else {
629         vp->name = (oid *) malloc(len);
630         if (!vp->name)
631             return 1;
632     }
633     if (objid)
634         memmove(vp->name, objid, len);
635     vp->name_length = name_length;
636     return 0;
637 }
638
639 int
640 snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type,
641                          const u_char * val_str, size_t val_len)
642 {
643     newvar->type = type;
644     return snmp_set_var_value(newvar, val_str, val_len);
645 }
646
647 int
648 count_varbinds(netsnmp_variable_list * var_ptr)
649 {
650     int             count = 0;
651
652     for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
653         count++;
654
655     return count;
656 }
657
658 int
659 count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type)
660 {
661     int             count = 0;
662
663     for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
664         if (var_ptr->type == type)
665             count++;
666
667     return count;
668 }
669
670 netsnmp_variable_list *
671 find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type)
672 {
673     for (; var_ptr != NULL && var_ptr->type != type;
674          var_ptr = var_ptr->next_variable);
675
676     return var_ptr;
677 }
678
679 /*
680  * Add some value to SNMP variable.
681  * If the value is large, additional memory is allocated.
682  * Returns 0 if successful.
683  */
684
685 int
686 snmp_set_var_value(netsnmp_variable_list * newvar,
687                    const u_char * val_str, size_t val_len)
688 {
689     if (newvar->val.string && newvar->val.string != newvar->buf) {
690         free(newvar->val.string);
691     }
692
693     newvar->val.string = 0;
694     newvar->val_len = 0;
695
696     /*
697      * need a pointer and a length to copy a string value. 
698      */
699     if (val_str && val_len) {
700         if (val_len <= sizeof(newvar->buf))
701             newvar->val.string = newvar->buf;
702         else {
703             newvar->val.string = (u_char *) malloc(val_len);
704             if (!newvar->val.string)
705                 return 1;
706         }
707         memmove(newvar->val.string, val_str, val_len);
708         newvar->val_len = val_len;
709     } else if (val_str) {
710         /*
711          * NULL STRING != NULL ptr 
712          */
713         newvar->val.string = newvar->buf;
714         newvar->val.string[0] = '\0';
715         newvar->val_len = 0;
716     }
717     return 0;
718 }
719
720 void
721 snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type,
722                        u_char new_type)
723 {
724     while (vbl) {
725         if (vbl->type == old_type) {
726             snmp_set_var_typed_value(vbl, new_type, NULL, 0);
727         }
728         vbl = vbl->next_variable;
729     }
730 }
731
732 void
733 snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type)
734 {
735     while (vbl) {
736         snmp_set_var_typed_value(vbl, new_type, NULL, 0);
737         vbl = vbl->next_variable;
738     }
739 }
740
741 int
742 snmp_synch_response_cb(netsnmp_session * ss,
743                        netsnmp_pdu *pdu,
744                        netsnmp_pdu **response, snmp_callback pcb)
745 {
746     struct synch_state lstate, *state;
747     snmp_callback   cbsav;
748     void           *cbmagsav;
749     int             numfds, count;
750     fd_set          fdset;
751     struct timeval  timeout, *tvp;
752     int             block;
753
754     memset((void *) &lstate, 0, sizeof(lstate));
755     state = &lstate;
756     cbsav = ss->callback;
757     cbmagsav = ss->callback_magic;
758     ss->callback = pcb;
759     ss->callback_magic = (void *) state;
760
761     if ((state->reqid = snmp_send(ss, pdu)) == 0) {
762         snmp_free_pdu(pdu);
763         state->status = STAT_ERROR;
764     } else
765         state->waiting = 1;
766
767     while (state->waiting) {
768         numfds = 0;
769         FD_ZERO(&fdset);
770         block = SNMPBLOCK;
771         tvp = &timeout;
772         timerclear(tvp);
773         snmp_select_info(&numfds, &fdset, tvp, &block);
774         if (block == 1)
775             tvp = NULL;         /* block without timeout */
776         count = select(numfds, &fdset, 0, 0, tvp);
777         if (count > 0) {
778             snmp_read(&fdset);
779         } else
780             switch (count) {
781             case 0:
782                 snmp_timeout();
783                 break;
784             case -1:
785                 if (errno == EINTR) {
786                     continue;
787                 } else {
788                     snmp_errno = SNMPERR_GENERR;
789                     /*
790                      * CAUTION! if another thread closed the socket(s)
791                      * waited on here, the session structure was freed.
792                      * It would be nice, but we can't rely on the pointer.
793                      * ss->s_snmp_errno = SNMPERR_GENERR;
794                      * ss->s_errno = errno;
795                      */
796                     snmp_set_detail(strerror(errno));
797                 }
798                 /*
799                  * FALLTHRU 
800                  */
801             default:
802                 state->status = STAT_ERROR;
803                 state->waiting = 0;
804             }
805     }
806     *response = state->pdu;
807     ss->callback = cbsav;
808     ss->callback_magic = cbmagsav;
809     return state->status;
810 }
811
812 int
813 snmp_synch_response(netsnmp_session * ss,
814                     netsnmp_pdu *pdu, netsnmp_pdu **response)
815 {
816     return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input);
817 }
818
819 int
820 snmp_sess_synch_response(void *sessp,
821                          netsnmp_pdu *pdu, netsnmp_pdu **response)
822 {
823     netsnmp_session *ss;
824     struct synch_state lstate, *state;
825     snmp_callback   cbsav;
826     void           *cbmagsav;
827     int             numfds, count;
828     fd_set          fdset;
829     struct timeval  timeout, *tvp;
830     int             block;
831
832     ss = snmp_sess_session(sessp);
833     memset((void *) &lstate, 0, sizeof(lstate));
834     state = &lstate;
835     cbsav = ss->callback;
836     cbmagsav = ss->callback_magic;
837     ss->callback = snmp_synch_input;
838     ss->callback_magic = (void *) state;
839
840     if ((state->reqid = snmp_sess_send(sessp, pdu)) == 0) {
841         snmp_free_pdu(pdu);
842         state->status = STAT_ERROR;
843     } else
844         state->waiting = 1;
845
846     while (state->waiting) {
847         numfds = 0;
848         FD_ZERO(&fdset);
849         block = SNMPBLOCK;
850         tvp = &timeout;
851         timerclear(tvp);
852         snmp_sess_select_info(sessp, &numfds, &fdset, tvp, &block);
853         if (block == 1)
854             tvp = NULL;         /* block without timeout */
855         count = select(numfds, &fdset, 0, 0, tvp);
856         if (count > 0) {
857             snmp_sess_read(sessp, &fdset);
858         } else
859             switch (count) {
860             case 0:
861                 snmp_sess_timeout(sessp);
862                 break;
863             case -1:
864                 if (errno == EINTR) {
865                     continue;
866                 } else {
867                     snmp_errno = SNMPERR_GENERR;
868                     /*
869                      * CAUTION! if another thread closed the socket(s)
870                      * waited on here, the session structure was freed.
871                      * It would be nice, but we can't rely on the pointer.
872                      * ss->s_snmp_errno = SNMPERR_GENERR;
873                      * ss->s_errno = errno;
874                      */
875                     snmp_set_detail(strerror(errno));
876                 }
877                 /*
878                  * FALLTHRU 
879                  */
880             default:
881                 state->status = STAT_ERROR;
882                 state->waiting = 0;
883             }
884     }
885     *response = state->pdu;
886     ss->callback = cbsav;
887     ss->callback_magic = cbmagsav;
888     return state->status;
889 }
890
891
892 const char     *error_string[19] = {
893     "(noError) No Error",
894     "(tooBig) Response message would have been too large.",
895     "(noSuchName) There is no such variable name in this MIB.",
896     "(badValue) The value given has the wrong type or length.",
897     "(readOnly) The two parties used do not have access to use the specified SNMP PDU.",
898     "(genError) A general failure occured",
899     "noAccess",
900     "wrongType (The set datatype does not match the data type the agent expects)",
901     "wrongLength (The set value has an illegal length from what the agent expects)",
902     "wrongEncoding",
903     "wrongValue (The set value is illegal or unsupported in some way)",
904     "noCreation (that table does not support row creation)",
905     "inconsistentValue (The set value is illegal or unsupported in some way)",
906     "resourceUnavailable (This is likely a out-of-memory failure within the agent)",
907     "commitFailed",
908     "undoFailed",
909     "authorizationError (access denied to that object)",
910     "notWritable (that object does not support modification)",
911     "inconsistentName"
912 };
913
914 const char     *
915 snmp_errstring(int errstat)
916 {
917     if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) {
918         return error_string[errstat];
919     } else {
920         return "Unknown Error";
921     }
922 }
923