net-snmp 5.7
snmp_api.c
00001 
00002 /* Portions of this file are subject to the following copyright(s).  See
00003  * the Net-SNMP's COPYING file for more details and other copyrights
00004  * that may apply:
00005  */
00006 /******************************************************************
00007         Copyright 1989, 1991, 1992 by Carnegie Mellon University
00008 
00009                       All Rights Reserved
00010 
00011 Permission to use, copy, modify, and distribute this software and its
00012 documentation for any purpose and without fee is hereby granted,
00013 provided that the above copyright notice appear in all copies and that
00014 both that copyright notice and this permission notice appear in
00015 supporting documentation, and that the name of CMU not be
00016 used in advertising or publicity pertaining to distribution of the
00017 software without specific, written prior permission.
00018 
00019 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
00020 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
00021 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
00022 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
00023 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
00024 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00025 SOFTWARE.
00026 ******************************************************************/
00027 /*
00028  * Portions of this file are copyrighted by:
00029  * Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved.
00030  * Use is subject to license terms specified in the COPYING file
00031  * distributed with the Net-SNMP package.
00032  */
00033 
00037 /*
00038  * snmp_api.c - API for access to snmp.
00039  */
00040 #include <net-snmp/net-snmp-config.h>
00041 #include <net-snmp/net-snmp-features.h>
00042 
00043 #include <stdio.h>
00044 #include <ctype.h>
00045 #if HAVE_STDLIB_H
00046 #include <stdlib.h>
00047 #endif
00048 #if HAVE_STRING_H
00049 #include <string.h>
00050 #else
00051 #include <strings.h>
00052 #endif
00053 #if HAVE_UNISTD_H
00054 #include <unistd.h>
00055 #endif
00056 #include <sys/types.h>
00057 #if HAVE_SYS_PARAM_H
00058 #include <sys/param.h>
00059 #endif
00060 #if TIME_WITH_SYS_TIME
00061 # include <sys/time.h>
00062 # include <time.h>
00063 #else
00064 # if HAVE_SYS_TIME_H
00065 #  include <sys/time.h>
00066 # else
00067 #  include <time.h>
00068 # endif
00069 #endif
00070 #if HAVE_NETINET_IN_H
00071 #include <netinet/in.h>
00072 #endif
00073 #if HAVE_ARPA_INET_H
00074 #include <arpa/inet.h>
00075 #endif
00076 #if HAVE_SYS_SELECT_H
00077 #include <sys/select.h>
00078 #endif
00079 #if HAVE_IO_H
00080 #include <io.h>
00081 #endif
00082 #if HAVE_SYS_SOCKET_H
00083 #include <sys/socket.h>
00084 #endif
00085 #if HAVE_SYS_UN_H
00086 #include <sys/un.h>
00087 #endif
00088 #if HAVE_NETDB_H
00089 #include <netdb.h>
00090 #endif
00091 #if HAVE_NET_IF_DL_H
00092 #ifndef dynix
00093 #include <net/if_dl.h>
00094 #else
00095 #include <sys/net/if_dl.h>
00096 #endif
00097 #endif
00098 #include <errno.h>
00099 
00100 #if HAVE_LOCALE_H
00101 #include <locale.h>
00102 #endif
00103 
00104 #if HAVE_DMALLOC_H
00105 #include <dmalloc.h>
00106 #endif
00107 
00108 #define SNMP_NEED_REQUEST_LIST
00109 #include <net-snmp/types.h>
00110 #include <net-snmp/output_api.h>
00111 #include <net-snmp/config_api.h>
00112 #include <net-snmp/utilities.h>
00113 
00114 #include <net-snmp/library/asn1.h>
00115 #include <net-snmp/library/snmp.h>      /* for xdump & {build,parse}_var_op */
00116 #include <net-snmp/library/snmp_api.h>
00117 #include <net-snmp/library/snmp_client.h>
00118 #include <net-snmp/library/parse.h>
00119 #include <net-snmp/library/mib.h>
00120 #include <net-snmp/library/int64.h>
00121 #include <net-snmp/library/snmpv3.h>
00122 #include <net-snmp/library/callback.h>
00123 #include <net-snmp/library/container.h>
00124 #include <net-snmp/library/snmp_secmod.h>
00125 #include <net-snmp/library/large_fd_set.h>
00126 #ifdef NETSNMP_SECMOD_USM
00127 #include <net-snmp/library/snmpusm.h>
00128 #endif
00129 #ifdef NETSNMP_SECMOD_KSM
00130 #include <net-snmp/library/snmpksm.h>
00131 #endif
00132 #include <net-snmp/library/keytools.h>
00133 #include <net-snmp/library/lcd_time.h>
00134 #include <net-snmp/library/snmp_alarm.h>
00135 #include <net-snmp/library/snmp_transport.h>
00136 #include <net-snmp/library/snmp_service.h>
00137 #include <net-snmp/library/vacm.h>
00138 
00139 netsnmp_feature_child_of(statistics, libnetsnmp)
00140 netsnmp_feature_child_of(snmp_api, libnetsnmp)
00141 netsnmp_feature_child_of(oid_is_subtree, snmp_api)
00142 netsnmp_feature_child_of(snmpv3_probe_contextEngineID_rfc5343, snmp_api)
00143 
00144 #if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL)
00145 extern void netsnmp_certs_init(void);
00146 extern void netsnmp_certs_shutdown(void);
00147 #endif
00148 
00149 static void     _init_snmp(void);
00150 
00151 static int      _snmp_store_needed = 0;
00152 
00153 #include "../agent/mibgroup/agentx/protocol.h"
00154 #include <net-snmp/library/transform_oids.h>
00155 #ifndef timercmp
00156 #define timercmp(tvp, uvp, cmp) \
00157         /* CSTYLED */ \
00158         ((tvp)->tv_sec cmp (uvp)->tv_sec || \
00159         ((tvp)->tv_sec == (uvp)->tv_sec && \
00160         /* CSTYLED */ \
00161         (tvp)->tv_usec cmp (uvp)->tv_usec))
00162 #endif
00163 #ifndef timerclear
00164 #define timerclear(tvp)         (tvp)->tv_sec = (tvp)->tv_usec = 0
00165 #endif
00166 
00167 /*
00168  * Globals.
00169  */
00170 #define MAX_PACKET_LENGTH       (0x7fffffff)
00171 #ifndef NETSNMP_STREAM_QUEUE_LEN
00172 #define NETSNMP_STREAM_QUEUE_LEN  5
00173 #endif
00174 
00175 #ifndef BSD4_3
00176 #define BSD4_2
00177 #endif
00178 
00179 #ifndef FD_SET
00180 
00181 typedef long    fd_mask;
00182 #define NFDBITS (sizeof(fd_mask) * NBBY)        /* bits per mask */
00183 
00184 #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
00185 #define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
00186 #define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
00187 #define FD_ZERO(p)      memset((p), 0, sizeof(*(p)))
00188 #endif
00189 
00190 static oid      default_enterprise[] = { 1, 3, 6, 1, 4, 1, 3, 1, 1 };
00191 /*
00192  * enterprises.cmu.systems.cmuSNMP 
00193  */
00194 
00195 #define DEFAULT_COMMUNITY   "public"
00196 #define DEFAULT_RETRIES     5
00197 #define DEFAULT_TIMEOUT     ONE_SEC
00198 #define DEFAULT_REMPORT     SNMP_PORT
00199 #define DEFAULT_ENTERPRISE  default_enterprise
00200 #define DEFAULT_TIME        0
00201 
00202 /*
00203  * don't set higher than 0x7fffffff, and I doubt it should be that high
00204  * * = 4 gig snmp messages max 
00205  */
00206 #define MAXIMUM_PACKET_SIZE 0x7fffffff
00207 
00208 /*
00209  * Internal information about the state of the snmp session.
00210  */
00211 struct snmp_internal_session {
00212     netsnmp_request_list *requests;     /* Info about outstanding requests */
00213     netsnmp_request_list *requestsEnd;  /* ptr to end of list */
00214     int             (*hook_pre) (netsnmp_session *, netsnmp_transport *,
00215                                  void *, int);
00216     int             (*hook_parse) (netsnmp_session *, netsnmp_pdu *,
00217                                    u_char *, size_t);
00218     int             (*hook_post) (netsnmp_session *, netsnmp_pdu *, int);
00219     int             (*hook_build) (netsnmp_session *, netsnmp_pdu *,
00220                                    u_char *, size_t *);
00221     int             (*hook_realloc_build) (netsnmp_session *,
00222                                            netsnmp_pdu *, u_char **,
00223                                            size_t *, size_t *);
00224     int             (*check_packet) (u_char *, size_t);
00225     netsnmp_pdu    *(*hook_create_pdu) (netsnmp_transport *,
00226                                         void *, size_t);
00227 
00228     u_char         *packet;
00229     size_t          packet_len, packet_size;
00230 };
00231 
00232 static const char *api_errors[-SNMPERR_MAX + 1] = {
00233     "No error",                 /* SNMPERR_SUCCESS */
00234     "Generic error",            /* SNMPERR_GENERR */
00235     "Invalid local port",       /* SNMPERR_BAD_LOCPORT */
00236     "Unknown host",             /* SNMPERR_BAD_ADDRESS */
00237     "Unknown session",          /* SNMPERR_BAD_SESSION */
00238     "Too long",                 /* SNMPERR_TOO_LONG */
00239     "No socket",                /* SNMPERR_NO_SOCKET */
00240     "Cannot send V2 PDU on V1 session", /* SNMPERR_V2_IN_V1 */
00241     "Cannot send V1 PDU on V2 session", /* SNMPERR_V1_IN_V2 */
00242     "Bad value for non-repeaters",      /* SNMPERR_BAD_REPEATERS */
00243     "Bad value for max-repetitions",    /* SNMPERR_BAD_REPETITIONS */
00244     "Error building ASN.1 representation",      /* SNMPERR_BAD_ASN1_BUILD */
00245     "Failure in sendto",        /* SNMPERR_BAD_SENDTO */
00246     "Bad parse of ASN.1 type",  /* SNMPERR_BAD_PARSE */
00247     "Bad version specified",    /* SNMPERR_BAD_VERSION */
00248     "Bad source party specified",       /* SNMPERR_BAD_SRC_PARTY */
00249     "Bad destination party specified",  /* SNMPERR_BAD_DST_PARTY */
00250     "Bad context specified",    /* SNMPERR_BAD_CONTEXT */
00251     "Bad community specified",  /* SNMPERR_BAD_COMMUNITY */
00252     "Cannot send noAuth/Priv",       /* SNMPERR_NOAUTH_DESPRIV */
00253     "Bad ACL definition",       /* SNMPERR_BAD_ACL */
00254     "Bad Party definition",     /* SNMPERR_BAD_PARTY */
00255     "Session abort failure",    /* SNMPERR_ABORT */
00256     "Unknown PDU type",         /* SNMPERR_UNKNOWN_PDU */
00257     "Timeout",                  /* SNMPERR_TIMEOUT */
00258     "Failure in recvfrom",      /* SNMPERR_BAD_RECVFROM */
00259     "Unable to determine contextEngineID",      /* SNMPERR_BAD_ENG_ID */
00260     "No securityName specified",        /* SNMPERR_BAD_SEC_NAME */
00261     "Unable to determine securityLevel",        /* SNMPERR_BAD_SEC_LEVEL  */
00262     "ASN.1 parse error in message",     /* SNMPERR_ASN_PARSE_ERR */
00263     "Unknown security model in message",        /* SNMPERR_UNKNOWN_SEC_MODEL */
00264     "Invalid message (e.g. msgFlags)",  /* SNMPERR_INVALID_MSG */
00265     "Unknown engine ID",        /* SNMPERR_UNKNOWN_ENG_ID */
00266     "Unknown user name",        /* SNMPERR_UNKNOWN_USER_NAME */
00267     "Unsupported security level",       /* SNMPERR_UNSUPPORTED_SEC_LEVEL */
00268     "Authentication failure (incorrect password, community or key)",    /* SNMPERR_AUTHENTICATION_FAILURE */
00269     "Not in time window",       /* SNMPERR_NOT_IN_TIME_WINDOW */
00270     "Decryption error",         /* SNMPERR_DECRYPTION_ERR */
00271     "SCAPI general failure",    /* SNMPERR_SC_GENERAL_FAILURE */
00272     "SCAPI sub-system not configured",  /* SNMPERR_SC_NOT_CONFIGURED */
00273     "Key tools not available",  /* SNMPERR_KT_NOT_AVAILABLE */
00274     "Unknown Report message",   /* SNMPERR_UNKNOWN_REPORT */
00275     "USM generic error",        /* SNMPERR_USM_GENERICERROR */
00276     "USM unknown security name (no such user exists)",  /* SNMPERR_USM_UNKNOWNSECURITYNAME */
00277     "USM unsupported security level (this user has not been configured for that level of security)",    /* SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL */
00278     "USM encryption error",     /* SNMPERR_USM_ENCRYPTIONERROR */
00279     "USM authentication failure (incorrect password or key)",   /* SNMPERR_USM_AUTHENTICATIONFAILURE */
00280     "USM parse error",          /* SNMPERR_USM_PARSEERROR */
00281     "USM unknown engineID",     /* SNMPERR_USM_UNKNOWNENGINEID */
00282     "USM not in time window",   /* SNMPERR_USM_NOTINTIMEWINDOW */
00283     "USM decryption error",     /* SNMPERR_USM_DECRYPTIONERROR */
00284     "MIB not initialized",      /* SNMPERR_NOMIB */
00285     "Value out of range",       /* SNMPERR_RANGE */
00286     "Sub-id out of range",      /* SNMPERR_MAX_SUBID */
00287     "Bad sub-id in object identifier",  /* SNMPERR_BAD_SUBID */
00288     "Object identifier too long",       /* SNMPERR_LONG_OID */
00289     "Bad value name",           /* SNMPERR_BAD_NAME */
00290     "Bad value notation",       /* SNMPERR_VALUE */
00291     "Unknown Object Identifier",        /* SNMPERR_UNKNOWN_OBJID */
00292     "No PDU in snmp_send",      /* SNMPERR_NULL_PDU */
00293     "Missing variables in PDU", /* SNMPERR_NO_VARS */
00294     "Bad variable type",        /* SNMPERR_VAR_TYPE */
00295     "Out of memory (malloc failure)",   /* SNMPERR_MALLOC */
00296     "Kerberos related error",   /* SNMPERR_KRB5 */
00297     "Protocol error",           /* SNMPERR_PROTOCOL */
00298     "OID not increasing",       /* SNMPERR_OID_NONINCREASING */
00299     "Context probe",            /* SNMPERR_JUST_A_CONTEXT_PROBE */
00300     "Configuration data found but the transport can't be configured", /* SNMPERR_TRANSPORT_NO_CONFIG */
00301     "Transport configuration failed", /* SNMPERR_TRANSPORT_CONFIG_ERROR */
00302 };
00303 
00304 static const char *secLevelName[] = {
00305     "BAD_SEC_LEVEL",
00306     "noAuthNoPriv",
00307     "authNoPriv",
00308     "authPriv"
00309 };
00310 
00311 /*
00312  * Multiple threads may changes these variables.
00313  * Suggest using the Single API, which does not use Sessions.
00314  *
00315  * Reqid may need to be protected. Time will tell...
00316  *
00317  */
00318 /*
00319  * MTCRITICAL_RESOURCE
00320  */
00321 /*
00322  * use token in comments to individually protect these resources 
00323  */
00324 struct session_list *Sessions = NULL;   /* MT_LIB_SESSION */
00325 static long     Reqid = 0;      /* MT_LIB_REQUESTID */
00326 static long     Msgid = 0;      /* MT_LIB_MESSAGEID */
00327 static long     Sessid = 0;     /* MT_LIB_SESSIONID */
00328 static long     Transid = 0;    /* MT_LIB_TRANSID */
00329 int             snmp_errno = 0;
00330 /*
00331  * END MTCRITICAL_RESOURCE
00332  */
00333 
00334 /*
00335  * global error detail storage
00336  */
00337 static char     snmp_detail[192];
00338 static int      snmp_detail_f = 0;
00339 
00340 /*
00341  * Prototypes.
00342  */
00343 int             snmp_build(u_char ** pkt, size_t * pkt_len,
00344                            size_t * offset, netsnmp_session * pss,
00345                            netsnmp_pdu *pdu);
00346 static int      snmp_parse(void *, netsnmp_session *, netsnmp_pdu *,
00347                            u_char *, size_t);
00348 
00349 static void     snmpv3_calc_msg_flags(int, int, u_char *);
00350 static int      snmpv3_verify_msg(netsnmp_request_list *, netsnmp_pdu *);
00351 static int      snmpv3_build(u_char ** pkt, size_t * pkt_len,
00352                              size_t * offset, netsnmp_session * session,
00353                              netsnmp_pdu *pdu);
00354 static int      snmp_parse_version(u_char *, size_t);
00355 static int      snmp_resend_request(struct session_list *slp,
00356                                     netsnmp_request_list *rp,
00357                                     int incr_retries);
00358 static void     register_default_handlers(void);
00359 static struct session_list *snmp_sess_copy(netsnmp_session * pss);
00360 int             snmp_get_errno(void);
00361 NETSNMP_IMPORT
00362 void            snmp_synch_reset(netsnmp_session * notused);
00363 NETSNMP_IMPORT
00364 void            snmp_synch_setup(netsnmp_session * notused);
00365 
00366 #ifndef HAVE_STRERROR
00367 const char     *
00368 strerror(int err)
00369 {
00370     extern const char *sys_errlist[];
00371     extern int      sys_nerr;
00372 
00373     if (err < 0 || err >= sys_nerr)
00374         return "Unknown error";
00375     return sys_errlist[err];
00376 }
00377 #endif
00378 
00379 const char *
00380 snmp_pdu_type(int type)
00381 {
00382     static char unknown[20];
00383     switch(type) {
00384     case SNMP_MSG_GET:
00385         return "GET";
00386     case SNMP_MSG_GETNEXT:
00387         return "GETNEXT";
00388     case SNMP_MSG_GETBULK:
00389         return "GETBULK";
00390 #ifndef NETSNMP_NO_WRITE_SUPPORT
00391     case SNMP_MSG_SET:
00392         return "SET";
00393 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
00394     case SNMP_MSG_RESPONSE:
00395         return "RESPONSE";
00396     case SNMP_MSG_INFORM:
00397         return "INFORM";
00398     case SNMP_MSG_TRAP2:
00399         return "TRAP2";
00400     case SNMP_MSG_REPORT:
00401         return "REPORT";
00402     default:
00403         snprintf(unknown, sizeof(unknown), "?0x%2X?", type);
00404         return unknown;
00405     }
00406 }
00407 
00408 #define DEBUGPRINTPDUTYPE(token, type) \
00409     DEBUGDUMPSECTION(token, snmp_pdu_type(type))
00410 
00411 long
00412 snmp_get_next_reqid(void)
00413 {
00414     long            retVal;
00415     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_REQUESTID);
00416     retVal = 1 + Reqid;         /*MTCRITICAL_RESOURCE */
00417     if (!retVal)
00418         retVal = 2;
00419     Reqid = retVal;
00420     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
00421         retVal &= 0x7fff;       /* mask to 15 bits */
00422     else
00423         retVal &= 0x7fffffff;   /* mask to 31 bits */
00424 
00425     if (!retVal) {
00426         Reqid = retVal = 2;
00427     }
00428     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_REQUESTID);
00429     return retVal;
00430 }
00431 
00432 long
00433 snmp_get_next_msgid(void)
00434 {
00435     long            retVal;
00436     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_MESSAGEID);
00437     retVal = 1 + Msgid;         /*MTCRITICAL_RESOURCE */
00438     if (!retVal)
00439         retVal = 2;
00440     Msgid = retVal;
00441     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
00442         retVal &= 0x7fff;       /* mask to 15 bits */
00443     else
00444         retVal &= 0x7fffffff;   /* mask to 31 bits */
00445 
00446     if (!retVal) {
00447         Msgid = retVal = 2;
00448     }
00449     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_MESSAGEID);
00450     return retVal;
00451 }
00452 
00453 long
00454 snmp_get_next_sessid(void)
00455 {
00456     long            retVal;
00457     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSIONID);
00458     retVal = 1 + Sessid;        /*MTCRITICAL_RESOURCE */
00459     if (!retVal)
00460         retVal = 2;
00461     Sessid = retVal;
00462     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
00463         retVal &= 0x7fff;       /* mask to 15 bits */
00464     else
00465         retVal &= 0x7fffffff;   /* mask to 31 bits */
00466 
00467     if (!retVal) {
00468         Sessid = retVal = 2;
00469     }
00470     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSIONID);
00471     return retVal;
00472 }
00473 
00474 long
00475 snmp_get_next_transid(void)
00476 {
00477     long            retVal;
00478     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_TRANSID);
00479     retVal = 1 + Transid;       /*MTCRITICAL_RESOURCE */
00480     if (!retVal)
00481         retVal = 2;
00482     Transid = retVal;
00483     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
00484         retVal &= 0x7fff;       /* mask to 15 bits */
00485     else
00486         retVal &= 0x7fffffff;   /* mask to 31 bits */
00487 
00488     if (!retVal) {
00489         Transid = retVal = 2;
00490     }
00491     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_TRANSID);
00492     return retVal;
00493 }
00494 
00495 void
00496 snmp_perror(const char *prog_string)
00497 {
00498     const char     *str;
00499     int             xerr;
00500     xerr = snmp_errno;          /*MTCRITICAL_RESOURCE */
00501     str = snmp_api_errstring(xerr);
00502     snmp_log(LOG_ERR, "%s: %s\n", prog_string, str);
00503 }
00504 
00505 void
00506 snmp_set_detail(const char *detail_string)
00507 {
00508     if (detail_string != NULL) {
00509         strncpy((char *) snmp_detail, detail_string, sizeof(snmp_detail));
00510         snmp_detail[sizeof(snmp_detail) - 1] = '\0';
00511         snmp_detail_f = 1;
00512     }
00513 }
00514 
00515 /*
00516  * returns pointer to static data 
00517  */
00518 /*
00519  * results not guaranteed in multi-threaded use 
00520  */
00521 const char     *
00522 snmp_api_errstring(int snmp_errnumber)
00523 {
00524     const char     *msg = "";
00525     static char     msg_buf[SPRINT_MAX_LEN];
00526     if (snmp_errnumber >= SNMPERR_MAX && snmp_errnumber <= SNMPERR_GENERR) {
00527         msg = api_errors[-snmp_errnumber];
00528     } else if (snmp_errnumber != SNMPERR_SUCCESS) {
00529         msg = NULL;
00530     }
00531     if (!msg)
00532         snprintf(msg_buf, sizeof(msg_buf), "Unknown error: %d", snmp_errnumber);
00533     else if (snmp_detail_f) {
00534         snprintf(msg_buf, sizeof(msg_buf), "%s (%s)", msg, snmp_detail);
00535         snmp_detail_f = 0;
00536     } else {
00537         strncpy(msg_buf, msg, sizeof(msg_buf));
00538     }
00539     msg_buf[sizeof(msg_buf)-1] = '\0';
00540 
00541     return (msg_buf);
00542 }
00543 
00544 /*
00545  * snmp_error - return error data
00546  * Inputs :  address of errno, address of snmp_errno, address of string
00547  * Caller must free the string returned after use.
00548  */
00549 void
00550 snmp_error(netsnmp_session * psess,
00551            int *p_errno, int *p_snmp_errno, char **p_str)
00552 {
00553     char            buf[SPRINT_MAX_LEN];
00554     int             snmp_errnumber;
00555 
00556     if (p_errno)
00557         *p_errno = psess->s_errno;
00558     if (p_snmp_errno)
00559         *p_snmp_errno = psess->s_snmp_errno;
00560     if (p_str == NULL)
00561         return;
00562 
00563     strcpy(buf, "");
00564     snmp_errnumber = psess->s_snmp_errno;
00565     if (snmp_errnumber >= SNMPERR_MAX && snmp_errnumber <= SNMPERR_GENERR) {
00566         if (snmp_detail_f) {
00567             snprintf(buf, sizeof(buf), "%s (%s)", api_errors[-snmp_errnumber],
00568                     snmp_detail);
00569             snmp_detail_f = 0;
00570         }
00571         else
00572             strncpy(buf, api_errors[-snmp_errnumber], sizeof(buf));
00573     } else {
00574         if (snmp_errnumber)
00575             snprintf(buf, sizeof(buf), "Unknown Error %d", snmp_errnumber);
00576     }
00577     buf[sizeof(buf)-1] = '\0';
00578 
00579     /*
00580      * append a useful system errno interpretation. 
00581      */
00582     if (psess->s_errno) {
00583         const char* error = strerror(psess->s_errno);
00584         if(error == NULL)
00585             error = "Unknown Error";
00586         snprintf (&buf[strlen(buf)], sizeof(buf)-strlen(buf),
00587                  " (%s)", error);
00588     }
00589     buf[sizeof(buf)-1] = '\0';
00590     *p_str = strdup(buf);
00591 }
00592 
00593 /*
00594  * snmp_sess_error - same as snmp_error for single session API use.
00595  */
00596 void
00597 snmp_sess_error(void *sessp, int *p_errno, int *p_snmp_errno, char **p_str)
00598 {
00599     struct session_list *slp = (struct session_list *) sessp;
00600 
00601     if ((slp) && (slp->session))
00602         snmp_error(slp->session, p_errno, p_snmp_errno, p_str);
00603 }
00604 
00605 /*
00606  * netsnmp_sess_log_error(): print a error stored in a session pointer 
00607  */
00608 void
00609 netsnmp_sess_log_error(int priority,
00610                        const char *prog_string, netsnmp_session * ss)
00611 {
00612     char           *err;
00613     snmp_error(ss, NULL, NULL, &err);
00614     snmp_log(priority, "%s: %s\n", prog_string, err);
00615     SNMP_FREE(err);
00616 }
00617 
00618 /*
00619  * snmp_sess_perror(): print a error stored in a session pointer 
00620  */
00621 void
00622 snmp_sess_perror(const char *prog_string, netsnmp_session * ss)
00623 {
00624     netsnmp_sess_log_error(LOG_ERR, prog_string, ss);
00625 }
00626 
00627 
00628 
00629 /*
00630  * Primordial SNMP library initialization.
00631  * Initializes mutex locks.
00632  * Invokes minimum required initialization for displaying MIB objects.
00633  * Gets initial request ID for all transactions,
00634  * and finds which port SNMP over UDP uses.
00635  * SNMP over AppleTalk is not currently supported.
00636  *
00637  * Warning: no debug messages here.
00638  */
00639 static char _init_snmp_init_done = 0;
00640 static void
00641 _init_snmp(void)
00642 {
00643 
00644     struct timeval  tv;
00645     long            tmpReqid, tmpMsgid;
00646 
00647     if (_init_snmp_init_done)
00648         return;
00649     _init_snmp_init_done = 1;
00650     Reqid = 1;
00651 
00652     snmp_res_init();            /* initialize the mt locking structures */
00653 #ifndef NETSNMP_DISABLE_MIB_LOADING
00654     netsnmp_init_mib_internals();
00655 #endif /* NETSNMP_DISABLE_MIB_LOADING */
00656     netsnmp_tdomain_init();
00657 
00658     gettimeofday(&tv, (struct timezone *) 0);
00659     /*
00660      * Now = tv;
00661      */
00662 
00663     /*
00664      * get pseudo-random values for request ID and message ID 
00665      */
00666 #ifdef SVR4
00667     srand48(tv.tv_sec ^ tv.tv_usec);
00668     tmpReqid = lrand48();
00669     tmpMsgid = lrand48();
00670 #else
00671     srandom((unsigned)(tv.tv_sec ^ tv.tv_usec));
00672     tmpReqid = random();
00673     tmpMsgid = random();
00674 #endif
00675 
00676     /*
00677      * don't allow zero value to repeat init 
00678      */
00679     if (tmpReqid == 0)
00680         tmpReqid = 1;
00681     if (tmpMsgid == 0)
00682         tmpMsgid = 1;
00683     Reqid = tmpReqid;
00684     Msgid = tmpMsgid;
00685 
00686     netsnmp_register_default_domain("snmp", "udp udp6");
00687     netsnmp_register_default_domain("snmptrap", "udp udp6");
00688 
00689     netsnmp_register_default_target("snmp", "udp", ":161");
00690     netsnmp_register_default_target("snmp", "tcp", ":161");
00691     netsnmp_register_default_target("snmp", "udp6", ":161");
00692     netsnmp_register_default_target("snmp", "tcp6", ":161");
00693     netsnmp_register_default_target("snmp", "dtlsudp", ":10161");
00694     netsnmp_register_default_target("snmp", "tlstcp", ":10161");
00695     netsnmp_register_default_target("snmp", "ipx", "/36879");
00696 
00697     netsnmp_register_default_target("snmptrap", "udp", ":162");
00698     netsnmp_register_default_target("snmptrap", "tcp", ":162");
00699     netsnmp_register_default_target("snmptrap", "udp6", ":162");
00700     netsnmp_register_default_target("snmptrap", "tcp6", ":162");
00701     netsnmp_register_default_target("snmptrap", "dtlsudp", ":10162");
00702     netsnmp_register_default_target("snmptrap", "tlstcp", ":10162");
00703     netsnmp_register_default_target("snmptrap", "ipx", "/36880");
00704 
00705     netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, 
00706                        NETSNMP_DS_LIB_HEX_OUTPUT_LENGTH, 16);
00707 
00708 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
00709     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, 
00710                            NETSNMP_DS_LIB_REVERSE_ENCODE,
00711                            NETSNMP_DEFAULT_ASNENCODING_DIRECTION);
00712 #endif
00713 }
00714 
00715 /*
00716  * Initializes the session structure.
00717  * May perform one time minimal library initialization.
00718  * No MIB file processing is done via this call.
00719  */
00720 void
00721 snmp_sess_init(netsnmp_session * session)
00722 {
00723     _init_snmp();
00724 
00725     /*
00726      * initialize session to default values 
00727      */
00728 
00729     memset(session, 0, sizeof(netsnmp_session));
00730     session->remote_port = SNMP_DEFAULT_REMPORT;
00731     session->timeout = SNMP_DEFAULT_TIMEOUT;
00732     session->retries = SNMP_DEFAULT_RETRIES;
00733     session->version = SNMP_DEFAULT_VERSION;
00734     session->securityModel = SNMP_DEFAULT_SECMODEL;
00735     session->rcvMsgMaxSize = SNMP_MAX_MSG_SIZE;
00736     session->flags |= SNMP_FLAGS_DONT_PROBE;
00737 }
00738 
00739 
00740 static void
00741 register_default_handlers(void)
00742 {
00743     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "dumpPacket",
00744                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DUMP_PACKET);
00745     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "reverseEncodeBER",
00746                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE);
00747     netsnmp_ds_register_config(ASN_INTEGER, "snmp", "defaultPort",
00748                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DEFAULT_PORT);
00749 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
00750     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defCommunity",
00751                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_COMMUNITY);
00752 #endif
00753     netsnmp_ds_register_premib(ASN_BOOLEAN, "snmp", "noTokenWarnings",
00754                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NO_TOKEN_WARNINGS);
00755     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noRangeCheck",
00756                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_CHECK_RANGE);
00757     netsnmp_ds_register_premib(ASN_OCTET_STR, "snmp", "persistentDir",
00758                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PERSISTENT_DIR);
00759     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "tempFilePattern",
00760                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_TEMP_FILE_PATTERN);
00761     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noDisplayHint",
00762                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NO_DISPLAY_HINT);
00763     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "16bitIDs",
00764                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS);
00765     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "clientaddr",
00766                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENT_ADDR);
00767     netsnmp_ds_register_config(ASN_INTEGER, "snmp", "serverSendBuf",
00768                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SERVERSENDBUF);
00769     netsnmp_ds_register_config(ASN_INTEGER, "snmp", "serverRecvBuf",
00770                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SERVERRECVBUF);
00771     netsnmp_ds_register_config(ASN_INTEGER, "snmp", "clientSendBuf",
00772                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENTSENDBUF);
00773     netsnmp_ds_register_config(ASN_INTEGER, "snmp", "clientRecvBuf",
00774                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENTRECVBUF);
00775     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noPersistentLoad",
00776                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD);
00777     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noPersistentSave",
00778                       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE);
00779     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp",
00780                                "noContextEngineIDDiscovery",
00781                                NETSNMP_DS_LIBRARY_ID,
00782                                NETSNMP_DS_LIB_NO_DISCOVERY);
00783     netsnmp_ds_register_config(ASN_INTEGER, "snmp", "timeout",
00784                                NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_TIMEOUT);
00785     netsnmp_ds_register_config(ASN_INTEGER, "snmp", "retries",
00786                                NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_RETRIES);
00787 
00788     netsnmp_register_service_handlers();
00789 }
00790 
00791 static int init_snmp_init_done = 0; /* To prevent double init's. */
00802 void
00803 init_snmp(const char *type)
00804 {
00805     if (init_snmp_init_done) {
00806         return;
00807     }
00808 
00809     init_snmp_init_done = 1;
00810 
00811     /*
00812      * make the type available everywhere else 
00813      */
00814     if (type && !netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00815                                        NETSNMP_DS_LIB_APPTYPE)) {
00816         netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, 
00817                               NETSNMP_DS_LIB_APPTYPE, type);
00818     }
00819 
00820     _init_snmp();
00821 
00822     /*
00823      * set our current locale properly to initialize isprint() type functions 
00824      */
00825 #ifdef HAVE_SETLOCALE
00826     setlocale(LC_CTYPE, "");
00827 #endif
00828 
00829     snmp_debug_init();    /* should be done first, to turn on debugging ASAP */
00830     netsnmp_container_init_list();
00831     init_callbacks();
00832     init_snmp_logging();
00833     snmp_init_statistics();
00834     register_mib_handlers();
00835     register_default_handlers();
00836     init_snmp_transport();
00837     init_snmpv3(type);
00838     init_snmp_alarm();
00839     init_snmp_enum(type);
00840     init_vacm();
00841 #if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL)
00842     netsnmp_certs_init();
00843 #endif
00844 #ifdef DNSSEC_LOCAL_VALIDATION
00845     netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "dnssecWarnOnly",
00846                                NETSNMP_DS_LIBRARY_ID,
00847                                NETSNMP_DS_LIB_DNSSEC_WARN_ONLY);
00848 #endif
00849 
00850     read_premib_configs();
00851 #ifndef NETSNMP_DISABLE_MIB_LOADING
00852     netsnmp_init_mib();
00853 #endif /* NETSNMP_DISABLE_MIB_LOADING */
00854 
00855     read_configs();
00856 
00857 }                               /* end init_snmp() */
00858 
00862 void
00863 snmp_store_needed(const char *type)
00864 {
00865     DEBUGMSGTL(("snmp_store", "setting needed flag...\n"));
00866     _snmp_store_needed = 1;
00867 }
00868 
00869 void
00870 snmp_store_if_needed(void)
00871 {
00872     if (0 == _snmp_store_needed)
00873         return;
00874     
00875     DEBUGMSGTL(("snmp_store", "store needed...\n"));
00876     snmp_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00877                                      NETSNMP_DS_LIB_APPTYPE));
00878     _snmp_store_needed = 0;
00879 }
00880 
00881 void
00882 snmp_store(const char *type)
00883 {
00884     DEBUGMSGTL(("snmp_store", "storing stuff...\n"));
00885     snmp_save_persistent(type);
00886     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, NULL);
00887     snmp_clean_persistent(type);
00888 }
00889 
00890 
00899 void
00900 snmp_shutdown(const char *type)
00901 {
00902     snmp_store(type);
00903     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN, NULL);
00904     shutdown_snmp_logging();
00905     snmp_alarm_unregister_all();
00906     snmp_close_sessions();
00907 #ifndef NETSNMP_DISABLE_MIB_LOADING
00908     shutdown_mib();
00909 #endif /* NETSNMP_DISABLE_MIB_LOADING */
00910 #if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL)
00911     netsnmp_certs_shutdown();
00912 #endif
00913     unregister_all_config_handlers();
00914     netsnmp_container_free_list();
00915     clear_sec_mod();
00916     clear_snmp_enum();
00917     netsnmp_clear_tdomain_list();
00918     clear_callback();
00919     netsnmp_ds_shutdown();
00920     netsnmp_clear_default_target();
00921     netsnmp_clear_default_domain();
00922     shutdown_secmod();
00923 
00924     init_snmp_init_done  = 0;
00925     _init_snmp_init_done = 0;
00926 }
00927 
00928 
00929 /*
00930  * Sets up the session with the snmp_session information provided by the user.
00931  * Then opens and binds the necessary low-level transport.  A handle to the
00932  * created session is returned (this is NOT the same as the pointer passed to
00933  * snmp_open()).  On any error, NULL is returned and snmp_errno is set to the
00934  * appropriate error code.
00935  */
00936 netsnmp_session *
00937 snmp_open(netsnmp_session *session)
00938 {
00939     struct session_list *slp;
00940     slp = (struct session_list *) snmp_sess_open(session);
00941     if (!slp) {
00942         return NULL;
00943     }
00944 
00945     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
00946     slp->next = Sessions;
00947     Sessions = slp;
00948     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
00949 
00950     return (slp->session);
00951 }
00952 
00953 /*
00954  * extended open 
00955  */
00956 netsnmp_feature_child_of(snmp_open_ex, netsnmp_unused)
00957 #ifndef NETSNMP_FEATURE_REMOVE_SNMP_OPEN_EX
00958 netsnmp_session *
00959 snmp_open_ex(netsnmp_session *session,
00960              int (*fpre_parse)  (netsnmp_session *, netsnmp_transport *,
00961                                 void *, int),
00962              int (*fparse)      (netsnmp_session *, netsnmp_pdu *, u_char *,
00963                                  size_t),
00964              int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int),
00965 
00966              int (*fbuild)      (netsnmp_session *, netsnmp_pdu *, u_char *,
00967                                  size_t *),
00968              int (*frbuild)     (netsnmp_session *, netsnmp_pdu *,
00969                                  u_char **, size_t *, size_t *),
00970              int (*fcheck)      (u_char *, size_t)
00971              )
00972 {
00973     struct session_list *slp;
00974     slp = (struct session_list *) snmp_sess_open(session);
00975     if (!slp) {
00976         return NULL;
00977     }
00978     slp->internal->hook_pre = fpre_parse;
00979     slp->internal->hook_parse = fparse;
00980     slp->internal->hook_post = fpost_parse;
00981     slp->internal->hook_build = fbuild;
00982     slp->internal->hook_realloc_build = frbuild;
00983     slp->internal->check_packet = fcheck;
00984 
00985     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
00986     slp->next = Sessions;
00987     Sessions = slp;
00988     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
00989 
00990     return (slp->session);
00991 }
00992 #endif /* NETSNMP_FEATURE_REMOVE_SNMP_OPEN_EX */
00993 
00994 static struct session_list *
00995 _sess_copy(netsnmp_session * in_session)
00996 {
00997     struct session_list *slp;
00998     struct snmp_internal_session *isp;
00999     netsnmp_session *session;
01000     struct snmp_secmod_def *sptr;
01001     char           *cp;
01002     u_char         *ucp;
01003 
01004     in_session->s_snmp_errno = 0;
01005     in_session->s_errno = 0;
01006 
01007     /*
01008      * Copy session structure and link into list 
01009      */
01010     slp = (struct session_list *) calloc(1, sizeof(struct session_list));
01011     if (slp == NULL) {
01012         in_session->s_snmp_errno = SNMPERR_MALLOC;
01013         return (NULL);
01014     }
01015 
01016     slp->transport = NULL;
01017 
01018     isp = (struct snmp_internal_session *)calloc(1, sizeof(struct snmp_internal_session));
01019 
01020     if (isp == NULL) {
01021         snmp_sess_close(slp);
01022         in_session->s_snmp_errno = SNMPERR_MALLOC;
01023         return (NULL);
01024     }
01025 
01026     slp->internal = isp;
01027     slp->session = (netsnmp_session *)malloc(sizeof(netsnmp_session));
01028     if (slp->session == NULL) {
01029         snmp_sess_close(slp);
01030         in_session->s_snmp_errno = SNMPERR_MALLOC;
01031         return (NULL);
01032     }
01033     memmove(slp->session, in_session, sizeof(netsnmp_session));
01034     session = slp->session;
01035 
01036     /*
01037      * zero out pointers so if we have to free the session we wont free mem
01038      * owned by in_session 
01039      */
01040     session->localname = NULL;
01041     session->peername = NULL;
01042     session->community = NULL;
01043     session->contextEngineID = NULL;
01044     session->contextName = NULL;
01045     session->securityEngineID = NULL;
01046     session->securityName = NULL;
01047     session->securityAuthProto = NULL;
01048     session->securityPrivProto = NULL;
01049     /*
01050      * session now points to the new structure that still contains pointers to
01051      * data allocated elsewhere.  Some of this data is copied to space malloc'd
01052      * here, and the pointer replaced with the new one.
01053      */
01054 
01055     if (in_session->peername != NULL) {
01056         session->peername = (char *)malloc(strlen(in_session->peername) + 1);
01057         if (session->peername == NULL) {
01058             snmp_sess_close(slp);
01059             in_session->s_snmp_errno = SNMPERR_MALLOC;
01060             return (NULL);
01061         }
01062         strcpy(session->peername, in_session->peername);
01063     }
01064 
01065     /*
01066      * Fill in defaults if necessary 
01067      */
01068 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
01069     if (in_session->community_len != SNMP_DEFAULT_COMMUNITY_LEN) {
01070         ucp = (u_char *) malloc(in_session->community_len);
01071         if (ucp != NULL)
01072             memmove(ucp, in_session->community, in_session->community_len);
01073     } else {
01074         if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01075                                         NETSNMP_DS_LIB_COMMUNITY)) != NULL) {
01076             session->community_len = strlen(cp);
01077             ucp = (u_char *) malloc(session->community_len);
01078             if (ucp)
01079                 memmove(ucp, cp, session->community_len);
01080         } else {
01081 #ifdef NETSNMP_NO_ZEROLENGTH_COMMUNITY
01082             session->community_len = strlen(DEFAULT_COMMUNITY);
01083             ucp = (u_char *) malloc(session->community_len);
01084             if (ucp)
01085                 memmove(ucp, DEFAULT_COMMUNITY, session->community_len);
01086 #else
01087             ucp = (u_char *) strdup("");
01088 #endif
01089         }
01090     }
01091 
01092     if (ucp == NULL) {
01093         snmp_sess_close(slp);
01094         in_session->s_snmp_errno = SNMPERR_MALLOC;
01095         return (NULL);
01096     }
01097     session->community = ucp;   /* replace pointer with pointer to new data */
01098 #endif
01099 
01100     if (session->securityLevel <= 0) {
01101         session->securityLevel =
01102             netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECLEVEL);
01103     }
01104 
01105     if (in_session->securityEngineIDLen > 0) {
01106         ucp = (u_char *) malloc(in_session->securityEngineIDLen);
01107         if (ucp == NULL) {
01108             snmp_sess_close(slp);
01109             in_session->s_snmp_errno = SNMPERR_MALLOC;
01110             return (NULL);
01111         }
01112         memmove(ucp, in_session->securityEngineID,
01113                 in_session->securityEngineIDLen);
01114         session->securityEngineID = ucp;
01115 
01116     }
01117 
01118     if (in_session->contextEngineIDLen > 0) {
01119         ucp = (u_char *) malloc(in_session->contextEngineIDLen);
01120         if (ucp == NULL) {
01121             snmp_sess_close(slp);
01122             in_session->s_snmp_errno = SNMPERR_MALLOC;
01123             return (NULL);
01124         }
01125         memmove(ucp, in_session->contextEngineID,
01126                 in_session->contextEngineIDLen);
01127         session->contextEngineID = ucp;
01128     } else if (in_session->securityEngineIDLen > 0) {
01129         /*
01130          * default contextEngineID to securityEngineIDLen if defined 
01131          */
01132         ucp = (u_char *) malloc(in_session->securityEngineIDLen);
01133         if (ucp == NULL) {
01134             snmp_sess_close(slp);
01135             in_session->s_snmp_errno = SNMPERR_MALLOC;
01136             return (NULL);
01137         }
01138         memmove(ucp, in_session->securityEngineID,
01139                 in_session->securityEngineIDLen);
01140         session->contextEngineID = ucp;
01141         session->contextEngineIDLen = in_session->securityEngineIDLen;
01142     }
01143 
01144     if (in_session->contextName) {
01145         session->contextName = strdup(in_session->contextName);
01146         if (session->contextName == NULL) {
01147             snmp_sess_close(slp);
01148             return (NULL);
01149         }
01150         session->contextNameLen = in_session->contextNameLen;
01151     } else {
01152         if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
01153                                         NETSNMP_DS_LIB_CONTEXT)) != NULL)
01154             cp = strdup(cp);
01155         else
01156             cp = strdup(SNMP_DEFAULT_CONTEXT);
01157         if (cp == NULL) {
01158             snmp_sess_close(slp);
01159             return (NULL);
01160         }
01161         session->contextName = cp;
01162         session->contextNameLen = strlen(cp);
01163     }
01164 
01165     if (in_session->securityName) {
01166         session->securityName = strdup(in_session->securityName);
01167         if (session->securityName == NULL) {
01168             snmp_sess_close(slp);
01169             return (NULL);
01170         }
01171     } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01172                                            NETSNMP_DS_LIB_SECNAME)) != NULL) {
01173         cp = strdup(cp);
01174         if (cp == NULL) {
01175             snmp_sess_close(slp);
01176             return (NULL);
01177         }
01178         session->securityName = cp;
01179         session->securityNameLen = strlen(cp);
01180     }
01181 
01182     if (session->retries == SNMP_DEFAULT_RETRIES) {
01183         int retry = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
01184                                        NETSNMP_DS_LIB_RETRIES);
01185         if (retry < 0)
01186             session->retries = DEFAULT_RETRIES;
01187         else
01188             session->retries = retry;
01189     }
01190     if (session->timeout == SNMP_DEFAULT_TIMEOUT) {
01191         int timeout = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
01192                                          NETSNMP_DS_LIB_TIMEOUT);
01193         if (timeout <= 0)
01194             session->timeout = DEFAULT_TIMEOUT;
01195         else
01196             session->timeout = timeout * ONE_SEC;
01197     }
01198     session->sessid = snmp_get_next_sessid();
01199 
01200     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SESSION_INIT,
01201                         session);
01202 
01203     if ((sptr = find_sec_mod(session->securityModel)) != NULL) {
01204         /*
01205          * security module specific copying 
01206          */
01207         if (sptr->session_setup) {
01208             int ret = (*sptr->session_setup) (in_session, session);
01209             if (ret != SNMPERR_SUCCESS) {
01210                 snmp_sess_close(slp);
01211                 return NULL;
01212             }
01213         }
01214 
01215         /*
01216          * security module specific opening
01217          */
01218         if (sptr->session_open) {
01219             int ret = (*sptr->session_open) (session);
01220             if (ret != SNMPERR_SUCCESS) {
01221                 snmp_sess_close(slp);
01222                 return NULL;
01223             }
01224         }
01225     }
01226 
01227     /* Anything below this point should only be done if the transport
01228        had no say in the matter */
01229     if (session->securityLevel == 0)
01230         session->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
01231 
01232     return (slp);
01233 }
01234 
01235 static struct session_list *
01236 snmp_sess_copy(netsnmp_session * pss)
01237 {
01238     struct session_list *psl;
01239     psl = _sess_copy(pss);
01240     if (!psl) {
01241         if (!pss->s_snmp_errno) {
01242             pss->s_snmp_errno = SNMPERR_GENERR;
01243         }
01244         SET_SNMP_ERROR(pss->s_snmp_errno);
01245     }
01246     return psl;
01247 }
01248 
01249 #ifndef NETSNMP_FEATURE_REMOVE_SNMPV3_PROBE_CONTEXTENGINEID_RFC5343
01250 
01258 int
01259 snmpv3_probe_contextEngineID_rfc5343(void *slp, netsnmp_session *session) {
01260     netsnmp_pdu    *pdu = NULL, *response = NULL;
01261     static oid      snmpEngineIDoid[]   = { 1,3,6,1,6,3,10,2,1,1,0};
01262     static size_t   snmpEngineIDoid_len = 11;
01263 
01264     static char     probeEngineID[] = { (char)0x80, 0, 0, 0, 6 };
01265     static size_t   probeEngineID_len = sizeof(probeEngineID);
01266     
01267     int status;
01268 
01269     pdu = snmp_pdu_create(SNMP_MSG_GET);
01270     if (!pdu)
01271         return SNMP_ERR_GENERR;
01272     pdu->version = SNMP_VERSION_3;
01273     /* don't require a securityName */
01274     if (session->securityName) {
01275         pdu->securityName = strdup(session->securityName);
01276         pdu->securityNameLen = strlen(pdu->securityName);
01277     }
01278     pdu->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
01279     pdu->securityModel = session->securityModel;
01280     if (memdup(&pdu->contextEngineID, probeEngineID, probeEngineID_len) !=
01281         SNMPERR_SUCCESS) {
01282         snmp_log(LOG_ERR, "failed to clone memory for rfc5343 probe\n");
01283         return SNMP_ERR_GENERR;
01284     }
01285     pdu->contextEngineIDLen = probeEngineID_len;
01286     
01287     snmp_add_null_var(pdu, snmpEngineIDoid, snmpEngineIDoid_len);
01288 
01289     DEBUGMSGTL(("snmp_api", "probing for engineID using rfc5343 methods...\n"));
01290     session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
01291     status = snmp_sess_synch_response(slp, pdu, &response);
01292 
01293     if ((response == NULL) || (status != STAT_SUCCESS)) {
01294         snmp_log(LOG_ERR, "failed rfc5343 contextEngineID probing\n");
01295         return SNMP_ERR_GENERR;
01296     }
01297 
01298     /* check that the response makes sense */
01299     if (NULL != response->variables &&
01300         NULL != response->variables->name &&
01301         snmp_oid_compare(response->variables->name,
01302                          response->variables->name_length,
01303                          snmpEngineIDoid, snmpEngineIDoid_len) == 0 &&
01304         ASN_OCTET_STR == response->variables->type  &&
01305         NULL != response->variables->val.string &&
01306         response->variables->val_len > 0) {
01307         if (memdup(&session->contextEngineID,
01308                    response->variables->val.string,
01309                    response->variables->val_len) != SNMPERR_SUCCESS) {
01310             snmp_log(LOG_ERR, "failed rfc5343 contextEngineID probing: memory allocation failed\n");
01311             return SNMP_ERR_GENERR;
01312         }
01313         
01314         /* technically there likely isn't a securityEngineID but just
01315            in case anyone goes looking we might as well have one */
01316         if (memdup(&session->securityEngineID,
01317                    response->variables->val.string,
01318                    response->variables->val_len) != SNMPERR_SUCCESS) {
01319             snmp_log(LOG_ERR, "failed rfc5343 securityEngineID probing: memory allocation failed\n");
01320             return SNMP_ERR_GENERR;
01321         }
01322         
01323         session->securityEngineIDLen = session->contextEngineIDLen =
01324             response->variables->val_len;
01325         
01326         if (snmp_get_do_debugging()) {
01327             size_t i;
01328             DEBUGMSGTL(("snmp_sess_open",
01329                         "  probe found engineID:  "));
01330             for (i = 0; i < session->securityEngineIDLen; i++)
01331                 DEBUGMSG(("snmp_sess_open", "%02x",
01332                           session->securityEngineID[i]));
01333             DEBUGMSG(("snmp_sess_open", "\n"));
01334         }
01335     }
01336     return SNMPERR_SUCCESS;
01337 }
01338 #endif /* NETSNMP_FEATURE_REMOVE_SNMPV3_PROBE_CONTEXTENGINEID_RFC5343 */
01339 
01340 
01356 int
01357 snmpv3_engineID_probe(struct session_list *slp,
01358                       netsnmp_session * in_session)
01359 {
01360     netsnmp_session *session;
01361     int             status;
01362     struct snmp_secmod_def *sptr = NULL;
01363 
01364     if (slp == NULL || slp->session == NULL) {
01365         return 0;
01366     }
01367 
01368     session = slp->session;
01369     netsnmp_assert_or_return(session != NULL, 0);
01370     sptr = find_sec_mod(session->securityModel);
01371 
01372     /*
01373      * If we are opening a V3 session and we don't know engineID we must probe
01374      * it -- this must be done after the session is created and inserted in the
01375      * list so that the response can handled correctly. 
01376      */
01377 
01378     if (session->version == SNMP_VERSION_3 &&
01379         (0 == (session->flags & SNMP_FLAGS_DONT_PROBE))) {
01380         if (NULL != sptr && NULL != sptr->probe_engineid) {
01381             DEBUGMSGTL(("snmp_api", "probing for engineID using security model callback...\n"));
01382             /* security model specific mechanism of determining engineID */
01383             status = (*sptr->probe_engineid) (slp, in_session);
01384             if (status != SNMPERR_SUCCESS)
01385                 return 0;
01386         } else {
01387             /* XXX: default to the default RFC5343 contextEngineID Probe? */
01388             return 0;
01389         }
01390     }
01391 
01392     /* see if there was any hooks to call after the engineID probing */
01393     if (sptr->post_probe_engineid) {
01394         status = (*sptr->post_probe_engineid)(slp, in_session);
01395         if (status != SNMPERR_SUCCESS)
01396             return 0;
01397     }
01398 
01399     return 1;
01400 }
01401 
01402 /*******************************************************************-o-******
01403  * netsnmp_sess_config_transport
01404  *
01405  * Parameters:
01406  *      *in_session
01407  *      *in_transport
01408  *
01409  * Returns:
01410  *      SNMPERR_SUCCESS                     - Yay
01411  *      SNMPERR_GENERR                      - Generic Error
01412  *      SNMPERR_TRANSPORT_CONFIG_ERROR      - Transport rejected config
01413  *      SNMPERR_TRANSPORT_NO_CONFIG         - Transport can't config
01414  */
01415 int
01416 netsnmp_sess_config_transport(netsnmp_container *transport_configuration,
01417                               netsnmp_transport *transport)
01418 {
01419     /* Optional supplimental transport configuration information and
01420        final call to actually open the transport */
01421     if (transport_configuration) {
01422         DEBUGMSGTL(("snmp_sess", "configuring transport\n"));
01423         if (transport->f_config) {
01424             netsnmp_iterator *iter;
01425             netsnmp_transport_config *config_data;
01426             int ret;
01427 
01428             iter = CONTAINER_ITERATOR(transport_configuration);
01429             if (NULL == iter) {
01430                 return SNMPERR_GENERR;
01431             }
01432 
01433             for(config_data = (netsnmp_transport_config*)ITERATOR_FIRST(iter); config_data;
01434                 config_data = (netsnmp_transport_config*)ITERATOR_NEXT(iter)) {
01435                 ret = transport->f_config(transport, config_data->key,
01436                                           config_data->value);
01437                 if (ret) {
01438                     return SNMPERR_TRANSPORT_CONFIG_ERROR;
01439                 }
01440             }
01441         } else {
01442             return SNMPERR_TRANSPORT_NO_CONFIG;
01443         }
01444     }
01445     return SNMPERR_SUCCESS;
01446 }
01447 
01448  
01462 /*******************************************************************-o-******
01463  * netsnmp_sess_config_transport
01464  *
01465  * Parameters:
01466  *      *in_session
01467  *      *in_transport
01468  *
01469  * Returns:
01470  *      SNMPERR_SUCCESS                     - Yay
01471  *      SNMPERR_GENERR                      - Generic Error
01472  *      SNMPERR_TRANSPORT_CONFIG_ERROR      - Transport rejected config
01473  *      SNMPERR_TRANSPORT_NO_CONFIG         - Transport can't config
01474  */
01475 int
01476 netsnmp_sess_config_and_open_transport(netsnmp_session *in_session,
01477                                        netsnmp_transport *transport)
01478 {
01479     int rc;
01480     
01481     DEBUGMSGTL(("snmp_sess", "opening transport: %x\n", transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED));
01482 
01483     /* don't double open */
01484     if (transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED)
01485         return SNMPERR_SUCCESS;
01486 
01487     if ((rc = netsnmp_sess_config_transport(in_session->transport_configuration,
01488                                             transport)) != SNMPERR_SUCCESS) {
01489         in_session->s_snmp_errno = rc;
01490         in_session->s_errno = 0;
01491         return rc;
01492     }
01493         
01494     if (transport->f_open)
01495         transport = transport->f_open(transport);
01496 
01497     if (transport == NULL) {
01498         DEBUGMSGTL(("snmp_sess", "couldn't interpret peername\n"));
01499         in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS;
01500         in_session->s_errno = errno;
01501         snmp_set_detail(in_session->peername);
01502         return SNMPERR_BAD_ADDRESS;
01503     }
01504 
01505     transport->flags |= NETSNMP_TRANSPORT_FLAG_OPENED;
01506     DEBUGMSGTL(("snmp_sess", "done opening transport: %x\n", transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED));
01507     return SNMPERR_SUCCESS;
01508 }
01509 
01510 /*******************************************************************-o-******
01511  * snmp_sess_open
01512  *
01513  * Parameters:
01514  *      *in_session
01515  *
01516  * Returns:
01517  *      Pointer to a session in the session list   -OR-         FIX -- right?
01518  *      NULL on failure.
01519  *
01520  * The "spin-free" version of snmp_open.
01521  */
01522 static void    *
01523 _sess_open(netsnmp_session * in_session)
01524 {
01525     netsnmp_transport *transport = NULL;
01526     int rc;
01527 
01528     in_session->s_snmp_errno = 0;
01529     in_session->s_errno = 0;
01530 
01531     _init_snmp();
01532 
01533     {
01534         char *clientaddr_save = NULL;
01535 
01536         if (NULL != in_session->localname) {
01537             clientaddr_save =
01538                 netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
01539                                       NETSNMP_DS_LIB_CLIENT_ADDR);
01540             netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
01541                                   NETSNMP_DS_LIB_CLIENT_ADDR,
01542                                   in_session->localname);
01543         }
01544 
01545         if (in_session->flags & SNMP_FLAGS_STREAM_SOCKET) {
01546             transport =
01547                 netsnmp_tdomain_transport_full("snmp", in_session->peername,
01548                                                in_session->local_port, "tcp",
01549                                                NULL);
01550         } else {
01551             transport =
01552                 netsnmp_tdomain_transport_full("snmp", in_session->peername,
01553                                                in_session->local_port, "udp",
01554                                                NULL);
01555         }
01556 
01557         if (NULL != clientaddr_save)
01558             netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
01559                                   NETSNMP_DS_LIB_CLIENT_ADDR, clientaddr_save);
01560     }
01561 
01562     if (transport == NULL) {
01563         DEBUGMSGTL(("_sess_open", "couldn't interpret peername\n"));
01564         in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS;
01565         in_session->s_errno = errno;
01566         snmp_set_detail(in_session->peername);
01567         return NULL;
01568     }
01569 
01570     /* Optional supplimental transport configuration information and
01571        final call to actually open the transport */
01572     if ((rc = netsnmp_sess_config_and_open_transport(in_session, transport))
01573         != SNMPERR_SUCCESS) {
01574         transport = NULL;
01575         return NULL;
01576     }
01577 
01578 #if defined(SO_BROADCAST) && defined(SOL_SOCKET)
01579     if ( in_session->flags & SNMP_FLAGS_UDP_BROADCAST) {
01580         int   b = 1;
01581         int   rc;
01582 
01583         rc = setsockopt(transport->sock, SOL_SOCKET, SO_BROADCAST,
01584                         (char *)&b, sizeof(b));
01585 
01586         if ( rc != 0 ) {
01587             in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS; /* good as any? */
01588             in_session->s_errno = errno;
01589 
01590             DEBUGMSGTL(("_sess_open", "couldn't enable UDP_BROADCAST\n"));
01591             return NULL;
01592         }
01593     }
01594 #endif
01595 
01596     return snmp_sess_add(in_session, transport, NULL, NULL);
01597 }
01598 
01599 /*
01600  * EXTENDED SESSION API ------------------------------------------ 
01601  * 
01602  * snmp_sess_add_ex, snmp_sess_add, snmp_add 
01603  * 
01604  * Analogous to snmp_open family of functions, but taking a netsnmp_transport
01605  * pointer as an extra argument.  Unlike snmp_open et al. it doesn't attempt
01606  * to interpret the in_session->peername as a transport endpoint specifier,
01607  * but instead uses the supplied transport.  JBPN
01608  * 
01609  */
01610 
01611 netsnmp_session *
01612 snmp_add(netsnmp_session * in_session,
01613          netsnmp_transport *transport,
01614          int (*fpre_parse) (netsnmp_session *, netsnmp_transport *, void *,
01615                             int), int (*fpost_parse) (netsnmp_session *,
01616                                                       netsnmp_pdu *, int))
01617 {
01618     struct session_list *slp;
01619     slp = (struct session_list *) snmp_sess_add_ex(in_session, transport,
01620                                                    fpre_parse, NULL,
01621                                                    fpost_parse, NULL, NULL,
01622                                                    NULL, NULL);
01623     if (slp == NULL) {
01624         return NULL;
01625     }
01626 
01627     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
01628     slp->next = Sessions;
01629     Sessions = slp;
01630     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
01631 
01632     return (slp->session);
01633 }
01634 
01635 netsnmp_session *
01636 snmp_add_full(netsnmp_session * in_session,
01637               netsnmp_transport *transport,
01638               int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
01639                                  void *, int),
01640               int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *,
01641                              size_t),
01642               int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int),
01643               int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *,
01644                              size_t *), int (*frbuild) (netsnmp_session *,
01645                                                         netsnmp_pdu *,
01646                                                         u_char **,
01647                                                         size_t *,
01648                                                         size_t *),
01649               int (*fcheck) (u_char *, size_t),
01650               netsnmp_pdu *(*fcreate_pdu) (netsnmp_transport *, void *,
01651                                            size_t))
01652 {
01653     struct session_list *slp;
01654     slp = (struct session_list *) snmp_sess_add_ex(in_session, transport,
01655                                                    fpre_parse, fparse,
01656                                                    fpost_parse, fbuild,
01657                                                    frbuild, fcheck,
01658                                                    fcreate_pdu);
01659     if (slp == NULL) {
01660         return NULL;
01661     }
01662 
01663     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
01664     slp->next = Sessions;
01665     Sessions = slp;
01666     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
01667 
01668     return (slp->session);
01669 }
01670 
01671 
01672 
01673 void           *
01674 snmp_sess_add_ex(netsnmp_session * in_session,
01675                  netsnmp_transport *transport,
01676                  int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
01677                                     void *, int),
01678                  int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *,
01679                                 size_t),
01680                  int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *,
01681                                      int),
01682                  int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *,
01683                                 size_t *),
01684                  int (*frbuild) (netsnmp_session *, netsnmp_pdu *,
01685                                  u_char **, size_t *, size_t *),
01686                  int (*fcheck) (u_char *, size_t),
01687                  netsnmp_pdu *(*fcreate_pdu) (netsnmp_transport *, void *,
01688                                               size_t))
01689 {
01690     struct session_list *slp;
01691     int rc;
01692     
01693     _init_snmp();
01694 
01695     if (transport == NULL)
01696         return NULL;
01697 
01698     if (in_session == NULL) {
01699         transport->f_close(transport);
01700         netsnmp_transport_free(transport);
01701         return NULL;
01702     }
01703 
01704     /* if the transport hasn't been fully opened yet, open it now */
01705     if ((rc = netsnmp_sess_config_and_open_transport(in_session, transport))
01706         != SNMPERR_SUCCESS) {
01707         return NULL;
01708     }
01709 
01710     if (transport->f_setup_session) {
01711         if (SNMPERR_SUCCESS !=
01712             transport->f_setup_session(transport, in_session)) {
01713             netsnmp_transport_free(transport);
01714             return NULL;
01715         }
01716     }
01717         
01718             
01719     DEBUGMSGTL(("snmp_sess_add", "fd %d\n", transport->sock));
01720 
01721 
01722     if ((slp = snmp_sess_copy(in_session)) == NULL) {
01723         transport->f_close(transport);
01724         netsnmp_transport_free(transport);
01725         return (NULL);
01726     }
01727 
01728     slp->transport = transport;
01729     slp->internal->hook_pre = fpre_parse;
01730     slp->internal->hook_parse = fparse;
01731     slp->internal->hook_post = fpost_parse;
01732     slp->internal->hook_build = fbuild;
01733     slp->internal->hook_realloc_build = frbuild;
01734     slp->internal->check_packet = fcheck;
01735     slp->internal->hook_create_pdu = fcreate_pdu;
01736 
01737     slp->session->rcvMsgMaxSize = transport->msgMaxSize;
01738 
01739     if (slp->session->version == SNMP_VERSION_3) {
01740         DEBUGMSGTL(("snmp_sess_add",
01741                     "adding v3 session -- maybe engineID probe now\n"));
01742         if (!snmpv3_engineID_probe(slp, slp->session)) {
01743             DEBUGMSGTL(("snmp_sess_add", "engine ID probe failed\n"));
01744             snmp_sess_close(slp);
01745             return NULL;
01746         }
01747     }
01748 
01749     slp->session->flags &= ~SNMP_FLAGS_DONT_PROBE;
01750 
01751     return (void *) slp;
01752 }                               /*  end snmp_sess_add_ex()  */
01753 
01754 
01755 
01756 void           *
01757 snmp_sess_add(netsnmp_session * in_session,
01758               netsnmp_transport *transport,
01759               int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
01760                                  void *, int),
01761               int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int))
01762 {
01763     return snmp_sess_add_ex(in_session, transport, fpre_parse, NULL,
01764                             fpost_parse, NULL, NULL, NULL, NULL);
01765 }
01766 
01767 
01768 
01769 void           *
01770 snmp_sess_open(netsnmp_session * pss)
01771 {
01772     void           *pvoid;
01773     pvoid = _sess_open(pss);
01774     if (!pvoid) {
01775         SET_SNMP_ERROR(pss->s_snmp_errno);
01776     }
01777     return pvoid;
01778 }
01779 
01780 int
01781 create_user_from_session(netsnmp_session * session) {
01782 #ifdef NETSNMP_SECMOD_USM
01783     return usm_create_user_from_session(session);
01784 #else
01785     snmp_log(LOG_ERR, "create_user_from_session called when USM wasn't compiled in");
01786     netsnmp_assert(0 == 1);
01787     return SNMP_ERR_GENERR;
01788 #endif
01789 }
01790 
01791 
01792 /*
01793  *  Do a "deep free()" of a netsnmp_session.
01794  *
01795  *  CAUTION:  SHOULD ONLY BE USED FROM snmp_sess_close() OR SIMILAR.
01796  *                                                      (hence it is static)
01797  */
01798 
01799 static void
01800 snmp_free_session(netsnmp_session * s)
01801 {
01802     if (s) {
01803         SNMP_FREE(s->localname);
01804         SNMP_FREE(s->peername);
01805         SNMP_FREE(s->community);
01806         SNMP_FREE(s->contextEngineID);
01807         SNMP_FREE(s->contextName);
01808         SNMP_FREE(s->securityEngineID);
01809         SNMP_FREE(s->securityName);
01810         SNMP_FREE(s->securityAuthProto);
01811         SNMP_FREE(s->securityPrivProto);
01812         SNMP_FREE(s->paramName);
01813 
01814         /*
01815          * clear session from any callbacks
01816          */
01817         netsnmp_callback_clear_client_arg(s, 0, 0);
01818 
01819         free((char *) s);
01820     }
01821 }
01822 
01823 /*
01824  * Close the input session.  Frees all data allocated for the session,
01825  * dequeues any pending requests, and closes any sockets allocated for
01826  * the session.  Returns 0 on error, 1 otherwise.
01827  */
01828 int
01829 snmp_sess_close(void *sessp)
01830 {
01831     struct session_list *slp = (struct session_list *) sessp;
01832     netsnmp_transport *transport;
01833     struct snmp_internal_session *isp;
01834     netsnmp_session *sesp = NULL;
01835     struct snmp_secmod_def *sptr;
01836 
01837     if (slp == NULL) {
01838         return 0;
01839     }
01840 
01841     if (slp->session != NULL &&
01842         (sptr = find_sec_mod(slp->session->securityModel)) != NULL &&
01843         sptr->session_close != NULL) {
01844         (*sptr->session_close) (slp->session);
01845     }
01846 
01847     isp = slp->internal;
01848     slp->internal = NULL;
01849 
01850     if (isp) {
01851         netsnmp_request_list *rp, *orp;
01852 
01853         SNMP_FREE(isp->packet);
01854 
01855         /*
01856          * Free each element in the input request list.  
01857          */
01858         rp = isp->requests;
01859         while (rp) {
01860             orp = rp;
01861             rp = rp->next_request;
01862             if (orp->callback) {
01863                 orp->callback(NETSNMP_CALLBACK_OP_TIMED_OUT,
01864                               slp->session, orp->pdu->reqid,
01865                               orp->pdu, orp->cb_data);
01866             }
01867             snmp_free_pdu(orp->pdu);
01868             free((char *) orp);
01869         }
01870 
01871         free((char *) isp);
01872     }
01873 
01874     transport = slp->transport;
01875     slp->transport = NULL;
01876 
01877     if (transport) {
01878         transport->f_close(transport);
01879         netsnmp_transport_free(transport);
01880     }
01881 
01882     sesp = slp->session;
01883     slp->session = NULL;
01884 
01885     /*
01886      * The following is necessary to avoid memory leakage when closing AgentX 
01887      * sessions that may have multiple subsessions.  These hang off the main
01888      * session at ->subsession, and chain through ->next.  
01889      */
01890 
01891     if (sesp != NULL && sesp->subsession != NULL) {
01892         netsnmp_session *subsession = sesp->subsession, *tmpsub;
01893 
01894         while (subsession != NULL) {
01895             DEBUGMSGTL(("snmp_sess_close",
01896                         "closing session %p, subsession %p\n", sesp,
01897                         subsession));
01898             tmpsub = subsession->next;
01899             snmp_free_session(subsession);
01900             subsession = tmpsub;
01901         }
01902     }
01903 
01904     snmp_free_session(sesp);
01905     free((char *) slp);
01906     return 1;
01907 }
01908 
01909 int
01910 snmp_close(netsnmp_session * session)
01911 {
01912     struct session_list *slp = NULL, *oslp = NULL;
01913 
01914     {                           /*MTCRITICAL_RESOURCE */
01915         snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
01916         if (Sessions && Sessions->session == session) { /* If first entry */
01917             slp = Sessions;
01918             Sessions = slp->next;
01919         } else {
01920             for (slp = Sessions; slp; slp = slp->next) {
01921                 if (slp->session == session) {
01922                     if (oslp)   /* if we found entry that points here */
01923                         oslp->next = slp->next; /* link around this entry */
01924                     break;
01925                 }
01926                 oslp = slp;
01927             }
01928         }
01929         snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
01930     }                           /*END MTCRITICAL_RESOURCE */
01931     if (slp == NULL) {
01932         return 0;
01933     }
01934     return snmp_sess_close((void *) slp);
01935 }
01936 
01937 int
01938 snmp_close_sessions(void)
01939 {
01940     struct session_list *slp;
01941 
01942     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
01943     while (Sessions) {
01944         slp = Sessions;
01945         Sessions = Sessions->next;
01946         snmp_sess_close((void *) slp);
01947     }
01948     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
01949     return 1;
01950 }
01951 
01952 static void
01953 snmpv3_calc_msg_flags(int sec_level, int msg_command, u_char * flags)
01954 {
01955     *flags = 0;
01956     if (sec_level == SNMP_SEC_LEVEL_AUTHNOPRIV)
01957         *flags = SNMP_MSG_FLAG_AUTH_BIT;
01958     else if (sec_level == SNMP_SEC_LEVEL_AUTHPRIV)
01959         *flags = SNMP_MSG_FLAG_AUTH_BIT | SNMP_MSG_FLAG_PRIV_BIT;
01960 
01961     if (SNMP_CMD_CONFIRMED(msg_command))
01962         *flags |= SNMP_MSG_FLAG_RPRT_BIT;
01963 
01964     return;
01965 }
01966 
01967 static int
01968 snmpv3_verify_msg(netsnmp_request_list *rp, netsnmp_pdu *pdu)
01969 {
01970     netsnmp_pdu    *rpdu;
01971 
01972     if (!rp || !rp->pdu || !pdu)
01973         return 0;
01974     /*
01975      * Reports don't have to match anything according to the spec 
01976      */
01977     if (pdu->command == SNMP_MSG_REPORT)
01978         return 1;
01979     rpdu = rp->pdu;
01980     if (rp->request_id != pdu->reqid || rpdu->reqid != pdu->reqid)
01981         return 0;
01982     if (rpdu->version != pdu->version)
01983         return 0;
01984     if (rpdu->securityModel != pdu->securityModel)
01985         return 0;
01986     if (rpdu->securityLevel != pdu->securityLevel)
01987         return 0;
01988 
01989     if (rpdu->contextEngineIDLen != pdu->contextEngineIDLen ||
01990         memcmp(rpdu->contextEngineID, pdu->contextEngineID,
01991                pdu->contextEngineIDLen))
01992         return 0;
01993     if (rpdu->contextNameLen != pdu->contextNameLen ||
01994         memcmp(rpdu->contextName, pdu->contextName, pdu->contextNameLen))
01995         return 0;
01996 
01997     /* tunneled transports don't have a securityEngineID...  that's
01998        USM specific (and maybe other future ones) */
01999     if (pdu->securityModel == SNMP_SEC_MODEL_USM &&
02000         (rpdu->securityEngineIDLen != pdu->securityEngineIDLen ||
02001         memcmp(rpdu->securityEngineID, pdu->securityEngineID,
02002                pdu->securityEngineIDLen)))
02003         return 0;
02004 
02005     /* the securityName must match though regardless of secmodel */
02006     if (rpdu->securityNameLen != pdu->securityNameLen ||
02007         memcmp(rpdu->securityName, pdu->securityName,
02008                pdu->securityNameLen))
02009         return 0;
02010     return 1;
02011 }
02012 
02013 
02014 /*
02015  * SNMPv3
02016  * * Takes a session and a pdu and serializes the ASN PDU into the area
02017  * * pointed to by packet.  out_length is the size of the data area available.
02018  * * Returns the length of the completed packet in out_length.  If any errors
02019  * * occur, -1 is returned.  If all goes well, 0 is returned.
02020  */
02021 static int
02022 snmpv3_build(u_char ** pkt, size_t * pkt_len, size_t * offset,
02023              netsnmp_session * session, netsnmp_pdu *pdu)
02024 {
02025     int             ret;
02026 
02027     session->s_snmp_errno = 0;
02028     session->s_errno = 0;
02029 
02030     /*
02031      * do validation for PDU types 
02032      */
02033     switch (pdu->command) {
02034     case SNMP_MSG_RESPONSE:
02035     case SNMP_MSG_TRAP2:
02036     case SNMP_MSG_REPORT:
02037         netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE));
02038         /*
02039          * Fallthrough 
02040          */
02041 #ifndef NETSNMP_NOTIFY_ONLY
02042     case SNMP_MSG_GET:
02043     case SNMP_MSG_GETNEXT:
02044 #endif /* ! NETSNMP_NOTIFY_ONLY */
02045 #ifndef NETSNMP_NO_WRITE_SUPPORT
02046     case SNMP_MSG_SET:
02047 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
02048     case SNMP_MSG_INFORM:
02049         if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
02050             pdu->errstat = 0;
02051         if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
02052             pdu->errindex = 0;
02053         break;
02054 
02055 #ifndef NETSNMP_NOTIFY_ONLY
02056     case SNMP_MSG_GETBULK:
02057         if (pdu->max_repetitions < 0) {
02058             session->s_snmp_errno = SNMPERR_BAD_REPETITIONS;
02059             return -1;
02060         }
02061         if (pdu->non_repeaters < 0) {
02062             session->s_snmp_errno = SNMPERR_BAD_REPEATERS;
02063             return -1;
02064         }
02065         break;
02066 #endif /* ! NETSNMP_NOTIFY_ONLY */
02067 
02068     case SNMP_MSG_TRAP:
02069         session->s_snmp_errno = SNMPERR_V1_IN_V2;
02070         return -1;
02071 
02072     default:
02073         session->s_snmp_errno = SNMPERR_UNKNOWN_PDU;
02074         return -1;
02075     }
02076 
02077     /* Do we need to set the session security engineid? */
02078     if (pdu->securityEngineIDLen == 0) {
02079         if (session->securityEngineIDLen) {
02080             snmpv3_clone_engineID(&pdu->securityEngineID,
02081                                   &pdu->securityEngineIDLen,
02082                                   session->securityEngineID,
02083                                   session->securityEngineIDLen);
02084         }
02085     }
02086     
02087     /* Do we need to set the session context engineid? */
02088     if (pdu->contextEngineIDLen == 0) {
02089         if (session->contextEngineIDLen) {
02090             snmpv3_clone_engineID(&pdu->contextEngineID,
02091                                   &pdu->contextEngineIDLen,
02092                                   session->contextEngineID,
02093                                   session->contextEngineIDLen);
02094         } else if (pdu->securityEngineIDLen) {
02095             snmpv3_clone_engineID(&pdu->contextEngineID,
02096                                   &pdu->contextEngineIDLen,
02097                                   pdu->securityEngineID,
02098                                   pdu->securityEngineIDLen);
02099         }
02100     }
02101 
02102     if (pdu->contextName == NULL) {
02103         if (!session->contextName) {
02104             session->s_snmp_errno = SNMPERR_BAD_CONTEXT;
02105             return -1;
02106         }
02107         pdu->contextName = strdup(session->contextName);
02108         if (pdu->contextName == NULL) {
02109             session->s_snmp_errno = SNMPERR_GENERR;
02110             return -1;
02111         }
02112         pdu->contextNameLen = session->contextNameLen;
02113     }
02114     if (pdu->securityModel == SNMP_DEFAULT_SECMODEL) {
02115         pdu->securityModel = session->securityModel;
02116         if (pdu->securityModel == SNMP_DEFAULT_SECMODEL) {
02117             pdu->securityModel = se_find_value_in_slist("snmp_secmods", netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECMODEL));
02118             
02119             if (pdu->securityModel <= 0) {
02120                 pdu->securityModel = SNMP_SEC_MODEL_USM;
02121             }
02122         }
02123     }
02124     if (pdu->securityNameLen == 0 && pdu->securityName == NULL) {
02125         if (session->securityModel != SNMP_SEC_MODEL_TSM &&
02126             session->securityNameLen == 0) {
02127             session->s_snmp_errno = SNMPERR_BAD_SEC_NAME;
02128             return -1;
02129         }
02130         if (session->securityName) {
02131             pdu->securityName = strdup(session->securityName);
02132             if (pdu->securityName == NULL) {
02133                 session->s_snmp_errno = SNMPERR_GENERR;
02134                 return -1;
02135             }
02136             pdu->securityNameLen = session->securityNameLen;
02137         } else {
02138             pdu->securityName = strdup("");
02139             session->securityName = strdup("");
02140         }
02141     }
02142     if (pdu->securityLevel == 0) {
02143         if (session->securityLevel == 0) {
02144             session->s_snmp_errno = SNMPERR_BAD_SEC_LEVEL;
02145             return -1;
02146         }
02147         pdu->securityLevel = session->securityLevel;
02148     }
02149     DEBUGMSGTL(("snmp_build",
02150                 "Building SNMPv3 message (secName:\"%s\", secLevel:%s)...\n",
02151                 ((session->securityName) ? (char *) session->securityName :
02152                  ((pdu->securityName) ? (char *) pdu->securityName :
02153                   "ERROR: undefined")), secLevelName[pdu->securityLevel]));
02154 
02155     DEBUGDUMPSECTION("send", "SNMPv3 Message");
02156 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
02157     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE)) {
02158         ret = snmpv3_packet_realloc_rbuild(pkt, pkt_len, offset,
02159                                            session, pdu, NULL, 0);
02160     } else {
02161 #endif
02162         ret = snmpv3_packet_build(session, pdu, *pkt, pkt_len, NULL, 0);
02163 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
02164     }
02165 #endif
02166     DEBUGINDENTLESS();
02167     if (-1 != ret) {
02168         session->s_snmp_errno = ret;
02169     }
02170 
02171     return ret;
02172 
02173 }                               /* end snmpv3_build() */
02174 
02175 
02176 
02177 
02178 static u_char  *
02179 snmpv3_header_build(netsnmp_session * session, netsnmp_pdu *pdu,
02180                     u_char * packet, size_t * out_length,
02181                     size_t length, u_char ** msg_hdr_e)
02182 {
02183     u_char         *global_hdr, *global_hdr_e;
02184     u_char         *cp;
02185     u_char          msg_flags;
02186     long            max_size;
02187     long            sec_model;
02188     u_char         *pb, *pb0e;
02189 
02190     /*
02191      * Save current location and build SEQUENCE tag and length placeholder
02192      * * for SNMP message sequence (actual length inserted later)
02193      */
02194     cp = asn_build_sequence(packet, out_length,
02195                             (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
02196                             length);
02197     if (cp == NULL)
02198         return NULL;
02199     if (msg_hdr_e != NULL)
02200         *msg_hdr_e = cp;
02201     pb0e = cp;
02202 
02203 
02204     /*
02205      * store the version field - msgVersion
02206      */
02207     DEBUGDUMPHEADER("send", "SNMP Version Number");
02208     cp = asn_build_int(cp, out_length,
02209                        (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02210                                  ASN_INTEGER), (long *) &pdu->version,
02211                        sizeof(pdu->version));
02212     DEBUGINDENTLESS();
02213     if (cp == NULL)
02214         return NULL;
02215 
02216     global_hdr = cp;
02217     /*
02218      * msgGlobalData HeaderData 
02219      */
02220     DEBUGDUMPSECTION("send", "msgGlobalData");
02221     cp = asn_build_sequence(cp, out_length,
02222                             (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0);
02223     if (cp == NULL)
02224         return NULL;
02225     global_hdr_e = cp;
02226 
02227 
02228     /*
02229      * msgID 
02230      */
02231     DEBUGDUMPHEADER("send", "msgID");
02232     cp = asn_build_int(cp, out_length,
02233                        (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02234                                  ASN_INTEGER), &pdu->msgid,
02235                        sizeof(pdu->msgid));
02236     DEBUGINDENTLESS();
02237     if (cp == NULL)
02238         return NULL;
02239 
02240     /*
02241      * msgMaxSize 
02242      */
02243     max_size = session->rcvMsgMaxSize;
02244     DEBUGDUMPHEADER("send", "msgMaxSize");
02245     cp = asn_build_int(cp, out_length,
02246                        (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02247                                  ASN_INTEGER), &max_size,
02248                        sizeof(max_size));
02249     DEBUGINDENTLESS();
02250     if (cp == NULL)
02251         return NULL;
02252 
02253     /*
02254      * msgFlags 
02255      */
02256     snmpv3_calc_msg_flags(pdu->securityLevel, pdu->command, &msg_flags);
02257     DEBUGDUMPHEADER("send", "msgFlags");
02258     cp = asn_build_string(cp, out_length,
02259                           (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02260                                     ASN_OCTET_STR), &msg_flags,
02261                           sizeof(msg_flags));
02262     DEBUGINDENTLESS();
02263     if (cp == NULL)
02264         return NULL;
02265 
02266     /*
02267      * msgSecurityModel 
02268      */
02269     sec_model = pdu->securityModel;
02270     DEBUGDUMPHEADER("send", "msgSecurityModel");
02271     cp = asn_build_int(cp, out_length,
02272                        (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02273                                  ASN_INTEGER), &sec_model,
02274                        sizeof(sec_model));
02275     DEBUGINDENTADD(-4);         /* return from global data indent */
02276     if (cp == NULL)
02277         return NULL;
02278 
02279 
02280     /*
02281      * insert actual length of globalData
02282      */
02283     pb = asn_build_sequence(global_hdr, out_length,
02284                             (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
02285                             cp - global_hdr_e);
02286     if (pb == NULL)
02287         return NULL;
02288 
02289 
02290     /*
02291      * insert the actual length of the entire packet
02292      */
02293     pb = asn_build_sequence(packet, out_length,
02294                             (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
02295                             length + (cp - pb0e));
02296     if (pb == NULL)
02297         return NULL;
02298 
02299     return cp;
02300 
02301 }                               /* end snmpv3_header_build() */
02302 
02303 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
02304 
02305 int
02306 snmpv3_header_realloc_rbuild(u_char ** pkt, size_t * pkt_len,
02307                              size_t * offset, netsnmp_session * session,
02308                              netsnmp_pdu *pdu)
02309 {
02310     size_t          start_offset = *offset;
02311     u_char          msg_flags;
02312     long            max_size, sec_model;
02313     int             rc = 0;
02314 
02315     /*
02316      * msgSecurityModel.  
02317      */
02318     sec_model = pdu->securityModel;
02319     DEBUGDUMPHEADER("send", "msgSecurityModel");
02320     rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
02321                                 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02322                                           ASN_INTEGER), &sec_model,
02323                                 sizeof(sec_model));
02324     DEBUGINDENTLESS();
02325     if (rc == 0) {
02326         return 0;
02327     }
02328 
02329     /*
02330      * msgFlags.  
02331      */
02332     snmpv3_calc_msg_flags(pdu->securityLevel, pdu->command, &msg_flags);
02333     DEBUGDUMPHEADER("send", "msgFlags");
02334     rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
02335                                    (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
02336                                              | ASN_OCTET_STR), &msg_flags,
02337                                    sizeof(msg_flags));
02338     DEBUGINDENTLESS();
02339     if (rc == 0) {
02340         return 0;
02341     }
02342 
02343     /*
02344      * msgMaxSize.  
02345      */
02346     max_size = session->rcvMsgMaxSize;
02347     DEBUGDUMPHEADER("send", "msgMaxSize");
02348     rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
02349                                 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02350                                           ASN_INTEGER), &max_size,
02351                                 sizeof(max_size));
02352     DEBUGINDENTLESS();
02353     if (rc == 0) {
02354         return 0;
02355     }
02356 
02357     /*
02358      * msgID.  
02359      */
02360     DEBUGDUMPHEADER("send", "msgID");
02361     rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
02362                                 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02363                                           ASN_INTEGER), &pdu->msgid,
02364                                 sizeof(pdu->msgid));
02365     DEBUGINDENTLESS();
02366     if (rc == 0) {
02367         return 0;
02368     }
02369 
02370     /*
02371      * Global data sequence.  
02372      */
02373     rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
02374                                      (u_char) (ASN_SEQUENCE |
02375                                                ASN_CONSTRUCTOR),
02376                                      *offset - start_offset);
02377     if (rc == 0) {
02378         return 0;
02379     }
02380 
02381     /*
02382      * Store the version field - msgVersion.  
02383      */
02384     DEBUGDUMPHEADER("send", "SNMP Version Number");
02385     rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
02386                                 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02387                                           ASN_INTEGER),
02388                                 (long *) &pdu->version,
02389                                 sizeof(pdu->version));
02390     DEBUGINDENTLESS();
02391     return rc;
02392 }                               /* end snmpv3_header_realloc_rbuild() */
02393 #endif                          /* NETSNMP_USE_REVERSE_ASNENCODING */
02394 
02395 static u_char  *
02396 snmpv3_scopedPDU_header_build(netsnmp_pdu *pdu,
02397                               u_char * packet, size_t * out_length,
02398                               u_char ** spdu_e)
02399 {
02400     size_t          init_length;
02401     u_char         *scopedPdu, *pb;
02402 
02403 
02404     init_length = *out_length;
02405 
02406     pb = scopedPdu = packet;
02407     pb = asn_build_sequence(pb, out_length,
02408                             (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0);
02409     if (pb == NULL)
02410         return NULL;
02411     if (spdu_e)
02412         *spdu_e = pb;
02413 
02414     DEBUGDUMPHEADER("send", "contextEngineID");
02415     pb = asn_build_string(pb, out_length,
02416                           (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
02417                           pdu->contextEngineID, pdu->contextEngineIDLen);
02418     DEBUGINDENTLESS();
02419     if (pb == NULL)
02420         return NULL;
02421 
02422     DEBUGDUMPHEADER("send", "contextName");
02423     pb = asn_build_string(pb, out_length,
02424                           (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
02425                           (u_char *) pdu->contextName,
02426                           pdu->contextNameLen);
02427     DEBUGINDENTLESS();
02428     if (pb == NULL)
02429         return NULL;
02430 
02431     return pb;
02432 
02433 }                               /* end snmpv3_scopedPDU_header_build() */
02434 
02435 
02436 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
02437 int
02438 snmpv3_scopedPDU_header_realloc_rbuild(u_char ** pkt, size_t * pkt_len,
02439                                        size_t * offset, netsnmp_pdu *pdu,
02440                                        size_t body_len)
02441 {
02442     size_t          start_offset = *offset;
02443     int             rc = 0;
02444 
02445     /*
02446      * contextName.  
02447      */
02448     DEBUGDUMPHEADER("send", "contextName");
02449     rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
02450                                    (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
02451                                              | ASN_OCTET_STR),
02452                                    (u_char *) pdu->contextName,
02453                                    pdu->contextNameLen);
02454     DEBUGINDENTLESS();
02455     if (rc == 0) {
02456         return 0;
02457     }
02458 
02459     /*
02460      * contextEngineID.  
02461      */
02462     DEBUGDUMPHEADER("send", "contextEngineID");
02463     rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
02464                                    (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
02465                                              | ASN_OCTET_STR),
02466                                    pdu->contextEngineID,
02467                                    pdu->contextEngineIDLen);
02468     DEBUGINDENTLESS();
02469     if (rc == 0) {
02470         return 0;
02471     }
02472 
02473     rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
02474                                      (u_char) (ASN_SEQUENCE |
02475                                                ASN_CONSTRUCTOR),
02476                                      *offset - start_offset + body_len);
02477 
02478     return rc;
02479 }                               /* end snmpv3_scopedPDU_header_realloc_rbuild() */
02480 #endif                          /* NETSNMP_USE_REVERSE_ASNENCODING */
02481 
02482 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
02483 /*
02484  * returns 0 if success, -1 if fail, not 0 if SM build failure 
02485  */
02486 int
02487 snmpv3_packet_realloc_rbuild(u_char ** pkt, size_t * pkt_len,
02488                              size_t * offset, netsnmp_session * session,
02489                              netsnmp_pdu *pdu, u_char * pdu_data,
02490                              size_t pdu_data_len)
02491 {
02492     u_char         *scoped_pdu, *hdrbuf = NULL, *hdr = NULL;
02493     size_t          hdrbuf_len = SNMP_MAX_MSG_V3_HDRS, hdr_offset =
02494         0, spdu_offset = 0;
02495     size_t          body_end_offset = *offset, body_len = 0;
02496     struct snmp_secmod_def *sptr = NULL;
02497     int             rc = 0;
02498 
02499     /*
02500      * Build a scopedPDU structure into the packet buffer.  
02501      */
02502     DEBUGPRINTPDUTYPE("send", pdu->command);
02503     if (pdu_data) {
02504         while ((*pkt_len - *offset) < pdu_data_len) {
02505             if (!asn_realloc(pkt, pkt_len)) {
02506                 return -1;
02507             }
02508         }
02509 
02510         *offset += pdu_data_len;
02511         memcpy(*pkt + *pkt_len - *offset, pdu_data, pdu_data_len);
02512     } else {
02513         rc = snmp_pdu_realloc_rbuild(pkt, pkt_len, offset, pdu);
02514         if (rc == 0) {
02515             return -1;
02516         }
02517     }
02518     body_len = *offset - body_end_offset;
02519 
02520     DEBUGDUMPSECTION("send", "ScopedPdu");
02521     rc = snmpv3_scopedPDU_header_realloc_rbuild(pkt, pkt_len, offset,
02522                                                 pdu, body_len);
02523     if (rc == 0) {
02524         return -1;
02525     }
02526     spdu_offset = *offset;
02527     DEBUGINDENTADD(-4);         /*  Return from Scoped PDU.  */
02528 
02529     if ((hdrbuf = (u_char *) malloc(hdrbuf_len)) == NULL) {
02530         return -1;
02531     }
02532 
02533     rc = snmpv3_header_realloc_rbuild(&hdrbuf, &hdrbuf_len, &hdr_offset,
02534                                       session, pdu);
02535     if (rc == 0) {
02536         SNMP_FREE(hdrbuf);
02537         return -1;
02538     }
02539     hdr = hdrbuf + hdrbuf_len - hdr_offset;
02540     scoped_pdu = *pkt + *pkt_len - spdu_offset;
02541 
02542     /*
02543      * Call the security module to possibly encrypt and authenticate the
02544      * message---the entire message to transmitted on the wire is returned.  
02545      */
02546 
02547     sptr = find_sec_mod(pdu->securityModel);
02548     DEBUGDUMPSECTION("send", "SM msgSecurityParameters");
02549     if (sptr && sptr->encode_reverse) {
02550         struct snmp_secmod_outgoing_params parms;
02551 
02552         parms.msgProcModel = pdu->msgParseModel;
02553         parms.globalData = hdr;
02554         parms.globalDataLen = hdr_offset;
02555         parms.maxMsgSize = SNMP_MAX_MSG_SIZE;
02556         parms.secModel = pdu->securityModel;
02557         parms.secEngineID = pdu->securityEngineID;
02558         parms.secEngineIDLen = pdu->securityEngineIDLen;
02559         parms.secName = pdu->securityName;
02560         parms.secNameLen = pdu->securityNameLen;
02561         parms.secLevel = pdu->securityLevel;
02562         parms.scopedPdu = scoped_pdu;
02563         parms.scopedPduLen = spdu_offset;
02564         parms.secStateRef = pdu->securityStateRef;
02565         parms.wholeMsg = pkt;
02566         parms.wholeMsgLen = pkt_len;
02567         parms.wholeMsgOffset = offset;
02568         parms.session = session;
02569         parms.pdu = pdu;
02570 
02571         rc = (*sptr->encode_reverse) (&parms);
02572     } else {
02573         if (!sptr) {
02574             snmp_log(LOG_ERR,
02575                      "no such security service available: %d\n",
02576                      pdu->securityModel);
02577         } else if (!sptr->encode_reverse) {
02578             snmp_log(LOG_ERR,
02579                      "security service %d doesn't support reverse encoding.\n",
02580                      pdu->securityModel);
02581         }
02582         rc = -1;
02583     }
02584 
02585     DEBUGINDENTLESS();
02586     SNMP_FREE(hdrbuf);
02587     return rc;
02588 }                               /* end snmpv3_packet_realloc_rbuild() */
02589 #endif                          /* NETSNMP_USE_REVERSE_ASNENCODING */
02590 
02591 /*
02592  * returns 0 if success, -1 if fail, not 0 if SM build failure 
02593  */
02594 int
02595 snmpv3_packet_build(netsnmp_session * session, netsnmp_pdu *pdu,
02596                     u_char * packet, size_t * out_length,
02597                     u_char * pdu_data, size_t pdu_data_len)
02598 {
02599     u_char         *global_data, *sec_params, *spdu_hdr_e;
02600     size_t          global_data_len, sec_params_len;
02601     u_char          spdu_buf[SNMP_MAX_MSG_SIZE];
02602     size_t          spdu_buf_len, spdu_len;
02603     u_char         *cp;
02604     int             result;
02605     struct snmp_secmod_def *sptr;
02606 
02607     global_data = packet;
02608 
02609     /*
02610      * build the headers for the packet, returned addr = start of secParams
02611      */
02612     sec_params = snmpv3_header_build(session, pdu, global_data,
02613                                      out_length, 0, NULL);
02614     if (sec_params == NULL)
02615         return -1;
02616     global_data_len = sec_params - global_data;
02617     sec_params_len = *out_length;       /* length left in packet buf for sec_params */
02618 
02619 
02620     /*
02621      * build a scopedPDU structure into spdu_buf
02622      */
02623     spdu_buf_len = SNMP_MAX_MSG_SIZE;
02624     DEBUGDUMPSECTION("send", "ScopedPdu");
02625     cp = snmpv3_scopedPDU_header_build(pdu, spdu_buf, &spdu_buf_len,
02626                                        &spdu_hdr_e);
02627     if (cp == NULL)
02628         return -1;
02629 
02630     /*
02631      * build the PDU structure onto the end of spdu_buf 
02632      */
02633     DEBUGPRINTPDUTYPE("send", ((pdu_data) ? *pdu_data : 0x00));
02634     if (pdu_data) {
02635         memcpy(cp, pdu_data, pdu_data_len);
02636         cp += pdu_data_len;
02637     } else {
02638         cp = snmp_pdu_build(pdu, cp, &spdu_buf_len);
02639         if (cp == NULL)
02640             return -1;
02641     }
02642     DEBUGINDENTADD(-4);         /* return from Scoped PDU */
02643 
02644     /*
02645      * re-encode the actual ASN.1 length of the scopedPdu
02646      */
02647     spdu_len = cp - spdu_hdr_e; /* length of scopedPdu minus ASN.1 headers */
02648     spdu_buf_len = SNMP_MAX_MSG_SIZE;
02649     if (asn_build_sequence(spdu_buf, &spdu_buf_len,
02650                            (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
02651                            spdu_len) == NULL)
02652         return -1;
02653     spdu_len = cp - spdu_buf;   /* the length of the entire scopedPdu */
02654 
02655 
02656     /*
02657      * call the security module to possibly encrypt and authenticate the
02658      * message - the entire message to transmitted on the wire is returned
02659      */
02660     cp = NULL;
02661     *out_length = SNMP_MAX_MSG_SIZE;
02662     DEBUGDUMPSECTION("send", "SM msgSecurityParameters");
02663     sptr = find_sec_mod(pdu->securityModel);
02664     if (sptr && sptr->encode_forward) {
02665         struct snmp_secmod_outgoing_params parms;
02666         parms.msgProcModel = pdu->msgParseModel;
02667         parms.globalData = global_data;
02668         parms.globalDataLen = global_data_len;
02669         parms.maxMsgSize = SNMP_MAX_MSG_SIZE;
02670         parms.secModel = pdu->securityModel;
02671         parms.secEngineID = pdu->securityEngineID;
02672         parms.secEngineIDLen = pdu->securityEngineIDLen;
02673         parms.secName = pdu->securityName;
02674         parms.secNameLen = pdu->securityNameLen;
02675         parms.secLevel = pdu->securityLevel;
02676         parms.scopedPdu = spdu_buf;
02677         parms.scopedPduLen = spdu_len;
02678         parms.secStateRef = pdu->securityStateRef;
02679         parms.secParams = sec_params;
02680         parms.secParamsLen = &sec_params_len;
02681         parms.wholeMsg = &cp;
02682         parms.wholeMsgLen = out_length;
02683         parms.session = session;
02684         parms.pdu = pdu;
02685         result = (*sptr->encode_forward) (&parms);
02686     } else {
02687         if (!sptr) {
02688             snmp_log(LOG_ERR, "no such security service available: %d\n",
02689                      pdu->securityModel);
02690         } else if (!sptr->encode_forward) {
02691             snmp_log(LOG_ERR,
02692                      "security service %d doesn't support forward out encoding.\n",
02693                      pdu->securityModel);
02694         }
02695         result = -1;
02696     }
02697     DEBUGINDENTLESS();
02698     return result;
02699 
02700 }                               /* end snmpv3_packet_build() */
02701 
02702 
02703 /*
02704  * Takes a session and a pdu and serializes the ASN PDU into the area
02705  * pointed to by *pkt.  *pkt_len is the size of the data area available.
02706  * Returns the length of the completed packet in *offset.  If any errors
02707  * occur, -1 is returned.  If all goes well, 0 is returned.
02708  */
02709 
02710 static int
02711 _snmp_build(u_char ** pkt, size_t * pkt_len, size_t * offset,
02712             netsnmp_session * session, netsnmp_pdu *pdu)
02713 {
02714 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
02715     u_char         *h0e = NULL;
02716     size_t          start_offset = *offset;
02717     long            version;
02718     int             rc = 0;
02719 #endif /* support for community based SNMP */
02720     
02721     u_char         *h0, *h1;
02722     u_char         *cp;
02723     size_t          length;
02724 
02725     session->s_snmp_errno = 0;
02726     session->s_errno = 0;
02727 
02728     if (pdu->version == SNMP_VERSION_3) {
02729         return snmpv3_build(pkt, pkt_len, offset, session, pdu);
02730     }
02731 
02732     switch (pdu->command) {
02733     case SNMP_MSG_RESPONSE:
02734         netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE));
02735         /*
02736          * Fallthrough 
02737          */
02738 #ifndef NETSNMP_NOTIFY_ONLY
02739     case SNMP_MSG_GET:
02740     case SNMP_MSG_GETNEXT:
02741 #endif /* ! NETSNMP_NOTIFY_ONLY */
02742 #ifndef NETSNMP_NO_WRITE_SUPPORT
02743     case SNMP_MSG_SET:
02744 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
02745         /*
02746          * all versions support these PDU types 
02747          */
02748         /*
02749          * initialize defaulted PDU fields 
02750          */
02751 
02752         if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
02753             pdu->errstat = 0;
02754         if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
02755             pdu->errindex = 0;
02756         break;
02757 
02758     case SNMP_MSG_TRAP2:
02759         netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE));
02760         /*
02761          * Fallthrough 
02762          */
02763     case SNMP_MSG_INFORM:
02764 #ifndef NETSNMP_DISABLE_SNMPV1
02765         /*
02766          * not supported in SNMPv1 and SNMPsec 
02767          */
02768         if (pdu->version == SNMP_VERSION_1) {
02769             session->s_snmp_errno = SNMPERR_V2_IN_V1;
02770             return -1;
02771         }
02772 #endif
02773         if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
02774             pdu->errstat = 0;
02775         if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
02776             pdu->errindex = 0;
02777         break;
02778 
02779 #ifndef NETSNMP_NOTIFY_ONLY
02780     case SNMP_MSG_GETBULK:
02781         /*
02782          * not supported in SNMPv1 and SNMPsec 
02783          */
02784 #ifndef NETSNMP_DISABLE_SNMPV1
02785         if (pdu->version == SNMP_VERSION_1) {
02786             session->s_snmp_errno = SNMPERR_V2_IN_V1;
02787             return -1;
02788         }
02789 #endif
02790         if (pdu->max_repetitions < 0) {
02791             session->s_snmp_errno = SNMPERR_BAD_REPETITIONS;
02792             return -1;
02793         }
02794         if (pdu->non_repeaters < 0) {
02795             session->s_snmp_errno = SNMPERR_BAD_REPEATERS;
02796             return -1;
02797         }
02798         break;
02799 #endif /* ! NETSNMP_NOTIFY_ONLY */
02800 
02801     case SNMP_MSG_TRAP:
02802         /*
02803          * *only* supported in SNMPv1 and SNMPsec 
02804          */
02805 #ifndef NETSNMP_DISABLE_SNMPV1
02806         if (pdu->version != SNMP_VERSION_1) {
02807             session->s_snmp_errno = SNMPERR_V1_IN_V2;
02808             return -1;
02809         }
02810 #endif
02811         /*
02812          * initialize defaulted Trap PDU fields 
02813          */
02814         pdu->reqid = 1;         /* give a bogus non-error reqid for traps */
02815         if (pdu->enterprise_length == SNMP_DEFAULT_ENTERPRISE_LENGTH) {
02816             pdu->enterprise = (oid *) malloc(sizeof(DEFAULT_ENTERPRISE));
02817             if (pdu->enterprise == NULL) {
02818                 session->s_snmp_errno = SNMPERR_MALLOC;
02819                 return -1;
02820             }
02821             memmove(pdu->enterprise, DEFAULT_ENTERPRISE,
02822                     sizeof(DEFAULT_ENTERPRISE));
02823             pdu->enterprise_length =
02824                 sizeof(DEFAULT_ENTERPRISE) / sizeof(oid);
02825         }
02826         if (pdu->time == SNMP_DEFAULT_TIME)
02827             pdu->time = DEFAULT_TIME;
02828         /*
02829          * don't expect a response 
02830          */
02831         pdu->flags &= (~UCD_MSG_FLAG_EXPECT_RESPONSE);
02832         break;
02833 
02834     case SNMP_MSG_REPORT:      /* SNMPv3 only */
02835     default:
02836         session->s_snmp_errno = SNMPERR_UNKNOWN_PDU;
02837         return -1;
02838     }
02839 
02840     /*
02841      * save length 
02842      */
02843     length = *pkt_len;
02844 
02845     /*
02846      * setup administrative fields based on version 
02847      */
02848     /*
02849      * build the message wrapper and all the administrative fields
02850      * upto the PDU sequence
02851      * (note that actual length of message will be inserted later) 
02852      */
02853     h0 = *pkt;
02854     switch (pdu->version) {
02855 #ifndef NETSNMP_DISABLE_SNMPV1
02856     case SNMP_VERSION_1:
02857 #endif
02858 #ifndef NETSNMP_DISABLE_SNMPV2C
02859     case SNMP_VERSION_2c:
02860 #endif
02861 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
02862 #ifdef NETSNMP_NO_ZEROLENGTH_COMMUNITY
02863         if (pdu->community_len == 0) {
02864             if (session->community_len == 0) {
02865                 session->s_snmp_errno = SNMPERR_BAD_COMMUNITY;
02866                 return -1;
02867             }
02868             pdu->community = (u_char *) malloc(session->community_len);
02869             if (pdu->community == NULL) {
02870                 session->s_snmp_errno = SNMPERR_MALLOC;
02871                 return -1;
02872             }
02873             memmove(pdu->community,
02874                     session->community, session->community_len);
02875             pdu->community_len = session->community_len;
02876         }
02877 #else                           /* !NETSNMP_NO_ZEROLENGTH_COMMUNITY */
02878         if (pdu->community_len == 0 && pdu->command != SNMP_MSG_RESPONSE) {
02879             /*
02880              * copy session community exactly to pdu community 
02881              */
02882             if (0 == session->community_len) {
02883                 SNMP_FREE(pdu->community);
02884             } else if (pdu->community_len == session->community_len) {
02885                 memmove(pdu->community,
02886                         session->community, session->community_len);
02887             } else {
02888                 SNMP_FREE(pdu->community);
02889                 pdu->community = (u_char *) malloc(session->community_len);
02890                 if (pdu->community == NULL) {
02891                     session->s_snmp_errno = SNMPERR_MALLOC;
02892                     return -1;
02893                 }
02894                 memmove(pdu->community,
02895                         session->community, session->community_len);
02896             }
02897             pdu->community_len = session->community_len;
02898         }
02899 #endif                          /* !NETSNMP_NO_ZEROLENGTH_COMMUNITY */
02900 
02901         DEBUGMSGTL(("snmp_send", "Building SNMPv%ld message...\n",
02902                     (1 + pdu->version)));
02903 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
02904         if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE)) {
02905             DEBUGPRINTPDUTYPE("send", pdu->command);
02906             rc = snmp_pdu_realloc_rbuild(pkt, pkt_len, offset, pdu);
02907             if (rc == 0) {
02908                 return -1;
02909             }
02910 
02911             DEBUGDUMPHEADER("send", "Community String");
02912             rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
02913                                            (u_char) (ASN_UNIVERSAL |
02914                                                      ASN_PRIMITIVE |
02915                                                      ASN_OCTET_STR),
02916                                            pdu->community,
02917                                            pdu->community_len);
02918             DEBUGINDENTLESS();
02919             if (rc == 0) {
02920                 return -1;
02921             }
02922 
02923 
02924             /*
02925              * Store the version field.  
02926              */
02927             DEBUGDUMPHEADER("send", "SNMP Version Number");
02928 
02929             version = pdu->version;
02930             rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
02931                                         (u_char) (ASN_UNIVERSAL |
02932                                                   ASN_PRIMITIVE |
02933                                                   ASN_INTEGER),
02934                                         (long *) &version,
02935                                         sizeof(version));
02936             DEBUGINDENTLESS();
02937             if (rc == 0) {
02938                 return -1;
02939             }
02940 
02941             /*
02942              * Build the final sequence.  
02943              */
02944 #ifndef NETSNMP_DISABLE_SNMPV1
02945             if (pdu->version == SNMP_VERSION_1) {
02946                 DEBUGDUMPSECTION("send", "SNMPv1 Message");
02947             } else {
02948 #endif
02949                 DEBUGDUMPSECTION("send", "SNMPv2c Message");
02950 #ifndef NETSNMP_DISABLE_SNMPV1
02951             }
02952 #endif
02953             rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
02954                                              (u_char) (ASN_SEQUENCE |
02955                                                        ASN_CONSTRUCTOR),
02956                                              *offset - start_offset);
02957             DEBUGINDENTLESS();
02958 
02959             if (rc == 0) {
02960                 return -1;
02961             }
02962             return 0;
02963         } else {
02964 
02965 #endif                          /* NETSNMP_USE_REVERSE_ASNENCODING */
02966             /*
02967              * Save current location and build SEQUENCE tag and length
02968              * placeholder for SNMP message sequence
02969              * (actual length will be inserted later) 
02970              */
02971             cp = asn_build_sequence(*pkt, pkt_len,
02972                                     (u_char) (ASN_SEQUENCE |
02973                                               ASN_CONSTRUCTOR), 0);
02974             if (cp == NULL) {
02975                 return -1;
02976             }
02977             h0e = cp;
02978 
02979 #ifndef NETSNMP_DISABLE_SNMPV1
02980             if (pdu->version == SNMP_VERSION_1) {
02981                 DEBUGDUMPSECTION("send", "SNMPv1 Message");
02982             } else {
02983 #endif
02984                 DEBUGDUMPSECTION("send", "SNMPv2c Message");
02985 #ifndef NETSNMP_DISABLE_SNMPV1
02986             }
02987 #endif
02988 
02989             /*
02990              * store the version field 
02991              */
02992             DEBUGDUMPHEADER("send", "SNMP Version Number");
02993 
02994             version = pdu->version;
02995             cp = asn_build_int(cp, pkt_len,
02996                                (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
02997                                          ASN_INTEGER), (long *) &version,
02998                                sizeof(version));
02999             DEBUGINDENTLESS();
03000             if (cp == NULL)
03001                 return -1;
03002 
03003             /*
03004              * store the community string 
03005              */
03006             DEBUGDUMPHEADER("send", "Community String");
03007             cp = asn_build_string(cp, pkt_len,
03008                                   (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
03009                                             ASN_OCTET_STR), pdu->community,
03010                                   pdu->community_len);
03011             DEBUGINDENTLESS();
03012             if (cp == NULL)
03013                 return -1;
03014             break;
03015 
03016 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
03017         }
03018 #endif                          /* NETSNMP_USE_REVERSE_ASNENCODING */
03019         break;
03020 #endif /* support for community based SNMP */
03021     case SNMP_VERSION_2p:
03022     case SNMP_VERSION_sec:
03023     case SNMP_VERSION_2u:
03024     case SNMP_VERSION_2star:
03025     default:
03026         session->s_snmp_errno = SNMPERR_BAD_VERSION;
03027         return -1;
03028     }
03029 
03030     h1 = cp;
03031     DEBUGPRINTPDUTYPE("send", pdu->command);
03032     cp = snmp_pdu_build(pdu, cp, pkt_len);
03033     DEBUGINDENTADD(-4);         /* return from entire v1/v2c message */
03034     if (cp == NULL)
03035         return -1;
03036 
03037     /*
03038      * insert the actual length of the message sequence 
03039      */
03040     switch (pdu->version) {
03041 #ifndef NETSNMP_DISABLE_SNMPV1
03042     case SNMP_VERSION_1:
03043 #endif
03044 #ifndef NETSNMP_DISABLE_SNMPV2C
03045     case SNMP_VERSION_2c:
03046 #endif
03047 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
03048         asn_build_sequence(*pkt, &length,
03049                            (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
03050                            cp - h0e);
03051         break;
03052 #endif /* support for community based SNMP */
03053 
03054     case SNMP_VERSION_2p:
03055     case SNMP_VERSION_sec:
03056     case SNMP_VERSION_2u:
03057     case SNMP_VERSION_2star:
03058     default:
03059         session->s_snmp_errno = SNMPERR_BAD_VERSION;
03060         return -1;
03061     }
03062     *pkt_len = cp - *pkt;
03063     return 0;
03064 }
03065 
03066 int
03067 snmp_build(u_char ** pkt, size_t * pkt_len, size_t * offset,
03068            netsnmp_session * pss, netsnmp_pdu *pdu)
03069 {
03070     int             rc;
03071     rc = _snmp_build(pkt, pkt_len, offset, pss, pdu);
03072     if (rc) {
03073         if (!pss->s_snmp_errno) {
03074             snmp_log(LOG_ERR, "snmp_build: unknown failure");
03075             pss->s_snmp_errno = SNMPERR_BAD_ASN1_BUILD;
03076         }
03077         SET_SNMP_ERROR(pss->s_snmp_errno);
03078         rc = -1;
03079     }
03080     return rc;
03081 }
03082 
03083 /*
03084  * on error, returns NULL (likely an encoding problem). 
03085  */
03086 u_char         *
03087 snmp_pdu_build(netsnmp_pdu *pdu, u_char * cp, size_t * out_length)
03088 {
03089     u_char         *h1, *h1e, *h2, *h2e;
03090     netsnmp_variable_list *vp;
03091     size_t          length;
03092 
03093     length = *out_length;
03094     /*
03095      * Save current location and build PDU tag and length placeholder
03096      * (actual length will be inserted later) 
03097      */
03098     h1 = cp;
03099     cp = asn_build_sequence(cp, out_length, (u_char) pdu->command, 0);
03100     if (cp == NULL)
03101         return NULL;
03102     h1e = cp;
03103 
03104     /*
03105      * store fields in the PDU preceeding the variable-bindings sequence 
03106      */
03107     if (pdu->command != SNMP_MSG_TRAP) {
03108         /*
03109          * PDU is not an SNMPv1 trap 
03110          */
03111 
03112         DEBUGDUMPHEADER("send", "request_id");
03113         /*
03114          * request id 
03115          */
03116         cp = asn_build_int(cp, out_length,
03117                            (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
03118                                      ASN_INTEGER), &pdu->reqid,
03119                            sizeof(pdu->reqid));
03120         DEBUGINDENTLESS();
03121         if (cp == NULL)
03122             return NULL;
03123 
03124         /*
03125          * error status (getbulk non-repeaters) 
03126          */
03127         DEBUGDUMPHEADER("send", "error status");
03128         cp = asn_build_int(cp, out_length,
03129                            (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
03130                                      ASN_INTEGER), &pdu->errstat,
03131                            sizeof(pdu->errstat));
03132         DEBUGINDENTLESS();
03133         if (cp == NULL)
03134             return NULL;
03135 
03136         /*
03137          * error index (getbulk max-repetitions) 
03138          */
03139         DEBUGDUMPHEADER("send", "error index");
03140         cp = asn_build_int(cp, out_length,
03141                            (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
03142                                      ASN_INTEGER), &pdu->errindex,
03143                            sizeof(pdu->errindex));
03144         DEBUGINDENTLESS();
03145         if (cp == NULL)
03146             return NULL;
03147     } else {
03148         /*
03149          * an SNMPv1 trap PDU 
03150          */
03151 
03152         /*
03153          * enterprise 
03154          */
03155         DEBUGDUMPHEADER("send", "enterprise OBJID");
03156         cp = asn_build_objid(cp, out_length,
03157                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
03158                                        ASN_OBJECT_ID),
03159                              (oid *) pdu->enterprise,
03160                              pdu->enterprise_length);
03161         DEBUGINDENTLESS();
03162         if (cp == NULL)
03163             return NULL;
03164 
03165         /*
03166          * agent-addr 
03167          */
03168         DEBUGDUMPHEADER("send", "agent Address");
03169         cp = asn_build_string(cp, out_length,
03170                               (u_char) (ASN_IPADDRESS | ASN_PRIMITIVE),
03171                               (u_char *) pdu->agent_addr, 4);
03172         DEBUGINDENTLESS();
03173         if (cp == NULL)
03174             return NULL;
03175 
03176         /*
03177          * generic trap 
03178          */
03179         DEBUGDUMPHEADER("send", "generic trap number");
03180         cp = asn_build_int(cp, out_length,
03181                            (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
03182                                      ASN_INTEGER),
03183                            (long *) &pdu->trap_type,
03184                            sizeof(pdu->trap_type));
03185         DEBUGINDENTLESS();
03186         if (cp == NULL)
03187             return NULL;
03188 
03189         /*
03190          * specific trap 
03191          */
03192         DEBUGDUMPHEADER("send", "specific trap number");
03193         cp = asn_build_int(cp, out_length,
03194                            (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
03195                                      ASN_INTEGER),
03196                            (long *) &pdu->specific_type,
03197                            sizeof(pdu->specific_type));
03198         DEBUGINDENTLESS();
03199         if (cp == NULL)
03200             return NULL;
03201 
03202         /*
03203          * timestamp  
03204          */
03205         DEBUGDUMPHEADER("send", "timestamp");
03206         cp = asn_build_unsigned_int(cp, out_length,
03207                                     (u_char) (ASN_TIMETICKS |
03208                                               ASN_PRIMITIVE), &pdu->time,
03209                                     sizeof(pdu->time));
03210         DEBUGINDENTLESS();
03211         if (cp == NULL)
03212             return NULL;
03213     }
03214 
03215     /*
03216      * Save current location and build SEQUENCE tag and length placeholder
03217      * for variable-bindings sequence
03218      * (actual length will be inserted later) 
03219      */
03220     h2 = cp;
03221     cp = asn_build_sequence(cp, out_length,
03222                             (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0);
03223     if (cp == NULL)
03224         return NULL;
03225     h2e = cp;
03226 
03227     /*
03228      * Store variable-bindings 
03229      */
03230     DEBUGDUMPSECTION("send", "VarBindList");
03231     for (vp = pdu->variables; vp; vp = vp->next_variable) {
03232         DEBUGDUMPSECTION("send", "VarBind");
03233         cp = snmp_build_var_op(cp, vp->name, &vp->name_length, vp->type,
03234                                vp->val_len, (u_char *) vp->val.string,
03235                                out_length);
03236         DEBUGINDENTLESS();
03237         if (cp == NULL)
03238             return NULL;
03239     }
03240     DEBUGINDENTLESS();
03241 
03242     /*
03243      * insert actual length of variable-bindings sequence 
03244      */
03245     asn_build_sequence(h2, &length,
03246                        (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
03247                        cp - h2e);
03248 
03249     /*
03250      * insert actual length of PDU sequence 
03251      */
03252     asn_build_sequence(h1, &length, (u_char) pdu->command, cp - h1e);
03253 
03254     return cp;
03255 }
03256 
03257 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
03258 /*
03259  * On error, returns 0 (likely an encoding problem).  
03260  */
03261 int
03262 snmp_pdu_realloc_rbuild(u_char ** pkt, size_t * pkt_len, size_t * offset,
03263                         netsnmp_pdu *pdu)
03264 {
03265 #ifndef VPCACHE_SIZE
03266 #define VPCACHE_SIZE 50
03267 #endif
03268     netsnmp_variable_list *vpcache[VPCACHE_SIZE];
03269     netsnmp_variable_list *vp, *tmpvp;
03270     size_t          start_offset = *offset;
03271     int             i, wrapped = 0, notdone, final, rc = 0;
03272 
03273     DEBUGMSGTL(("snmp_pdu_realloc_rbuild", "starting\n"));
03274     for (vp = pdu->variables, i = VPCACHE_SIZE - 1; vp;
03275          vp = vp->next_variable, i--) {
03276         if (i < 0) {
03277             wrapped = notdone = 1;
03278             i = VPCACHE_SIZE - 1;
03279             DEBUGMSGTL(("snmp_pdu_realloc_rbuild", "wrapped\n"));
03280         }
03281         vpcache[i] = vp;
03282     }
03283     final = i + 1;
03284 
03285     do {
03286         for (i = final; i < VPCACHE_SIZE; i++) {
03287             vp = vpcache[i];
03288             DEBUGDUMPSECTION("send", "VarBind");
03289             rc = snmp_realloc_rbuild_var_op(pkt, pkt_len, offset, 1,
03290                                             vp->name, &vp->name_length,
03291                                             vp->type,
03292                                             (u_char *) vp->val.string,
03293                                             vp->val_len);
03294             DEBUGINDENTLESS();
03295             if (rc == 0) {
03296                 return 0;
03297             }
03298         }
03299 
03300         DEBUGINDENTLESS();
03301         if (wrapped) {
03302             notdone = 1;
03303             for (i = 0; i < final; i++) {
03304                 vp = vpcache[i];
03305                 DEBUGDUMPSECTION("send", "VarBind");
03306                 rc = snmp_realloc_rbuild_var_op(pkt, pkt_len, offset, 1,
03307                                                 vp->name, &vp->name_length,
03308                                                 vp->type,
03309                                                 (u_char *) vp->val.string,
03310                                                 vp->val_len);
03311                 DEBUGINDENTLESS();
03312                 if (rc == 0) {
03313                     return 0;
03314                 }
03315             }
03316 
03317             if (final == 0) {
03318                 tmpvp = vpcache[VPCACHE_SIZE - 1];
03319             } else {
03320                 tmpvp = vpcache[final - 1];
03321             }
03322             wrapped = 0;
03323 
03324             for (vp = pdu->variables, i = VPCACHE_SIZE - 1;
03325                  vp && vp != tmpvp; vp = vp->next_variable, i--) {
03326                 if (i < 0) {
03327                     wrapped = 1;
03328                     i = VPCACHE_SIZE - 1;
03329                     DEBUGMSGTL(("snmp_pdu_realloc_rbuild", "wrapped\n"));
03330                 }
03331                 vpcache[i] = vp;
03332             }
03333             final = i + 1;
03334         } else {
03335             notdone = 0;
03336         }
03337     } while (notdone);
03338 
03339     /*
03340      * Save current location and build SEQUENCE tag and length placeholder for
03341      * variable-bindings sequence (actual length will be inserted later).  
03342      */
03343 
03344     rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
03345                                      (u_char) (ASN_SEQUENCE |
03346                                                ASN_CONSTRUCTOR),
03347                                      *offset - start_offset);
03348 
03349     /*
03350      * Store fields in the PDU preceeding the variable-bindings sequence.  
03351      */
03352     if (pdu->command != SNMP_MSG_TRAP) {
03353         /*
03354          * Error index (getbulk max-repetitions).  
03355          */
03356         DEBUGDUMPHEADER("send", "error index");
03357         rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
03358                                     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
03359                                               | ASN_INTEGER),
03360                                     &pdu->errindex, sizeof(pdu->errindex));
03361         DEBUGINDENTLESS();
03362         if (rc == 0) {
03363             return 0;
03364         }
03365 
03366         /*
03367          * Error status (getbulk non-repeaters).  
03368          */
03369         DEBUGDUMPHEADER("send", "error status");
03370         rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
03371                                     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
03372                                               | ASN_INTEGER),
03373                                     &pdu->errstat, sizeof(pdu->errstat));
03374         DEBUGINDENTLESS();
03375         if (rc == 0) {
03376             return 0;
03377         }
03378 
03379         /*
03380          * Request ID.  
03381          */
03382         DEBUGDUMPHEADER("send", "request_id");
03383         rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
03384                                     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
03385                                               | ASN_INTEGER), &pdu->reqid,
03386                                     sizeof(pdu->reqid));
03387         DEBUGINDENTLESS();
03388         if (rc == 0) {
03389             return 0;
03390         }
03391     } else {
03392         /*
03393          * An SNMPv1 trap PDU.  
03394          */
03395 
03396         /*
03397          * Timestamp.  
03398          */
03399         DEBUGDUMPHEADER("send", "timestamp");
03400         rc = asn_realloc_rbuild_unsigned_int(pkt, pkt_len, offset, 1,
03401                                              (u_char) (ASN_TIMETICKS |
03402                                                        ASN_PRIMITIVE),
03403                                              &pdu->time,
03404                                              sizeof(pdu->time));
03405         DEBUGINDENTLESS();
03406         if (rc == 0) {
03407             return 0;
03408         }
03409 
03410         /*
03411          * Specific trap.  
03412          */
03413         DEBUGDUMPHEADER("send", "specific trap number");
03414         rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
03415                                     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
03416                                               | ASN_INTEGER),
03417                                     (long *) &pdu->specific_type,
03418                                     sizeof(pdu->specific_type));
03419         DEBUGINDENTLESS();
03420         if (rc == 0) {
03421             return 0;
03422         }
03423 
03424         /*
03425          * Generic trap.  
03426          */
03427         DEBUGDUMPHEADER("send", "generic trap number");
03428         rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
03429                                     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
03430                                               | ASN_INTEGER),
03431                                     (long *) &pdu->trap_type,
03432                                     sizeof(pdu->trap_type));
03433         DEBUGINDENTLESS();
03434         if (rc == 0) {
03435             return 0;
03436         }
03437 
03438         /*
03439          * Agent-addr.  
03440          */
03441         DEBUGDUMPHEADER("send", "agent Address");
03442         rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
03443                                        (u_char) (ASN_IPADDRESS |
03444                                                  ASN_PRIMITIVE),
03445                                        (u_char *) pdu->agent_addr, 4);
03446         DEBUGINDENTLESS();
03447         if (rc == 0) {
03448             return 0;
03449         }
03450 
03451         /*
03452          * Enterprise.  
03453          */
03454         DEBUGDUMPHEADER("send", "enterprise OBJID");
03455         rc = asn_realloc_rbuild_objid(pkt, pkt_len, offset, 1,
03456                                       (u_char) (ASN_UNIVERSAL |
03457                                                 ASN_PRIMITIVE |
03458                                                 ASN_OBJECT_ID),
03459                                       (oid *) pdu->enterprise,
03460                                       pdu->enterprise_length);
03461         DEBUGINDENTLESS();
03462         if (rc == 0) {
03463             return 0;
03464         }
03465     }
03466 
03467     /*
03468      * Build the PDU sequence.  
03469      */
03470     rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
03471                                      (u_char) pdu->command,
03472                                      *offset - start_offset);
03473     return rc;
03474 }
03475 #endif                          /* NETSNMP_USE_REVERSE_ASNENCODING */
03476 
03477 /*
03478  * Parses the packet received to determine version, either directly
03479  * from packets version field or inferred from ASN.1 construct.
03480  */
03481 static int
03482 snmp_parse_version(u_char * data, size_t length)
03483 {
03484     u_char          type;
03485     long            version = SNMPERR_BAD_VERSION;
03486 
03487     data = asn_parse_sequence(data, &length, &type,
03488                               (ASN_SEQUENCE | ASN_CONSTRUCTOR), "version");
03489     if (data) {
03490         DEBUGDUMPHEADER("recv", "SNMP Version");
03491         data =
03492             asn_parse_int(data, &length, &type, &version, sizeof(version));
03493         DEBUGINDENTLESS();
03494         if (!data || type != ASN_INTEGER) {
03495             return SNMPERR_BAD_VERSION;
03496         }
03497     }
03498     return version;
03499 }
03500 
03501 
03502 int
03503 snmpv3_parse(netsnmp_pdu *pdu,
03504              u_char * data,
03505              size_t * length,
03506              u_char ** after_header, netsnmp_session * sess)
03507 {
03508     u_char          type, msg_flags;
03509     long            ver, msg_max_size, msg_sec_model;
03510     size_t          max_size_response;
03511     u_char          tmp_buf[SNMP_MAX_MSG_SIZE];
03512     size_t          tmp_buf_len;
03513     u_char          pdu_buf[SNMP_MAX_MSG_SIZE];
03514     u_char         *mallocbuf = NULL;
03515     size_t          pdu_buf_len = SNMP_MAX_MSG_SIZE;
03516     u_char         *sec_params;
03517     u_char         *msg_data;
03518     u_char         *cp;
03519     size_t          asn_len, msg_len;
03520     int             ret, ret_val;
03521     struct snmp_secmod_def *sptr;
03522 
03523 
03524     msg_data = data;
03525     msg_len = *length;
03526 
03527 
03528     /*
03529      * message is an ASN.1 SEQUENCE  
03530      */
03531     DEBUGDUMPSECTION("recv", "SNMPv3 Message");
03532     data = asn_parse_sequence(data, length, &type,
03533                               (ASN_SEQUENCE | ASN_CONSTRUCTOR), "message");
03534     if (data == NULL) {
03535         /*
03536          * error msg detail is set 
03537          */
03538         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03539         DEBUGINDENTLESS();
03540         return SNMPERR_ASN_PARSE_ERR;
03541     }
03542 
03543     /*
03544      * parse msgVersion  
03545      */
03546     DEBUGDUMPHEADER("recv", "SNMP Version Number");
03547     data = asn_parse_int(data, length, &type, &ver, sizeof(ver));
03548     DEBUGINDENTLESS();
03549     if (data == NULL) {
03550         ERROR_MSG("bad parse of version");
03551         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03552         DEBUGINDENTLESS();
03553         return SNMPERR_ASN_PARSE_ERR;
03554     }
03555     pdu->version = ver;
03556 
03557     /*
03558      * parse msgGlobalData sequence  
03559      */
03560     cp = data;
03561     asn_len = *length;
03562     DEBUGDUMPSECTION("recv", "msgGlobalData");
03563     data = asn_parse_sequence(data, &asn_len, &type,
03564                               (ASN_SEQUENCE | ASN_CONSTRUCTOR),
03565                               "msgGlobalData");
03566     if (data == NULL) {
03567         /*
03568          * error msg detail is set 
03569          */
03570         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03571         DEBUGINDENTADD(-4);
03572         return SNMPERR_ASN_PARSE_ERR;
03573     }
03574     *length -= data - cp;       /* subtract off the length of the header */
03575 
03576     /*
03577      * msgID 
03578      */
03579     DEBUGDUMPHEADER("recv", "msgID");
03580     data =
03581         asn_parse_int(data, length, &type, &pdu->msgid,
03582                       sizeof(pdu->msgid));
03583     DEBUGINDENTLESS();
03584     if (data == NULL || type != ASN_INTEGER) {
03585         ERROR_MSG("error parsing msgID");
03586         DEBUGINDENTADD(-4);
03587         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03588         return SNMPERR_ASN_PARSE_ERR;
03589     }
03590 
03591     /*
03592      * Check the msgID we received is a legal value.  If not, then increment
03593      * snmpInASNParseErrs and return the appropriate error (see RFC 2572,
03594      * para. 7.2, section 2 -- note that a bad msgID means that the received
03595      * message is NOT a serialiization of an SNMPv3Message, since the msgID
03596      * field is out of bounds).  
03597      */
03598 
03599     if (pdu->msgid < 0 || pdu->msgid > 0x7fffffff) {
03600         snmp_log(LOG_ERR, "Received bad msgID (%ld %s %s).\n", pdu->msgid,
03601                  (pdu->msgid < 0) ? "<" : ">",
03602                  (pdu->msgid < 0) ? "0" : "2^31 - 1");
03603         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03604         DEBUGINDENTADD(-4);
03605         return SNMPERR_ASN_PARSE_ERR;
03606     }
03607 
03608     /*
03609      * msgMaxSize 
03610      */
03611     DEBUGDUMPHEADER("recv", "msgMaxSize");
03612     data = asn_parse_int(data, length, &type, &msg_max_size,
03613                          sizeof(msg_max_size));
03614     DEBUGINDENTLESS();
03615     if (data == NULL || type != ASN_INTEGER) {
03616         ERROR_MSG("error parsing msgMaxSize");
03617         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03618         DEBUGINDENTADD(-4);
03619         return SNMPERR_ASN_PARSE_ERR;
03620     }
03621 
03622     /*
03623      * Check the msgMaxSize we received is a legal value.  If not, then
03624      * increment snmpInASNParseErrs and return the appropriate error (see RFC
03625      * 2572, para. 7.2, section 2 -- note that a bad msgMaxSize means that the
03626      * received message is NOT a serialiization of an SNMPv3Message, since the
03627      * msgMaxSize field is out of bounds).
03628      * 
03629      * Note we store the msgMaxSize on a per-session basis which also seems
03630      * reasonable; it could vary from PDU to PDU but that would be strange
03631      * (also since we deal with a PDU at a time, it wouldn't make any
03632      * difference to our responses, if any).  
03633      */
03634 
03635     if (msg_max_size < 484) {
03636         snmp_log(LOG_ERR, "Received bad msgMaxSize (%lu < 484).\n",
03637                  msg_max_size);
03638         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03639         DEBUGINDENTADD(-4);
03640         return SNMPERR_ASN_PARSE_ERR;
03641     } else if (msg_max_size > 0x7fffffff) {
03642         snmp_log(LOG_ERR, "Received bad msgMaxSize (%lu > 2^31 - 1).\n",
03643                  msg_max_size);
03644         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03645         DEBUGINDENTADD(-4);
03646         return SNMPERR_ASN_PARSE_ERR;
03647     } else {
03648         DEBUGMSGTL(("snmpv3_parse", "msgMaxSize %lu received\n",
03649                     msg_max_size));
03650         sess->sndMsgMaxSize = msg_max_size;
03651     }
03652 
03653     /*
03654      * msgFlags 
03655      */
03656     tmp_buf_len = SNMP_MAX_MSG_SIZE;
03657     DEBUGDUMPHEADER("recv", "msgFlags");
03658     data = asn_parse_string(data, length, &type, tmp_buf, &tmp_buf_len);
03659     DEBUGINDENTLESS();
03660     if (data == NULL || type != ASN_OCTET_STR || tmp_buf_len != 1) {
03661         ERROR_MSG("error parsing msgFlags");
03662         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03663         DEBUGINDENTADD(-4);
03664         return SNMPERR_ASN_PARSE_ERR;
03665     }
03666     msg_flags = *tmp_buf;
03667     if (msg_flags & SNMP_MSG_FLAG_RPRT_BIT)
03668         pdu->flags |= SNMP_MSG_FLAG_RPRT_BIT;
03669     else
03670         pdu->flags &= (~SNMP_MSG_FLAG_RPRT_BIT);
03671 
03672     /*
03673      * msgSecurityModel 
03674      */
03675     DEBUGDUMPHEADER("recv", "msgSecurityModel");
03676     data = asn_parse_int(data, length, &type, &msg_sec_model,
03677                          sizeof(msg_sec_model));
03678     DEBUGINDENTADD(-4);         /* return from global data indent */
03679     if (data == NULL || type != ASN_INTEGER ||
03680         msg_sec_model < 1 || msg_sec_model > 0x7fffffff) {
03681         ERROR_MSG("error parsing msgSecurityModel");
03682         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03683         DEBUGINDENTLESS();
03684         return SNMPERR_ASN_PARSE_ERR;
03685     }
03686     sptr = find_sec_mod(msg_sec_model);
03687     if (!sptr) {
03688         snmp_log(LOG_WARNING, "unknown security model: %ld\n",
03689                  msg_sec_model);
03690         snmp_increment_statistic(STAT_SNMPUNKNOWNSECURITYMODELS);
03691         DEBUGINDENTLESS();
03692         return SNMPERR_UNKNOWN_SEC_MODEL;
03693     }
03694     pdu->securityModel = msg_sec_model;
03695 
03696     if (msg_flags & SNMP_MSG_FLAG_PRIV_BIT &&
03697         !(msg_flags & SNMP_MSG_FLAG_AUTH_BIT)) {
03698         ERROR_MSG("invalid message, illegal msgFlags");
03699         snmp_increment_statistic(STAT_SNMPINVALIDMSGS);
03700         DEBUGINDENTLESS();
03701         return SNMPERR_INVALID_MSG;
03702     }
03703     pdu->securityLevel = ((msg_flags & SNMP_MSG_FLAG_AUTH_BIT)
03704                           ? ((msg_flags & SNMP_MSG_FLAG_PRIV_BIT)
03705                              ? SNMP_SEC_LEVEL_AUTHPRIV
03706                              : SNMP_SEC_LEVEL_AUTHNOPRIV)
03707                           : SNMP_SEC_LEVEL_NOAUTH);
03708     /*
03709      * end of msgGlobalData 
03710      */
03711 
03712     /*
03713      * securtityParameters OCTET STRING begins after msgGlobalData 
03714      */
03715     sec_params = data;
03716     pdu->contextEngineID = (u_char *) calloc(1, SNMP_MAX_ENG_SIZE);
03717     pdu->contextEngineIDLen = SNMP_MAX_ENG_SIZE;
03718 
03719     /*
03720      * Note: there is no length limit on the msgAuthoritativeEngineID field,
03721      * although we would EXPECT it to be limited to 32 (the SnmpEngineID TC
03722      * limit).  We'll use double that here to be on the safe side.  
03723      */
03724 
03725     pdu->securityEngineID = (u_char *) calloc(1, SNMP_MAX_ENG_SIZE * 2);
03726     pdu->securityEngineIDLen = SNMP_MAX_ENG_SIZE * 2;
03727     pdu->securityName = (char *) calloc(1, SNMP_MAX_SEC_NAME_SIZE);
03728     pdu->securityNameLen = SNMP_MAX_SEC_NAME_SIZE;
03729 
03730     if ((pdu->securityName == NULL) ||
03731         (pdu->securityEngineID == NULL) ||
03732         (pdu->contextEngineID == NULL)) {
03733         return SNMPERR_MALLOC;
03734     }
03735 
03736     if (pdu_buf_len < msg_len
03737         && pdu->securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
03738         /*
03739          * space needed is larger than we have in the default buffer 
03740          */
03741         mallocbuf = (u_char *) calloc(1, msg_len);
03742         pdu_buf_len = msg_len;
03743         cp = mallocbuf;
03744     } else {
03745         memset(pdu_buf, 0, pdu_buf_len);
03746         cp = pdu_buf;
03747     }
03748 
03749     DEBUGDUMPSECTION("recv", "SM msgSecurityParameters");
03750     if (sptr->decode) {
03751         struct snmp_secmod_incoming_params parms;
03752         parms.msgProcModel = pdu->msgParseModel;
03753         parms.maxMsgSize = msg_max_size;
03754         parms.secParams = sec_params;
03755         parms.secModel = msg_sec_model;
03756         parms.secLevel = pdu->securityLevel;
03757         parms.wholeMsg = msg_data;
03758         parms.wholeMsgLen = msg_len;
03759         parms.secEngineID = pdu->securityEngineID;
03760         parms.secEngineIDLen = &pdu->securityEngineIDLen;
03761         parms.secName = pdu->securityName;
03762         parms.secNameLen = &pdu->securityNameLen;
03763         parms.scopedPdu = &cp;
03764         parms.scopedPduLen = &pdu_buf_len;
03765         parms.maxSizeResponse = &max_size_response;
03766         parms.secStateRef = &pdu->securityStateRef;
03767         parms.sess = sess;
03768         parms.pdu = pdu;
03769         parms.msg_flags = msg_flags;
03770         ret_val = (*sptr->decode) (&parms);
03771     } else {
03772         SNMP_FREE(mallocbuf);
03773         DEBUGINDENTLESS();
03774         snmp_log(LOG_WARNING, "security service %ld can't decode packets\n",
03775                  msg_sec_model);
03776         return (-1);
03777     }
03778 
03779     if (ret_val != SNMPERR_SUCCESS) {
03780         DEBUGDUMPSECTION("recv", "ScopedPDU");
03781         /*
03782          * Parse as much as possible -- though I don't see the point? [jbpn].  
03783          */
03784         if (cp) {
03785             cp = snmpv3_scopedPDU_parse(pdu, cp, &pdu_buf_len);
03786         }
03787         if (cp) {
03788             DEBUGPRINTPDUTYPE("recv", *cp);
03789             snmp_pdu_parse(pdu, cp, &pdu_buf_len);
03790             DEBUGINDENTADD(-8);
03791         } else {
03792             DEBUGINDENTADD(-4);
03793         }
03794 
03795         SNMP_FREE(mallocbuf);
03796         return ret_val;
03797     }
03798 
03799     /*
03800      * parse plaintext ScopedPDU sequence 
03801      */
03802     *length = pdu_buf_len;
03803     DEBUGDUMPSECTION("recv", "ScopedPDU");
03804     data = snmpv3_scopedPDU_parse(pdu, cp, length);
03805     if (data == NULL) {
03806         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03807         DEBUGINDENTADD(-4);
03808         SNMP_FREE(mallocbuf);
03809         return SNMPERR_ASN_PARSE_ERR;
03810     }
03811 
03812     /*
03813      * parse the PDU.  
03814      */
03815     if (after_header != NULL) {
03816         *after_header = data;
03817         tmp_buf_len = *length;
03818     }
03819 
03820     DEBUGPRINTPDUTYPE("recv", *data);
03821     ret = snmp_pdu_parse(pdu, data, length);
03822     DEBUGINDENTADD(-8);
03823 
03824     if (after_header != NULL) {
03825         *length = tmp_buf_len;
03826     }
03827 
03828     if (ret != SNMPERR_SUCCESS) {
03829         ERROR_MSG("error parsing PDU");
03830         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03831         SNMP_FREE(mallocbuf);
03832         return SNMPERR_ASN_PARSE_ERR;
03833     }
03834 
03835     SNMP_FREE(mallocbuf);
03836     return SNMPERR_SUCCESS;
03837 }                               /* end snmpv3_parse() */
03838 
03839 #define ERROR_STAT_LENGTH 11
03840 
03841 int
03842 snmpv3_make_report(netsnmp_pdu *pdu, int error)
03843 {
03844 
03845     long            ltmp;
03846     static oid      unknownSecurityLevel[] =
03847         { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 };
03848     static oid      notInTimeWindow[] =
03849         { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 };
03850     static oid      unknownUserName[] =
03851         { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 };
03852     static oid      unknownEngineID[] =
03853         { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 };
03854     static oid      wrongDigest[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 };
03855     static oid      decryptionError[] =
03856         { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 };
03857     oid            *err_var;
03858     int             err_var_len;
03859     int             stat_ind;
03860     struct snmp_secmod_def *sptr;
03861 
03862     switch (error) {
03863     case SNMPERR_USM_UNKNOWNENGINEID:
03864         stat_ind = STAT_USMSTATSUNKNOWNENGINEIDS;
03865         err_var = unknownEngineID;
03866         err_var_len = ERROR_STAT_LENGTH;
03867         break;
03868     case SNMPERR_USM_UNKNOWNSECURITYNAME:
03869         stat_ind = STAT_USMSTATSUNKNOWNUSERNAMES;
03870         err_var = unknownUserName;
03871         err_var_len = ERROR_STAT_LENGTH;
03872         break;
03873     case SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL:
03874         stat_ind = STAT_USMSTATSUNSUPPORTEDSECLEVELS;
03875         err_var = unknownSecurityLevel;
03876         err_var_len = ERROR_STAT_LENGTH;
03877         break;
03878     case SNMPERR_USM_AUTHENTICATIONFAILURE:
03879         stat_ind = STAT_USMSTATSWRONGDIGESTS;
03880         err_var = wrongDigest;
03881         err_var_len = ERROR_STAT_LENGTH;
03882         break;
03883     case SNMPERR_USM_NOTINTIMEWINDOW:
03884         stat_ind = STAT_USMSTATSNOTINTIMEWINDOWS;
03885         err_var = notInTimeWindow;
03886         err_var_len = ERROR_STAT_LENGTH;
03887         break;
03888     case SNMPERR_USM_DECRYPTIONERROR:
03889         stat_ind = STAT_USMSTATSDECRYPTIONERRORS;
03890         err_var = decryptionError;
03891         err_var_len = ERROR_STAT_LENGTH;
03892         break;
03893     default:
03894         return SNMPERR_GENERR;
03895     }
03896 
03897     snmp_free_varbind(pdu->variables);  /* free the current varbind */
03898 
03899     pdu->variables = NULL;
03900     SNMP_FREE(pdu->securityEngineID);
03901     pdu->securityEngineID =
03902         snmpv3_generate_engineID(&pdu->securityEngineIDLen);
03903     SNMP_FREE(pdu->contextEngineID);
03904     pdu->contextEngineID =
03905         snmpv3_generate_engineID(&pdu->contextEngineIDLen);
03906     pdu->command = SNMP_MSG_REPORT;
03907     pdu->errstat = 0;
03908     pdu->errindex = 0;
03909     SNMP_FREE(pdu->contextName);
03910     pdu->contextName = strdup("");
03911     pdu->contextNameLen = strlen(pdu->contextName);
03912 
03913     /*
03914      * reports shouldn't cache previous data. 
03915      */
03916     /*
03917      * FIX - yes they should but USM needs to follow new EoP to determine
03918      * which cached values to use 
03919      */
03920     if (pdu->securityStateRef) {
03921         sptr = find_sec_mod(pdu->securityModel);
03922         if (sptr) {
03923             if (sptr->pdu_free_state_ref) {
03924                 (*sptr->pdu_free_state_ref) (pdu->securityStateRef);
03925             } else {
03926                 snmp_log(LOG_ERR,
03927                          "Security Model %d can't free state references\n",
03928                          pdu->securityModel);
03929             }
03930         } else {
03931             snmp_log(LOG_ERR,
03932                      "Can't find security model to free ptr: %d\n",
03933                      pdu->securityModel);
03934         }
03935         pdu->securityStateRef = NULL;
03936     }
03937 
03938     if (error == SNMPERR_USM_NOTINTIMEWINDOW) {
03939         pdu->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
03940     } else {
03941         pdu->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
03942     }
03943 
03944     /*
03945      * find the appropriate error counter  
03946      */
03947 #ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
03948     ltmp = snmp_get_statistic(stat_ind);
03949 #else /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
03950     ltmp = 1;
03951 #endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
03952 
03953     /*
03954      * return the appropriate error counter  
03955      */
03956     snmp_pdu_add_variable(pdu, err_var, err_var_len,
03957                           ASN_COUNTER, & ltmp, sizeof(ltmp));
03958 
03959     return SNMPERR_SUCCESS;
03960 }                               /* end snmpv3_make_report() */
03961 
03962 
03963 int
03964 snmpv3_get_report_type(netsnmp_pdu *pdu)
03965 {
03966     static oid      snmpMPDStats[] = { 1, 3, 6, 1, 6, 3, 11, 2, 1 };
03967     static oid      targetStats[]  = { 1, 3, 6, 1, 6, 3, 12, 1    };
03968     static oid      usmStats[]     = { 1, 3, 6, 1, 6, 3, 15, 1, 1 };
03969     netsnmp_variable_list *vp;
03970     int             rpt_type = SNMPERR_UNKNOWN_REPORT;
03971 
03972     if (pdu == NULL || pdu->variables == NULL)
03973         return rpt_type;
03974     vp = pdu->variables;
03975     /* MPD or USM based report statistics objects have the same length prefix
03976      *   so the actual statistics OID will have this length,
03977      *   plus one subidentifier for the scalar MIB object itself,
03978      *   and one for the instance subidentifier
03979      */
03980     if (vp->name_length == REPORT_STATS_LEN + 2) {
03981         if (memcmp(snmpMPDStats, vp->name, REPORT_STATS_LEN * sizeof(oid)) == 0) {
03982             switch (vp->name[REPORT_STATS_LEN]) {
03983             case REPORT_snmpUnknownSecurityModels_NUM:
03984                 rpt_type = SNMPERR_UNKNOWN_SEC_MODEL;
03985                 break;
03986             case REPORT_snmpInvalidMsgs_NUM:
03987                 rpt_type = SNMPERR_INVALID_MSG;
03988                 break;
03989             case REPORT_snmpUnknownPDUHandlers_NUM:
03990                 rpt_type = SNMPERR_BAD_VERSION;
03991                 break;
03992             }
03993         } else if (memcmp(usmStats, vp->name, REPORT_STATS_LEN * sizeof(oid)) == 0) {
03994             switch (vp->name[REPORT_STATS_LEN]) {
03995             case REPORT_usmStatsUnsupportedSecLevels_NUM:
03996                 rpt_type = SNMPERR_UNSUPPORTED_SEC_LEVEL;
03997                 break;
03998             case REPORT_usmStatsNotInTimeWindows_NUM:
03999                 rpt_type = SNMPERR_NOT_IN_TIME_WINDOW;
04000                 break;
04001             case REPORT_usmStatsUnknownUserNames_NUM:
04002                 rpt_type = SNMPERR_UNKNOWN_USER_NAME;
04003                 break;
04004             case REPORT_usmStatsUnknownEngineIDs_NUM:
04005                 rpt_type = SNMPERR_UNKNOWN_ENG_ID;
04006                 break;
04007             case REPORT_usmStatsWrongDigests_NUM:
04008                 rpt_type = SNMPERR_AUTHENTICATION_FAILURE;
04009                 break;
04010             case REPORT_usmStatsDecryptionErrors_NUM:
04011                 rpt_type = SNMPERR_DECRYPTION_ERR;
04012                 break;
04013             }
04014         }
04015     }
04016     /* Context-based report statistics from the Target MIB are similar
04017      *   but the OID prefix has a different length
04018      */
04019     if (vp->name_length == REPORT_STATS_LEN2 + 2) {
04020         if (memcmp(targetStats, vp->name, REPORT_STATS_LEN2 * sizeof(oid)) == 0) {
04021             switch (vp->name[REPORT_STATS_LEN2]) {
04022             case REPORT_snmpUnavailableContexts_NUM:
04023                 rpt_type = SNMPERR_BAD_CONTEXT;
04024                 break;
04025             case REPORT_snmpUnknownContexts_NUM:
04026                 rpt_type = SNMPERR_BAD_CONTEXT;
04027                 break;
04028             }
04029         }
04030     }
04031     DEBUGMSGTL(("report", "Report type: %d\n", rpt_type));
04032     return rpt_type;
04033 }
04034 
04035 /*
04036  * Parses the packet received on the input session, and places the data into
04037  * the input pdu.  length is the length of the input packet.
04038  * If any errors are encountered, -1 or USM error is returned.
04039  * Otherwise, a 0 is returned.
04040  */
04041 static int
04042 _snmp_parse(void *sessp,
04043             netsnmp_session * session,
04044             netsnmp_pdu *pdu, u_char * data, size_t length)
04045 {
04046 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
04047     u_char          community[COMMUNITY_MAX_LEN];
04048     size_t          community_length = COMMUNITY_MAX_LEN;
04049 #endif
04050     int             result = -1;
04051 
04052     static oid      snmpEngineIDoid[]   = { 1,3,6,1,6,3,10,2,1,1,0};
04053     static size_t   snmpEngineIDoid_len = 11;
04054 
04055     static char     ourEngineID[SNMP_SEC_PARAM_BUF_SIZE];
04056     static size_t   ourEngineID_len = sizeof(ourEngineID);
04057 
04058     netsnmp_pdu    *pdu2 = NULL;
04059 
04060     session->s_snmp_errno = 0;
04061     session->s_errno = 0;
04062 
04063     /*
04064      * Ensure all incoming PDUs have a unique means of identification 
04065      * (This is not restricted to AgentX handling,
04066      * though that is where the need becomes visible)   
04067      */
04068     pdu->transid = snmp_get_next_transid();
04069 
04070     if (session->version != SNMP_DEFAULT_VERSION) {
04071         pdu->version = session->version;
04072     } else {
04073         pdu->version = snmp_parse_version(data, length);
04074     }
04075 
04076     switch (pdu->version) {
04077 #ifndef NETSNMP_DISABLE_SNMPV1
04078     case SNMP_VERSION_1:
04079 #endif
04080 #ifndef NETSNMP_DISABLE_SNMPV2C
04081     case SNMP_VERSION_2c:
04082 #endif
04083 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
04084         DEBUGMSGTL(("snmp_api", "Parsing SNMPv%ld message...\n",
04085                     (1 + pdu->version)));
04086 
04087         /*
04088          * authenticates message and returns length if valid 
04089          */
04090 #ifndef NETSNMP_DISABLE_SNMPV1
04091         if (pdu->version == SNMP_VERSION_1) {
04092             DEBUGDUMPSECTION("recv", "SNMPv1 message\n");
04093         } else {
04094 #endif
04095             DEBUGDUMPSECTION("recv", "SNMPv2c message\n");
04096 #ifndef NETSNMP_DISABLE_SNMPV1
04097         }
04098 #endif
04099         data = snmp_comstr_parse(data, &length,
04100                                  community, &community_length,
04101                                  &pdu->version);
04102         if (data == NULL)
04103             return -1;
04104 
04105         if (pdu->version != session->version &&
04106             session->version != SNMP_DEFAULT_VERSION) {
04107             session->s_snmp_errno = SNMPERR_BAD_VERSION;
04108             return -1;
04109         }
04110 
04111         /*
04112          * maybe get the community string. 
04113          */
04114         pdu->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
04115         pdu->securityModel = 
04116 #ifndef NETSNMP_DISABLE_SNMPV1
04117             (pdu->version == SNMP_VERSION_1) ? SNMP_SEC_MODEL_SNMPv1 : 
04118 #endif
04119                                                SNMP_SEC_MODEL_SNMPv2c;
04120         SNMP_FREE(pdu->community);
04121         pdu->community_len = 0;
04122         pdu->community = (u_char *) 0;
04123         if (community_length) {
04124             pdu->community_len = community_length;
04125             pdu->community = (u_char *) malloc(community_length);
04126             if (pdu->community == NULL) {
04127                 session->s_snmp_errno = SNMPERR_MALLOC;
04128                 return -1;
04129             }
04130             memmove(pdu->community, community, community_length);
04131         }
04132         if (session->authenticator) {
04133             data = session->authenticator(data, &length,
04134                                           community, community_length);
04135             if (data == NULL) {
04136                 session->s_snmp_errno = SNMPERR_AUTHENTICATION_FAILURE;
04137                 return -1;
04138             }
04139         }
04140 
04141         DEBUGDUMPSECTION("recv", "PDU");
04142         result = snmp_pdu_parse(pdu, data, &length);
04143         if (result < 0) {
04144             /*
04145              * This indicates a parse error.  
04146              */
04147             snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
04148         }
04149         DEBUGINDENTADD(-6);
04150         break;
04151 #endif /* support for community based SNMP */
04152 
04153     case SNMP_VERSION_3:
04154         result = snmpv3_parse(pdu, data, &length, NULL, session);
04155         DEBUGMSGTL(("snmp_parse",
04156                     "Parsed SNMPv3 message (secName:%s, secLevel:%s): %s\n",
04157                     pdu->securityName, secLevelName[pdu->securityLevel],
04158                     snmp_api_errstring(result)));
04159 
04160         if (result) {
04161             struct snmp_secmod_def *secmod =
04162                 find_sec_mod(pdu->securityModel);
04163             if (!sessp) {
04164                 session->s_snmp_errno = result;
04165             } else {
04166                 /*
04167                  * Call the security model to special handle any errors
04168                  */
04169 
04170                 if (secmod && secmod->handle_report) {
04171                     struct session_list *slp = (struct session_list *) sessp;
04172                     (*secmod->handle_report)(sessp, slp->transport, session,
04173                                              result, pdu);
04174                 }
04175             }
04176             if (pdu->securityStateRef != NULL) {
04177                 if (secmod && secmod->pdu_free_state_ref) {
04178                     secmod->pdu_free_state_ref(pdu->securityStateRef);
04179                     pdu->securityStateRef = NULL;
04180                 }
04181             }
04182         }
04183 
04184         /* Implement RFC5343 here for two reasons:
04185            1) From a security perspective it handles this otherwise
04186               always approved request earlier.  It bypasses the need
04187               for authorization to the snmpEngineID scalar, which is
04188               what is what RFC3415 appendix A species as ok.  Note
04189               that we haven't bypassed authentication since if there
04190               was an authentication eror it would have been handled
04191               above in the if(result) part at the lastet.
04192            2) From an application point of view if we let this request
04193               get all the way to the application, it'd require that
04194               all application types supporting discovery also fire up
04195               a minimal agent in order to handle just this request
04196               which seems like overkill.  Though there is no other
04197               application types that currently need discovery (NRs
04198               accept notifications from contextEngineIDs that derive
04199               from the NO not the NR).  Also a lame excuse for doing
04200               it here.
04201            3) Less important technically, but the net-snmp agent
04202               doesn't currently handle registrations of different
04203               engineIDs either and it would have been a lot more work
04204               to implement there since we'd need to support that
04205               first. :-/ Supporting multiple context engineIDs should
04206               be done anyway, so it's not a valid excuse here.
04207            4) There is a lot less to do if we trump the agent at this
04208               point; IE, the agent does a lot more unnecessary
04209               processing when the only thing that should ever be in
04210               this context by definition is the single scalar.
04211         */
04212 
04213         /* special RFC5343 engineID discovery engineID check */
04214         if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
04215                                     NETSNMP_DS_LIB_NO_DISCOVERY) &&
04216             SNMP_MSG_RESPONSE       != pdu->command &&
04217             NULL                    != pdu->contextEngineID &&
04218             pdu->contextEngineIDLen == 5 &&
04219             pdu->contextEngineID[0] == 0x80 &&
04220             pdu->contextEngineID[1] == 0x00 &&
04221             pdu->contextEngineID[2] == 0x00 &&
04222             pdu->contextEngineID[3] == 0x00 &&
04223             pdu->contextEngineID[4] == 0x06) {
04224 
04225             /* define a result so it doesn't get past us at this point
04226                and gets dropped by future parts of the stack */
04227             result = SNMPERR_JUST_A_CONTEXT_PROBE;
04228 
04229             DEBUGMSGTL(("snmpv3_contextid", "starting context ID discovery\n"));
04230             /* ensure exactly one variable */
04231             if (NULL != pdu->variables &&
04232                 NULL == pdu->variables->next_variable &&
04233 
04234                 /* if it's a GET, match it exactly */
04235                 ((SNMP_MSG_GET == pdu->command &&
04236                   snmp_oid_compare(snmpEngineIDoid,
04237                                    snmpEngineIDoid_len,
04238                                    pdu->variables->name,
04239                                    pdu->variables->name_length) == 0)
04240                  /* if it's a GETNEXT ensure it's less than the engineID oid */
04241                  ||
04242                  (SNMP_MSG_GETNEXT == pdu->command &&
04243                   snmp_oid_compare(snmpEngineIDoid,
04244                                    snmpEngineIDoid_len,
04245                                    pdu->variables->name,
04246                                    pdu->variables->name_length) > 0)
04247                     )) {
04248 
04249                 DEBUGMSGTL(("snmpv3_contextid",
04250                             "  One correct variable found\n"));
04251 
04252                 /* Note: we're explictly not handling a GETBULK.  Deal. */
04253 
04254                 /* set up the response */
04255                 pdu2 = snmp_clone_pdu(pdu);
04256 
04257                 /* free the current varbind */
04258                 snmp_free_varbind(pdu2->variables);
04259 
04260                 /* set the variables */
04261                 pdu2->variables = NULL;
04262                 pdu2->command = SNMP_MSG_RESPONSE;
04263                 pdu2->errstat = 0;
04264                 pdu2->errindex = 0;
04265 
04266                 ourEngineID_len =
04267                     snmpv3_get_engineID((u_char*)ourEngineID, ourEngineID_len);
04268                 if (0 != ourEngineID_len) {
04269 
04270                     DEBUGMSGTL(("snmpv3_contextid",
04271                                 "  responding with our engineID\n"));
04272 
04273                     snmp_pdu_add_variable(pdu2,
04274                                           snmpEngineIDoid, snmpEngineIDoid_len,
04275                                           ASN_OCTET_STR,
04276                                           ourEngineID, ourEngineID_len);
04277                     
04278                     /* send the response */
04279                     if (0 == snmp_sess_send(sessp, pdu2)) {
04280 
04281                         DEBUGMSGTL(("snmpv3_contextid",
04282                                     "  sent it off!\n"));
04283 
04284                         snmp_free_pdu(pdu2);
04285                         
04286                         snmp_log(LOG_ERR, "sending a response to the context engineID probe failed\n");
04287                     }
04288                 } else {
04289                     snmp_log(LOG_ERR, "failed to get our own engineID!\n");
04290                 }
04291             } else {
04292                 snmp_log(LOG_WARNING,
04293                          "received an odd context engineID probe\n");
04294             }
04295         }
04296 
04297         break;
04298     case SNMPERR_BAD_VERSION:
04299         ERROR_MSG("error parsing snmp message version");
04300         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
04301         session->s_snmp_errno = SNMPERR_BAD_VERSION;
04302         break;
04303     case SNMP_VERSION_sec:
04304     case SNMP_VERSION_2u:
04305     case SNMP_VERSION_2star:
04306     case SNMP_VERSION_2p:
04307     default:
04308         ERROR_MSG("unsupported snmp message version");
04309         snmp_increment_statistic(STAT_SNMPINBADVERSIONS);
04310 
04311         /*
04312          * need better way to determine OS independent
04313          * INT32_MAX value, for now hardcode
04314          */
04315         if (pdu->version < 0 || pdu->version > 2147483647) {
04316             snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
04317         }
04318         session->s_snmp_errno = SNMPERR_BAD_VERSION;
04319         break;
04320     }
04321 
04322     return result;
04323 }
04324 
04325 static int
04326 snmp_parse(void *sessp,
04327            netsnmp_session * pss,
04328            netsnmp_pdu *pdu, u_char * data, size_t length)
04329 {
04330     int             rc;
04331 
04332     rc = _snmp_parse(sessp, pss, pdu, data, length);
04333     if (rc) {
04334         if (!pss->s_snmp_errno) {
04335             pss->s_snmp_errno = SNMPERR_BAD_PARSE;
04336         }
04337         SET_SNMP_ERROR(pss->s_snmp_errno);
04338     }
04339 
04340     return rc;
04341 }
04342 
04343 int
04344 snmp_pdu_parse(netsnmp_pdu *pdu, u_char * data, size_t * length)
04345 {
04346     u_char          type;
04347     u_char          msg_type;
04348     u_char         *var_val;
04349     int             badtype = 0;
04350     size_t          len;
04351     size_t          four;
04352     netsnmp_variable_list *vp = NULL;
04353     oid             objid[MAX_OID_LEN];
04354 
04355     /*
04356      * Get the PDU type 
04357      */
04358     data = asn_parse_header(data, length, &msg_type);
04359     if (data == NULL)
04360         return -1;
04361     DEBUGMSGTL(("dumpv_recv","    Command %s\n", snmp_pdu_type(msg_type)));
04362     pdu->command = msg_type;
04363     pdu->flags &= (~UCD_MSG_FLAG_RESPONSE_PDU);
04364 
04365     /*
04366      * get the fields in the PDU preceeding the variable-bindings sequence 
04367      */
04368     switch (pdu->command) {
04369     case SNMP_MSG_TRAP:
04370         /*
04371          * enterprise 
04372          */
04373         pdu->enterprise_length = MAX_OID_LEN;
04374         data = asn_parse_objid(data, length, &type, objid,
04375                                &pdu->enterprise_length);
04376         if (data == NULL)
04377             return -1;
04378         pdu->enterprise =
04379             (oid *) malloc(pdu->enterprise_length * sizeof(oid));
04380         if (pdu->enterprise == NULL) {
04381             return -1;
04382         }
04383         memmove(pdu->enterprise, objid,
04384                 pdu->enterprise_length * sizeof(oid));
04385 
04386         /*
04387          * agent-addr 
04388          */
04389         four = 4;
04390         data = asn_parse_string(data, length, &type,
04391                                 (u_char *) pdu->agent_addr, &four);
04392         if (data == NULL)
04393             return -1;
04394 
04395         /*
04396          * generic trap 
04397          */
04398         data = asn_parse_int(data, length, &type, (long *) &pdu->trap_type,
04399                              sizeof(pdu->trap_type));
04400         if (data == NULL)
04401             return -1;
04402         /*
04403          * specific trap 
04404          */
04405         data =
04406             asn_parse_int(data, length, &type,
04407                           (long *) &pdu->specific_type,
04408                           sizeof(pdu->specific_type));
04409         if (data == NULL)
04410             return -1;
04411 
04412         /*
04413          * timestamp  
04414          */
04415         data = asn_parse_unsigned_int(data, length, &type, &pdu->time,
04416                                       sizeof(pdu->time));
04417         if (data == NULL)
04418             return -1;
04419 
04420         break;
04421 
04422     case SNMP_MSG_RESPONSE:
04423     case SNMP_MSG_REPORT:
04424         pdu->flags |= UCD_MSG_FLAG_RESPONSE_PDU;
04425         /*
04426          * fallthrough 
04427          */
04428 
04429 #ifndef NETSNMP_NOTIFY_ONLY
04430     case SNMP_MSG_GET:
04431     case SNMP_MSG_GETNEXT:
04432     case SNMP_MSG_GETBULK:
04433 #endif /* ! NETSNMP_NOTIFY_ONLY */
04434 #ifndef NETSNMP_NO_WRITE_SUPPORT
04435     case SNMP_MSG_SET:
04436 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
04437     case SNMP_MSG_TRAP2:
04438     case SNMP_MSG_INFORM:
04439         /*
04440          * PDU is not an SNMPv1 TRAP 
04441          */
04442 
04443         /*
04444          * request id 
04445          */
04446         DEBUGDUMPHEADER("recv", "request_id");
04447         data = asn_parse_int(data, length, &type, &pdu->reqid,
04448                              sizeof(pdu->reqid));
04449         DEBUGINDENTLESS();
04450         if (data == NULL) {
04451             return -1;
04452         }
04453 
04454         /*
04455          * error status (getbulk non-repeaters) 
04456          */
04457         DEBUGDUMPHEADER("recv", "error status");
04458         data = asn_parse_int(data, length, &type, &pdu->errstat,
04459                              sizeof(pdu->errstat));
04460         DEBUGINDENTLESS();
04461         if (data == NULL) {
04462             return -1;
04463         }
04464 
04465         /*
04466          * error index (getbulk max-repetitions) 
04467          */
04468         DEBUGDUMPHEADER("recv", "error index");
04469         data = asn_parse_int(data, length, &type, &pdu->errindex,
04470                              sizeof(pdu->errindex));
04471         DEBUGINDENTLESS();
04472         if (data == NULL) {
04473             return -1;
04474         }
04475         break;
04476 
04477     default:
04478         snmp_log(LOG_ERR, "Bad PDU type received: 0x%.2x\n", pdu->command);
04479         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
04480         return -1;
04481     }
04482 
04483     /*
04484      * get header for variable-bindings sequence 
04485      */
04486     DEBUGDUMPSECTION("recv", "VarBindList");
04487     data = asn_parse_sequence(data, length, &type,
04488                               (ASN_SEQUENCE | ASN_CONSTRUCTOR),
04489                               "varbinds");
04490     if (data == NULL)
04491         return -1;
04492 
04493     /*
04494      * get each varBind sequence 
04495      */
04496     while ((int) *length > 0) {
04497         netsnmp_variable_list *vptemp;
04498         vptemp = (netsnmp_variable_list *) malloc(sizeof(*vptemp));
04499         if (NULL == vptemp) {
04500             return -1;
04501         }
04502         if (NULL == vp) {
04503             pdu->variables = vptemp;
04504         } else {
04505             vp->next_variable = vptemp;
04506         }
04507         vp = vptemp;
04508 
04509         vp->next_variable = NULL;
04510         vp->val.string = NULL;
04511         vp->name_length = MAX_OID_LEN;
04512         vp->name = NULL;
04513         vp->index = 0;
04514         vp->data = NULL;
04515         vp->dataFreeHook = NULL;
04516         DEBUGDUMPSECTION("recv", "VarBind");
04517         data = snmp_parse_var_op(data, objid, &vp->name_length, &vp->type,
04518                                  &vp->val_len, &var_val, length);
04519         if (data == NULL)
04520             return -1;
04521         if (snmp_set_var_objid(vp, objid, vp->name_length))
04522             return -1;
04523 
04524         len = MAX_PACKET_LENGTH;
04525         DEBUGDUMPHEADER("recv", "Value");
04526         switch ((short) vp->type) {
04527         case ASN_INTEGER:
04528             vp->val.integer = (long *) vp->buf;
04529             vp->val_len = sizeof(long);
04530             asn_parse_int(var_val, &len, &vp->type,
04531                           (long *) vp->val.integer,
04532                           sizeof(*vp->val.integer));
04533             break;
04534         case ASN_COUNTER:
04535         case ASN_GAUGE:
04536         case ASN_TIMETICKS:
04537         case ASN_UINTEGER:
04538             vp->val.integer = (long *) vp->buf;
04539             vp->val_len = sizeof(u_long);
04540             asn_parse_unsigned_int(var_val, &len, &vp->type,
04541                                    (u_long *) vp->val.integer,
04542                                    vp->val_len);
04543             break;
04544 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
04545         case ASN_OPAQUE_COUNTER64:
04546         case ASN_OPAQUE_U64:
04547 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
04548         case ASN_COUNTER64:
04549             vp->val.counter64 = (struct counter64 *) vp->buf;
04550             vp->val_len = sizeof(struct counter64);
04551             asn_parse_unsigned_int64(var_val, &len, &vp->type,
04552                                      (struct counter64 *) vp->val.
04553                                      counter64, vp->val_len);
04554             break;
04555 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
04556         case ASN_OPAQUE_FLOAT:
04557             vp->val.floatVal = (float *) vp->buf;
04558             vp->val_len = sizeof(float);
04559             asn_parse_float(var_val, &len, &vp->type,
04560                             vp->val.floatVal, vp->val_len);
04561             break;
04562         case ASN_OPAQUE_DOUBLE:
04563             vp->val.doubleVal = (double *) vp->buf;
04564             vp->val_len = sizeof(double);
04565             asn_parse_double(var_val, &len, &vp->type,
04566                              vp->val.doubleVal, vp->val_len);
04567             break;
04568         case ASN_OPAQUE_I64:
04569             vp->val.counter64 = (struct counter64 *) vp->buf;
04570             vp->val_len = sizeof(struct counter64);
04571             asn_parse_signed_int64(var_val, &len, &vp->type,
04572                                    (struct counter64 *) vp->val.counter64,
04573                                    sizeof(*vp->val.counter64));
04574 
04575             break;
04576 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
04577         case ASN_OCTET_STR:
04578         case ASN_IPADDRESS:
04579         case ASN_OPAQUE:
04580         case ASN_NSAP:
04581             if (vp->val_len < sizeof(vp->buf)) {
04582                 vp->val.string = (u_char *) vp->buf;
04583             } else {
04584                 vp->val.string = (u_char *) malloc(vp->val_len);
04585             }
04586             if (vp->val.string == NULL) {
04587                 return -1;
04588             }
04589             asn_parse_string(var_val, &len, &vp->type, vp->val.string,
04590                              &vp->val_len);
04591             break;
04592         case ASN_OBJECT_ID:
04593             vp->val_len = MAX_OID_LEN;
04594             asn_parse_objid(var_val, &len, &vp->type, objid, &vp->val_len);
04595             vp->val_len *= sizeof(oid);
04596             vp->val.objid = (oid *) malloc(vp->val_len);
04597             if (vp->val.objid == NULL) {
04598                 return -1;
04599             }
04600             memmove(vp->val.objid, objid, vp->val_len);
04601             break;
04602         case SNMP_NOSUCHOBJECT:
04603         case SNMP_NOSUCHINSTANCE:
04604         case SNMP_ENDOFMIBVIEW:
04605         case ASN_NULL:
04606             break;
04607         case ASN_BIT_STR:
04608             vp->val.bitstring = (u_char *) malloc(vp->val_len);
04609             if (vp->val.bitstring == NULL) {
04610                 return -1;
04611             }
04612             asn_parse_bitstring(var_val, &len, &vp->type,
04613                                 vp->val.bitstring, &vp->val_len);
04614             break;
04615         default:
04616             snmp_log(LOG_ERR, "bad type returned (%x)\n", vp->type);
04617             badtype = -1;
04618             break;
04619         }
04620         DEBUGINDENTADD(-4);
04621     }
04622     return badtype;
04623 }
04624 
04625 /*
04626  * snmp v3 utility function to parse into the scopedPdu. stores contextName
04627  * and contextEngineID in pdu struct. Also stores pdu->command (handy for 
04628  * Report generation).
04629  * 
04630  * returns pointer to begining of PDU or NULL on error.
04631  */
04632 u_char         *
04633 snmpv3_scopedPDU_parse(netsnmp_pdu *pdu, u_char * cp, size_t * length)
04634 {
04635     u_char          tmp_buf[SNMP_MAX_MSG_SIZE];
04636     size_t          tmp_buf_len;
04637     u_char          type;
04638     size_t          asn_len;
04639     u_char         *data;
04640 
04641     pdu->command = 0;           /* initialize so we know if it got parsed */
04642     asn_len = *length;
04643     data = asn_parse_sequence(cp, &asn_len, &type,
04644                               (ASN_SEQUENCE | ASN_CONSTRUCTOR),
04645                               "plaintext scopedPDU");
04646     if (data == NULL) {
04647         return NULL;
04648     }
04649     *length -= data - cp;
04650 
04651     /*
04652      * contextEngineID from scopedPdu  
04653      */
04654     DEBUGDUMPHEADER("recv", "contextEngineID");
04655     data = asn_parse_string(data, length, &type, pdu->contextEngineID,
04656                             &pdu->contextEngineIDLen);
04657     DEBUGINDENTLESS();
04658     if (data == NULL) {
04659         ERROR_MSG("error parsing contextEngineID from scopedPdu");
04660         return NULL;
04661     }
04662 
04663     /*
04664      * parse contextName from scopedPdu
04665      */
04666     tmp_buf_len = SNMP_MAX_CONTEXT_SIZE;
04667     DEBUGDUMPHEADER("recv", "contextName");
04668     data = asn_parse_string(data, length, &type, tmp_buf, &tmp_buf_len);
04669     DEBUGINDENTLESS();
04670     if (data == NULL) {
04671         ERROR_MSG("error parsing contextName from scopedPdu");
04672         return NULL;
04673     }
04674 
04675     if (tmp_buf_len) {
04676         pdu->contextName = (char *) malloc(tmp_buf_len);
04677         memmove(pdu->contextName, tmp_buf, tmp_buf_len);
04678         pdu->contextNameLen = tmp_buf_len;
04679     } else {
04680         pdu->contextName = strdup("");
04681         pdu->contextNameLen = 0;
04682     }
04683     if (pdu->contextName == NULL) {
04684         ERROR_MSG("error copying contextName from scopedPdu");
04685         return NULL;
04686     }
04687 
04688     /*
04689      * Get the PDU type 
04690      */
04691     asn_len = *length;
04692     cp = asn_parse_header(data, &asn_len, &type);
04693     if (cp == NULL)
04694         return NULL;
04695 
04696     pdu->command = type;
04697 
04698     return data;
04699 }
04700 
04701 /*
04702  * These functions send PDUs using an active session:
04703  * snmp_send             - traditional API, no callback
04704  * snmp_async_send       - traditional API, with callback
04705  * snmp_sess_send        - single session API, no callback
04706  * snmp_sess_async_send  - single session API, with callback
04707  *
04708  * Call snmp_build to create a serialized packet (the pdu).
04709  * If necessary, set some of the pdu data from the
04710  * session defaults.
04711  * If there is an expected response for this PDU,
04712  * queue a corresponding request on the list
04713  * of outstanding requests for this session,
04714  * and store the callback vectors in the request.
04715  *
04716  * Send the pdu to the target identified by this session.
04717  * Return on success:
04718  *   The request id of the pdu is returned, and the pdu is freed.
04719  * Return on failure:
04720  *   Zero (0) is returned.
04721  *   The caller must call snmp_free_pdu if 0 is returned.
04722  */
04723 int
04724 snmp_send(netsnmp_session * session, netsnmp_pdu *pdu)
04725 {
04726     return snmp_async_send(session, pdu, NULL, NULL);
04727 }
04728 
04729 int
04730 snmp_sess_send(void *sessp, netsnmp_pdu *pdu)
04731 {
04732     return snmp_sess_async_send(sessp, pdu, NULL, NULL);
04733 }
04734 
04735 int
04736 snmp_async_send(netsnmp_session * session,
04737                 netsnmp_pdu *pdu, snmp_callback callback, void *cb_data)
04738 {
04739     void           *sessp = snmp_sess_pointer(session);
04740     return snmp_sess_async_send(sessp, pdu, callback, cb_data);
04741 }
04742 
04743 static int
04744 _sess_async_send(void *sessp,
04745                  netsnmp_pdu *pdu, snmp_callback callback, void *cb_data)
04746 {
04747     struct session_list *slp = (struct session_list *) sessp;
04748     netsnmp_session *session;
04749     struct snmp_internal_session *isp;
04750     netsnmp_transport *transport = NULL;
04751     u_char         *pktbuf = NULL, *packet = NULL;
04752     size_t          pktbuf_len = 0, offset = 0, length = 0;
04753     int             result;
04754     long            reqid;
04755 
04756     if (slp == NULL) {
04757         return 0;
04758     } else {
04759         session = slp->session;
04760         isp = slp->internal;
04761         transport = slp->transport;
04762         if (!session || !isp || !transport) {
04763             DEBUGMSGTL(("sess_async_send", "send fail: closing...\n"));
04764             return 0;
04765         }
04766     }
04767 
04768     if (pdu == NULL) {
04769         session->s_snmp_errno = SNMPERR_NULL_PDU;
04770         return 0;
04771     }
04772 
04773     session->s_snmp_errno = 0;
04774     session->s_errno = 0;
04775 
04776     /*
04777      * Check/setup the version.  
04778      */
04779     if (pdu->version == SNMP_DEFAULT_VERSION) {
04780         if (session->version == SNMP_DEFAULT_VERSION) {
04781             session->s_snmp_errno = SNMPERR_BAD_VERSION;
04782             return 0;
04783         }
04784         pdu->version = session->version;
04785     } else if (session->version == SNMP_DEFAULT_VERSION) {
04786         /*
04787          * It's OK  
04788          */
04789     } else if (pdu->version != session->version) {
04790         /*
04791          * ENHANCE: we should support multi-lingual sessions  
04792          */
04793         session->s_snmp_errno = SNMPERR_BAD_VERSION;
04794         return 0;
04795     }
04796 
04797     /*
04798      * do we expect a response?
04799      */
04800     switch (pdu->command) {
04801 
04802         case SNMP_MSG_RESPONSE:
04803         case SNMP_MSG_TRAP:
04804         case SNMP_MSG_TRAP2:
04805         case SNMP_MSG_REPORT:
04806         case AGENTX_MSG_CLEANUPSET:
04807         case AGENTX_MSG_RESPONSE:
04808             pdu->flags &= ~UCD_MSG_FLAG_EXPECT_RESPONSE;
04809             break;
04810             
04811         default:
04812             pdu->flags |= UCD_MSG_FLAG_EXPECT_RESPONSE;
04813             break;
04814     }
04815 
04816     /*
04817      * check to see if we need a v3 engineID probe
04818      */
04819     if ((pdu->version == SNMP_VERSION_3) &&
04820         (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE) &&
04821         (session->securityEngineIDLen == 0) &&
04822         (0 == (session->flags & SNMP_FLAGS_DONT_PROBE))) {
04823         int rc;
04824         DEBUGMSGTL(("snmpv3_build", "delayed probe for engineID\n"));
04825         rc = snmpv3_engineID_probe(slp, session);
04826         if (rc == 0)
04827             return 0; /* s_snmp_errno already set */
04828     }
04829 
04830     if ((pktbuf = (u_char *)malloc(2048)) == NULL) {
04831         DEBUGMSGTL(("sess_async_send",
04832                     "couldn't malloc initial packet buffer\n"));
04833         session->s_snmp_errno = SNMPERR_MALLOC;
04834         return 0;
04835     } else {
04836         pktbuf_len = 2048;
04837     }
04838 
04839 #if TEMPORARILY_DISABLED
04840     /*
04841      *  NULL variable are allowed in certain PDU types.
04842      *  In particular, SNMPv3 engineID probes are of this form.
04843      *  There is an internal PDU flag to indicate that this
04844      *    is acceptable, but until the construction of engineID
04845      *    probes can be amended to set this flag, we'll simply
04846      *    skip this test altogether.
04847      */
04848     if (pdu->variables == NULL) {
04849         switch (pdu->command) {
04850 #ifndef NETSNMP_NO_WRITE_SUPPORT
04851         case SNMP_MSG_SET:
04852 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
04853         case SNMP_MSG_GET:
04854         case SNMP_MSG_GETNEXT:
04855         case SNMP_MSG_GETBULK:
04856         case SNMP_MSG_RESPONSE:
04857         case SNMP_MSG_TRAP2:
04858         case SNMP_MSG_REPORT:
04859         case SNMP_MSG_INFORM:
04860             session->s_snmp_errno = snmp_errno = SNMPERR_NO_VARS;
04861             return 0;
04862         case SNMP_MSG_TRAP:
04863             break;
04864         }
04865     }
04866 #endif
04867 
04868 
04869     /*
04870      * Build the message to send.  
04871      */
04872     if (isp->hook_realloc_build) {
04873         result = isp->hook_realloc_build(session, pdu,
04874                                          &pktbuf, &pktbuf_len, &offset);
04875         packet = pktbuf;
04876         length = offset;
04877     } else if (isp->hook_build) {
04878         packet = pktbuf;
04879         length = pktbuf_len;
04880         result = isp->hook_build(session, pdu, pktbuf, &length);
04881     } else {
04882 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
04883         if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE)) {
04884             result =
04885                 snmp_build(&pktbuf, &pktbuf_len, &offset, session, pdu);
04886             packet = pktbuf + pktbuf_len - offset;
04887             length = offset;
04888         } else {
04889 #endif
04890             packet = pktbuf;
04891             length = pktbuf_len;
04892             result = snmp_build(&pktbuf, &length, &offset, session, pdu);
04893 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
04894         }
04895 #endif
04896     }
04897 
04898     if (result < 0) {
04899         DEBUGMSGTL(("sess_async_send", "encoding failure\n"));
04900         SNMP_FREE(pktbuf);
04901         return 0;
04902     }
04903 
04904     /*
04905      * Make sure we don't send something that is bigger than the msgMaxSize
04906      * specified in the received PDU.  
04907      */
04908 
04909     if (session->sndMsgMaxSize != 0 && length > session->sndMsgMaxSize) {
04910         DEBUGMSGTL(("sess_async_send",
04911                     "length of packet (%lu) exceeds session maximum (%lu)\n",
04912                     (unsigned long)length, (unsigned long)session->sndMsgMaxSize));
04913         session->s_snmp_errno = SNMPERR_TOO_LONG;
04914         SNMP_FREE(pktbuf);
04915         return 0;
04916     }
04917 
04918     /*
04919      * Check that the underlying transport is capable of sending a packet as
04920      * large as length.  
04921      */
04922 
04923     if (transport->msgMaxSize != 0 && length > transport->msgMaxSize) {
04924         DEBUGMSGTL(("sess_async_send",
04925                     "length of packet (%lu) exceeds transport maximum (%lu)\n",
04926                     (unsigned long)length, (unsigned long)transport->msgMaxSize));
04927         session->s_snmp_errno = SNMPERR_TOO_LONG;
04928         SNMP_FREE(pktbuf);
04929         return 0;
04930     }
04931 
04932     /*
04933      * Send the message.  
04934      */
04935 
04936     DEBUGMSGTL(("sess_process_packet", "sending message id#%ld reqid#%ld len %"
04937                 NETSNMP_PRIz "u\n", pdu->msgid, pdu->reqid, length));
04938     result = netsnmp_transport_send(transport, packet, length,
04939                                     &(pdu->transport_data),
04940                                     &(pdu->transport_data_length));
04941 
04942     SNMP_FREE(pktbuf);
04943 
04944     if (result < 0) {
04945         session->s_snmp_errno = SNMPERR_BAD_SENDTO;
04946         session->s_errno = errno;
04947         return 0;
04948     }
04949 
04950     reqid = pdu->reqid;
04951 
04952     /*
04953      * Add to pending requests list if we expect a response.  
04954      */
04955     if (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE) {
04956         netsnmp_request_list *rp;
04957         struct timeval  tv;
04958 
04959         rp = (netsnmp_request_list *) calloc(1,
04960                                              sizeof(netsnmp_request_list));
04961         if (rp == NULL) {
04962             session->s_snmp_errno = SNMPERR_GENERR;
04963             return 0;
04964         }
04965 
04966         gettimeofday(&tv, (struct timezone *) 0);
04967         rp->pdu = pdu;
04968         rp->request_id = pdu->reqid;
04969         rp->message_id = pdu->msgid;
04970         rp->callback = callback;
04971         rp->cb_data = cb_data;
04972         rp->retries = 0;
04973         if (pdu->flags & UCD_MSG_FLAG_PDU_TIMEOUT) {
04974             rp->timeout = pdu->time * 1000000L;
04975         } else {
04976             rp->timeout = session->timeout;
04977         }
04978         rp->time = tv;
04979         tv.tv_usec += rp->timeout;
04980         tv.tv_sec += tv.tv_usec / 1000000L;
04981         tv.tv_usec %= 1000000L;
04982         rp->expire = tv;
04983 
04984         /*
04985          * XX lock should be per session ! 
04986          */
04987         snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
04988         if (isp->requestsEnd) {
04989             rp->next_request = isp->requestsEnd->next_request;
04990             isp->requestsEnd->next_request = rp;
04991             isp->requestsEnd = rp;
04992         } else {
04993             rp->next_request = isp->requests;
04994             isp->requests = rp;
04995             isp->requestsEnd = rp;
04996         }
04997         snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
04998     } else {
04999         /*
05000          * No response expected...  
05001          */
05002         if (reqid) {
05003             /*
05004              * Free v1 or v2 TRAP PDU iff no error  
05005              */
05006             snmp_free_pdu(pdu);
05007         }
05008     }
05009 
05010     return reqid;
05011 }
05012 
05013 int
05014 snmp_sess_async_send(void *sessp,
05015                      netsnmp_pdu *pdu,
05016                      snmp_callback callback, void *cb_data)
05017 {
05018     int             rc;
05019 
05020     if (sessp == NULL) {
05021         snmp_errno = SNMPERR_BAD_SESSION;       /*MTCRITICAL_RESOURCE */
05022         return (0);
05023     }
05024     /*
05025      * send pdu
05026      */
05027     rc = _sess_async_send(sessp, pdu, callback, cb_data);
05028     if (rc == 0) {
05029         struct session_list *psl;
05030         netsnmp_session *pss;
05031         psl = (struct session_list *) sessp;
05032         pss = psl->session;
05033         SET_SNMP_ERROR(pss->s_snmp_errno);
05034     }
05035     return rc;
05036 }
05037 
05038 
05039 /*
05040  * Frees the variable and any malloc'd data associated with it.
05041  */
05042 void
05043 snmp_free_var_internals(netsnmp_variable_list * var)
05044 {
05045     if (!var)
05046         return;
05047 
05048     if (var->name != var->name_loc)
05049         SNMP_FREE(var->name);
05050     if (var->val.string != var->buf)
05051         SNMP_FREE(var->val.string);
05052     if (var->data) {
05053         if (var->dataFreeHook) {
05054             var->dataFreeHook(var->data);
05055             var->data = NULL;
05056         } else {
05057             SNMP_FREE(var->data);
05058         }
05059     }
05060 }
05061 
05062 void
05063 snmp_free_var(netsnmp_variable_list * var)
05064 {
05065     snmp_free_var_internals(var);
05066     free((char *) var);
05067 }
05068 
05069 void
05070 snmp_free_varbind(netsnmp_variable_list * var)
05071 {
05072     netsnmp_variable_list *ptr;
05073     while (var) {
05074         ptr = var->next_variable;
05075         snmp_free_var(var);
05076         var = ptr;
05077     }
05078 }
05079 
05080 /*
05081  * Frees the pdu and any malloc'd data associated with it.
05082  */
05083 void
05084 snmp_free_pdu(netsnmp_pdu *pdu)
05085 {
05086     struct snmp_secmod_def *sptr;
05087 
05088     if (!pdu)
05089         return;
05090 
05091     /*
05092      * If the command field is empty, that probably indicates
05093      *   that this PDU structure has already been freed.
05094      *   Log a warning and return (rather than freeing things again)
05095      *
05096      * Note that this does not pick up dual-frees where the
05097      *   memory is set to random junk, which is probably more serious.
05098      *
05099      * rks: while this is a good idea, there are two problems.
05100      *         1) agentx sets command to 0 in some cases
05101      *         2) according to Wes, a bad decode of a v3 message could
05102      *            result in a 0 at this offset.
05103      *      so I'm commenting it out until a better solution is found.
05104      *      note that I'm leaving the memset, below....
05105      *
05106     if (pdu->command == 0) {
05107         snmp_log(LOG_WARNING, "snmp_free_pdu probably called twice\n");
05108         return;
05109     }
05110      */
05111     if ((sptr = find_sec_mod(pdu->securityModel)) != NULL &&
05112         sptr->pdu_free != NULL) {
05113         (*sptr->pdu_free) (pdu);
05114     }
05115     snmp_free_varbind(pdu->variables);
05116     SNMP_FREE(pdu->enterprise);
05117     SNMP_FREE(pdu->community);
05118     SNMP_FREE(pdu->contextEngineID);
05119     SNMP_FREE(pdu->securityEngineID);
05120     SNMP_FREE(pdu->contextName);
05121     SNMP_FREE(pdu->securityName);
05122     SNMP_FREE(pdu->transport_data);
05123     memset(pdu, 0, sizeof(netsnmp_pdu));
05124     free((char *) pdu);
05125 }
05126 
05127 netsnmp_pdu    *
05128 snmp_create_sess_pdu(netsnmp_transport *transport, void *opaque,
05129                      size_t olength)
05130 {
05131     netsnmp_pdu *pdu = (netsnmp_pdu *)calloc(1, sizeof(netsnmp_pdu));
05132     if (pdu == NULL) {
05133         DEBUGMSGTL(("sess_process_packet", "can't malloc space for PDU\n"));
05134         return NULL;
05135     }
05136 
05137     /*
05138      * Save the transport-level data specific to this reception (e.g. UDP
05139      * source address).  
05140      */
05141 
05142     pdu->transport_data = opaque;
05143     pdu->transport_data_length = olength;
05144     pdu->tDomain = transport->domain;
05145     pdu->tDomainLen = transport->domain_length;
05146     return pdu;
05147 }
05148 
05149 
05150 /*
05151  * This function processes a complete (according to asn_check_packet or the
05152  * AgentX equivalent) packet, parsing it into a PDU and calling the relevant
05153  * callbacks.  On entry, packetptr points at the packet in the session's
05154  * buffer and length is the length of the packet.  
05155  */
05156 
05157 static int
05158 _sess_process_packet(void *sessp, netsnmp_session * sp,
05159                      struct snmp_internal_session *isp,
05160                      netsnmp_transport *transport,
05161                      void *opaque, int olength,
05162                      u_char * packetptr, int length)
05163 {
05164   struct session_list *slp = (struct session_list *) sessp;
05165   netsnmp_pdu    *pdu;
05166   netsnmp_request_list *rp, *orp = NULL;
05167   struct snmp_secmod_def *sptr;
05168   int             ret = 0, handled = 0;
05169 
05170   DEBUGMSGTL(("sess_process_packet",
05171               "session %p fd %d pkt %p length %d\n", sessp,
05172               transport->sock, packetptr, length));
05173 
05174   if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,NETSNMP_DS_LIB_DUMP_PACKET)) {
05175       char *addrtxt = netsnmp_transport_peer_string(transport, opaque, olength);
05176       snmp_log(LOG_DEBUG, "\nReceived %d byte packet from %s\n",
05177                length, addrtxt);
05178       SNMP_FREE(addrtxt);
05179       xdump(packetptr, length, "");
05180   }
05181 
05182   /*
05183    * Do transport-level filtering (e.g. IP-address based allow/deny).  
05184    */
05185 
05186   if (isp->hook_pre) {
05187     if (isp->hook_pre(sp, transport, opaque, olength) == 0) {
05188       DEBUGMSGTL(("sess_process_packet", "pre-parse fail\n"));
05189       SNMP_FREE(opaque);
05190       return -1;
05191     }
05192   }
05193 
05194   if (isp->hook_create_pdu) {
05195     pdu = isp->hook_create_pdu(transport, opaque, olength);
05196   } else {
05197     pdu = snmp_create_sess_pdu(transport, opaque, olength);
05198   }
05199 
05200   /* if the transport was a magic tunnel, mark the PDU as having come
05201      through one. */
05202   if (transport->flags & NETSNMP_TRANSPORT_FLAG_TUNNELED) {
05203       pdu->flags |= UCD_MSG_FLAG_TUNNELED;
05204   }
05205 
05206   if (pdu == NULL) {
05207     snmp_log(LOG_ERR, "pdu failed to be created\n");
05208     SNMP_FREE(opaque);
05209     return -1;
05210   }
05211 
05212   if (isp->hook_parse) {
05213     ret = isp->hook_parse(sp, pdu, packetptr, length);
05214   } else {
05215     ret = snmp_parse(sessp, sp, pdu, packetptr, length);
05216   }
05217 
05218   DEBUGMSGTL(("sess_process_packet", "received message id#%ld reqid#%ld len "
05219               "%u\n", pdu->msgid, pdu->reqid, length));
05220 
05221   if (ret != SNMP_ERR_NOERROR) {
05222     DEBUGMSGTL(("sess_process_packet", "parse fail\n"));
05223   }
05224 
05225   if (isp->hook_post) {
05226     if (isp->hook_post(sp, pdu, ret) == 0) {
05227       DEBUGMSGTL(("sess_process_packet", "post-parse fail\n"));
05228       ret = SNMPERR_ASN_PARSE_ERR;
05229     }
05230   }
05231 
05232   if (ret != SNMP_ERR_NOERROR) {
05233     /*
05234      * Call the security model to free any securityStateRef supplied w/ msg.  
05235      */
05236     if (pdu->securityStateRef != NULL) {
05237       sptr = find_sec_mod(pdu->securityModel);
05238       if (sptr != NULL) {
05239         if (sptr->pdu_free_state_ref != NULL) {
05240           (*sptr->pdu_free_state_ref) (pdu->securityStateRef);
05241         } else {
05242           snmp_log(LOG_ERR,
05243                    "Security Model %d can't free state references\n",
05244                    pdu->securityModel);
05245         }
05246       } else {
05247         snmp_log(LOG_ERR,
05248                  "Can't find security model to free ptr: %d\n",
05249                  pdu->securityModel);
05250       }
05251       pdu->securityStateRef = NULL;
05252     }
05253     snmp_free_pdu(pdu);
05254     return -1;
05255   }
05256 
05257   if (pdu->flags & UCD_MSG_FLAG_RESPONSE_PDU) {
05258     /*
05259      * Call USM to free any securityStateRef supplied with the message.  
05260      */
05261     if (pdu->securityStateRef) {
05262       sptr = find_sec_mod(pdu->securityModel);
05263       if (sptr) {
05264         if (sptr->pdu_free_state_ref) {
05265           (*sptr->pdu_free_state_ref) (pdu->securityStateRef);
05266         } else {
05267           snmp_log(LOG_ERR,
05268                    "Security Model %d can't free state references\n",
05269                    pdu->securityModel);
05270         }
05271       } else {
05272         snmp_log(LOG_ERR,
05273                  "Can't find security model to free ptr: %d\n",
05274                  pdu->securityModel);
05275       }
05276       pdu->securityStateRef = NULL;
05277     }
05278 
05279     for (rp = isp->requests; rp; orp = rp, rp = rp->next_request) {
05280       snmp_callback   callback;
05281       void           *magic;
05282 
05283       if (pdu->version == SNMP_VERSION_3) {
05284         /*
05285          * msgId must match for v3 messages.  
05286          */
05287         if (rp->message_id != pdu->msgid) {
05288             DEBUGMSGTL(("sess_process_packet", "unmatched msg id: %ld != %ld\n",
05289                         rp->message_id, pdu->msgid));
05290             continue;
05291         }
05292 
05293         /*
05294          * Check that message fields match original, if not, no further
05295          * processing.  
05296          */
05297         if (!snmpv3_verify_msg(rp, pdu)) {
05298           break;
05299         }
05300       } else {
05301         if (rp->request_id != pdu->reqid) {
05302           continue;
05303         }
05304       }
05305 
05306       if (rp->callback) {
05307         callback = rp->callback;
05308         magic = rp->cb_data;
05309       } else {
05310         callback = sp->callback;
05311         magic = sp->callback_magic;
05312       }
05313       handled = 1;
05314 
05315       /*
05316        * MTR snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);  ?* XX lock
05317        * should be per session ! 
05318        */
05319 
05320       if (callback == NULL
05321           || callback(NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE, sp,
05322                       pdu->reqid, pdu, magic) == 1) {
05323         if (pdu->command == SNMP_MSG_REPORT) {
05324           if (sp->s_snmp_errno == SNMPERR_NOT_IN_TIME_WINDOW ||
05325               snmpv3_get_report_type(pdu) ==
05326               SNMPERR_NOT_IN_TIME_WINDOW) {
05327             /*
05328              * trigger immediate retry on recoverable Reports 
05329              * * (notInTimeWindow), incr_retries == TRUE to prevent
05330              * * inifinite resend                      
05331              */
05332             if (rp->retries <= sp->retries) {
05333               snmp_resend_request(slp, rp, TRUE);
05334               break;
05335             }
05336           } else {
05337             if (SNMPV3_IGNORE_UNAUTH_REPORTS) {
05338               break;
05339             }
05340           }
05341 
05342           /*
05343            * Handle engineID discovery.  
05344            */
05345           if (!sp->securityEngineIDLen && pdu->securityEngineIDLen) {
05346             sp->securityEngineID =
05347               (u_char *) malloc(pdu->securityEngineIDLen);
05348             if (sp->securityEngineID == NULL) {
05349               /*
05350                * TODO FIX: recover after message callback *?
05351                * return -1;
05352                */
05353             }
05354             memcpy(sp->securityEngineID, pdu->securityEngineID,
05355                    pdu->securityEngineIDLen);
05356             sp->securityEngineIDLen = pdu->securityEngineIDLen;
05357             if (!sp->contextEngineIDLen) {
05358               sp->contextEngineID =
05359                 (u_char *) malloc(pdu->
05360                                   securityEngineIDLen);
05361               if (sp->contextEngineID == NULL) {
05362                 /*
05363                  * TODO FIX: recover after message callback *?
05364                  * return -1;
05365                  */
05366               }
05367               memcpy(sp->contextEngineID,
05368                      pdu->securityEngineID,
05369                      pdu->securityEngineIDLen);
05370               sp->contextEngineIDLen =
05371                 pdu->securityEngineIDLen;
05372             }
05373           }
05374         }
05375 
05376         /*
05377          * Successful, so delete request.  
05378          */
05379         if (isp->requests == rp) {
05380           isp->requests = rp->next_request;
05381           if (isp->requestsEnd == rp) {
05382             isp->requestsEnd = NULL;
05383           }
05384         } else {
05385           orp->next_request = rp->next_request;
05386           if (isp->requestsEnd == rp) {
05387             isp->requestsEnd = orp;
05388           }
05389         }
05390         snmp_free_pdu(rp->pdu);
05391         free((char *) rp);
05392         /*
05393          * There shouldn't be any more requests with the same reqid.  
05394          */
05395         break;
05396       }
05397       /*
05398        * MTR snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);  ?* XX lock should be per session ! 
05399        */
05400     }
05401   } else {
05402     if (sp->callback) {
05403       /*
05404        * MTR snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); 
05405        */
05406       handled = 1;
05407       sp->callback(NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE,
05408                    sp, pdu->reqid, pdu, sp->callback_magic);
05409       /*
05410        * MTR snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); 
05411        */
05412     }
05413   }
05414 
05415   /*
05416    * Call USM to free any securityStateRef supplied with the message.  
05417    */
05418   if (pdu != NULL && pdu->securityStateRef &&
05419       pdu->command == SNMP_MSG_TRAP2) {
05420     sptr = find_sec_mod(pdu->securityModel);
05421     if (sptr) {
05422       if (sptr->pdu_free_state_ref) {
05423         (*sptr->pdu_free_state_ref) (pdu->securityStateRef);
05424       } else {
05425         snmp_log(LOG_ERR,
05426                  "Security Model %d can't free state references\n",
05427                  pdu->securityModel);
05428       }
05429     } else {
05430       snmp_log(LOG_ERR,
05431                "Can't find security model to free ptr: %d\n",
05432                pdu->securityModel);
05433     }
05434     pdu->securityStateRef = NULL;
05435   }
05436 
05437   if (!handled) {
05438     snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
05439     DEBUGMSGTL(("sess_process_packet", "unhandled PDU\n"));
05440   }
05441 
05442   snmp_free_pdu(pdu);
05443   return 0;
05444 }
05445 
05446 /*
05447  * Checks to see if any of the fd's set in the fdset belong to
05448  * snmp.  Each socket with it's fd set has a packet read from it
05449  * and snmp_parse is called on the packet received.  The resulting pdu
05450  * is passed to the callback routine for that session.  If the callback
05451  * routine returns successfully, the pdu and it's request are deleted.
05452  */
05453 void
05454 snmp_read(fd_set * fdset)
05455 {
05456     netsnmp_large_fd_set lfdset;
05457 
05458     netsnmp_large_fd_set_init(&lfdset, FD_SETSIZE);
05459     netsnmp_copy_fd_set_to_large_fd_set(&lfdset, fdset);
05460     snmp_read2(&lfdset);
05461     netsnmp_large_fd_set_cleanup(&lfdset);
05462 }
05463 
05464 void
05465 snmp_read2(netsnmp_large_fd_set * fdset)
05466 {
05467     struct session_list *slp;
05468     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
05469     for (slp = Sessions; slp; slp = slp->next) {
05470         snmp_sess_read2((void *) slp, fdset);
05471     }
05472     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
05473 }
05474 
05475 /*
05476  * Same as snmp_read, but works just one session. 
05477  * returns 0 if success, -1 if fail 
05478  * MTR: can't lock here and at snmp_read 
05479  * Beware recursive send maybe inside snmp_read callback function. 
05480  */
05481 int
05482 _sess_read(void *sessp, netsnmp_large_fd_set * fdset)
05483 {
05484     struct session_list *slp = (struct session_list *) sessp;
05485     netsnmp_session *sp = slp ? slp->session : NULL;
05486     struct snmp_internal_session *isp = slp ? slp->internal : NULL;
05487     netsnmp_transport *transport = slp ? slp->transport : NULL;
05488     size_t          pdulen = 0, rxbuf_len = 65536;
05489     u_char         *rxbuf = NULL;
05490     int             length = 0, olength = 0, rc = 0;
05491     void           *opaque = NULL;
05492 
05493     if (!sp || !isp || !transport) {
05494         DEBUGMSGTL(("sess_read", "read fail: closing...\n"));
05495         return 0;
05496     }
05497 
05498     /* to avoid subagent crash */ 
05499     if (transport->sock < 0) { 
05500         snmp_log (LOG_INFO, "transport->sock got negative fd value %d\n", transport->sock);
05501         return 0; 
05502     }
05503 
05504     if (!fdset || !(NETSNMP_LARGE_FD_ISSET(transport->sock, fdset))) {
05505         DEBUGMSGTL(("sess_read", "not reading %d (fdset %p set %d)\n",
05506                     transport->sock, fdset,
05507                     fdset ? NETSNMP_LARGE_FD_ISSET(transport->sock, fdset)
05508                     : -9));
05509         return 0;
05510     }
05511 
05512     sp->s_snmp_errno = 0;
05513     sp->s_errno = 0;
05514 
05515     if (transport->flags & NETSNMP_TRANSPORT_FLAG_LISTEN) {
05516         int             data_sock = transport->f_accept(transport);
05517 
05518         if (data_sock >= 0) {
05519             /*
05520              * We've successfully accepted a new stream-based connection.
05521              * It's not too clear what should happen here if we are using the
05522              * single-session API at this point.  Basically a "session
05523              * accepted" callback is probably needed to hand the new session
05524              * over to the application.
05525              * 
05526              * However, for now, as in the original snmp_api, we will ASSUME
05527              * that we're using the traditional API, and simply add the new
05528              * session to the list.  Note we don't have to get the Session
05529              * list lock here, because under that assumption we already hold
05530              * it (this is also why we don't just use snmp_add).
05531              * 
05532              * The moral of the story is: don't use listening stream-based
05533              * transports in a multi-threaded environment because something
05534              * will go HORRIBLY wrong (and also that SNMP/TCP is not trivial).
05535              * 
05536              * Another open issue: what should happen to sockets that have
05537              * been accept()ed from a listening socket when that original
05538              * socket is closed?  If they are left open, then attempting to
05539              * re-open the listening socket will fail, which is semantically
05540              * confusing.  Perhaps there should be some kind of chaining in
05541              * the transport structure so that they can all be closed.
05542              * Discuss.  ;-)
05543              */
05544 
05545             netsnmp_transport *new_transport=netsnmp_transport_copy(transport);
05546             if (new_transport != NULL) {
05547                 struct session_list *nslp = NULL;
05548 
05549                 new_transport->sock = data_sock;
05550                 new_transport->flags &= ~NETSNMP_TRANSPORT_FLAG_LISTEN;
05551 
05552                 nslp = (struct session_list *)snmp_sess_add_ex(sp,
05553                           new_transport, isp->hook_pre, isp->hook_parse,
05554                           isp->hook_post, isp->hook_build,
05555                           isp->hook_realloc_build, isp->check_packet,
05556                           isp->hook_create_pdu);
05557 
05558                 if (nslp != NULL) {
05559                     nslp->next = Sessions;
05560                     Sessions = nslp;
05561                     /*
05562                      * Tell the new session about its existance if possible.
05563                      */
05564                     DEBUGMSGTL(("sess_read",
05565                                 "perform callback with op=CONNECT\n"));
05566                     (void)nslp->session->callback(NETSNMP_CALLBACK_OP_CONNECT,
05567                                                   nslp->session, 0, NULL,
05568                                                   sp->callback_magic);
05569                 }
05570                 return 0;
05571             } else {
05572                 sp->s_snmp_errno = SNMPERR_MALLOC;
05573                 sp->s_errno = errno;
05574                 snmp_set_detail(strerror(errno));
05575                 return -1;
05576             }
05577         } else {
05578             sp->s_snmp_errno = SNMPERR_BAD_RECVFROM;
05579             sp->s_errno = errno;
05580             snmp_set_detail(strerror(errno));
05581             return -1;
05582         }
05583     }
05584 
05585     /*
05586      * Work out where to receive the data to.  
05587      */
05588 
05589     if (transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM) {
05590         if (isp->packet == NULL) {
05591             /*
05592              * We have no saved packet.  Allocate one.  
05593              */
05594             if ((isp->packet = (u_char *) malloc(rxbuf_len)) == NULL) {
05595                 DEBUGMSGTL(("sess_read", "can't malloc %lu bytes for rxbuf\n",
05596                             (unsigned long)rxbuf_len));
05597                 return 0;
05598             } else {
05599                 rxbuf = isp->packet;
05600                 isp->packet_size = rxbuf_len;
05601                 isp->packet_len = 0;
05602             }
05603         } else {
05604             /*
05605              * We have saved a partial packet from last time.  Extend that, if
05606              * necessary, and receive new data after the old data.  
05607              */
05608             u_char         *newbuf;
05609 
05610             if (isp->packet_size < isp->packet_len + rxbuf_len) {
05611                 newbuf =
05612                     (u_char *) realloc(isp->packet,
05613                                        isp->packet_len + rxbuf_len);
05614                 if (newbuf == NULL) {
05615                     DEBUGMSGTL(("sess_read",
05616                                 "can't malloc %lu more for rxbuf (%lu tot)\n",
05617                                 (unsigned long)rxbuf_len, (unsigned long)(isp->packet_len + rxbuf_len)));
05618                     return 0;
05619                 } else {
05620                     isp->packet = newbuf;
05621                     isp->packet_size = isp->packet_len + rxbuf_len;
05622                     rxbuf = isp->packet + isp->packet_len;
05623                 }
05624             } else {
05625                 rxbuf = isp->packet + isp->packet_len;
05626                 rxbuf_len = isp->packet_size - isp->packet_len;
05627             }
05628         }
05629     } else {
05630         if ((rxbuf = (u_char *) malloc(rxbuf_len)) == NULL) {
05631             DEBUGMSGTL(("sess_read", "can't malloc %lu bytes for rxbuf\n",
05632                         (unsigned long)rxbuf_len));
05633             return 0;
05634         }
05635     }
05636 
05637     length = netsnmp_transport_recv(transport, rxbuf, rxbuf_len, &opaque,
05638                                     &olength);
05639 
05640     if (length == -1 && !(transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM)) {
05641         sp->s_snmp_errno = SNMPERR_BAD_RECVFROM;
05642         sp->s_errno = errno;
05643         snmp_set_detail(strerror(errno));
05644         SNMP_FREE(rxbuf);
05645         SNMP_FREE(opaque);
05646         return -1;
05647     }
05648 
05649     if (0 == length && transport->flags & NETSNMP_TRANSPORT_FLAG_EMPTY_PKT) {
05650         /* this allows for a transport that needs to return from
05651          * packet processing that doesn't necessarily have any
05652          * consumable data in it. */
05653 
05654         /* reset the flag since it's a per-message flag */
05655         transport->flags &= (~NETSNMP_TRANSPORT_FLAG_EMPTY_PKT);
05656 
05657         return 0;
05658     }
05659 
05660     /*
05661      * Remote end closed connection.  
05662      */
05663 
05664     if (length <= 0 && transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM) {
05665         /*
05666          * Alert the application if possible.  
05667          */
05668         if (sp->callback != NULL) {
05669             DEBUGMSGTL(("sess_read", "perform callback with op=DISCONNECT\n"));
05670             (void) sp->callback(NETSNMP_CALLBACK_OP_DISCONNECT, sp, 0,
05671                                 NULL, sp->callback_magic);
05672         }
05673         /*
05674          * Close socket and mark session for deletion.  
05675          */
05676         DEBUGMSGTL(("sess_read", "fd %d closed\n", transport->sock));
05677         transport->f_close(transport);
05678         SNMP_FREE(isp->packet);
05679         SNMP_FREE(opaque);
05680         return -1;
05681     }
05682 
05683     if (transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM) {
05684         u_char *pptr = isp->packet;
05685         void *ocopy = NULL;
05686 
05687         isp->packet_len += length;
05688 
05689         while (isp->packet_len > 0) {
05690 
05691             /*
05692              * Get the total data length we're expecting (and need to wait
05693              * for).
05694              */
05695             if (isp->check_packet) {
05696                 pdulen = isp->check_packet(pptr, isp->packet_len);
05697             } else {
05698                 pdulen = asn_check_packet(pptr, isp->packet_len);
05699             }
05700 
05701             DEBUGMSGTL(("sess_read", "  loop packet_len %lu, PDU length %lu\n",
05702                         (unsigned long)isp->packet_len, (unsigned long)pdulen));
05703              
05704             if (pdulen > MAX_PACKET_LENGTH) {
05705                 /*
05706                  * Illegal length, drop the connection.  
05707                  */
05708                 snmp_log(LOG_ERR, 
05709                          "Received broken packet. Closing session.\n");
05710                 if (sp->callback != NULL) {
05711                   DEBUGMSGTL(("sess_read",
05712                               "perform callback with op=DISCONNECT\n"));
05713                   (void)sp->callback(NETSNMP_CALLBACK_OP_DISCONNECT,
05714                                      sp, 0, NULL, sp->callback_magic);
05715                 }
05716                 DEBUGMSGTL(("sess_read", "fd %d closed\n", transport->sock));
05717                 transport->f_close(transport);
05718                 SNMP_FREE(opaque);
05720                 return -1;
05721             }
05722 
05723             if (pdulen > isp->packet_len || pdulen == 0) {
05724                 /*
05725                  * We don't have a complete packet yet.  If we've already
05726                  * processed a packet, break out so we'll shift this packet
05727                  * to the start of the buffer. If we're already at the
05728                  * start, simply return and wait for more data to arrive.
05729                  */
05730                 DEBUGMSGTL(("sess_read",
05731                             "pkt not complete (need %lu got %lu so far)\n",
05732                             (unsigned long)pdulen, (unsigned long)isp->packet_len));
05733 
05734                 if (pptr != isp->packet)
05735                     break; /* opaque freed for us outside of loop. */
05736 
05737                 SNMP_FREE(opaque);
05738                 return 0;
05739             }
05740 
05741             /*  We have *at least* one complete packet in the buffer now.  If
05742                 we have possibly more than one packet, we must copy the opaque
05743                 pointer because we may need to reuse it for a later packet.  */
05744 
05745             if (pdulen < isp->packet_len) {
05746                 if (olength > 0 && opaque != NULL) {
05747                     ocopy = malloc(olength);
05748                     if (ocopy != NULL) {
05749                         memcpy(ocopy, opaque, olength);
05750                     }
05751                 }
05752             } else if (pdulen == isp->packet_len) {
05753                 /*  Common case -- exactly one packet.  No need to copy the
05754                     opaque pointer.  */
05755                 ocopy = opaque;
05756                 opaque = NULL;
05757             }
05758 
05759             if ((rc = _sess_process_packet(sessp, sp, isp, transport,
05760                                            ocopy, ocopy?olength:0, pptr,
05761                                            pdulen))) {
05762                 /*
05763                  * Something went wrong while processing this packet -- set the
05764                  * errno.  
05765                  */
05766                 if (sp->s_snmp_errno != 0) {
05767                     SET_SNMP_ERROR(sp->s_snmp_errno);
05768                 }
05769             }
05770 
05771             /*  ocopy has been free()d by _sess_process_packet by this point,
05772                 so set it to NULL.  */
05773 
05774             ocopy = NULL;
05775 
05776             /*  Step past the packet we've just dealt with.  */
05777 
05778             pptr += pdulen;
05779             isp->packet_len -= pdulen;
05780         }
05781 
05782         /*  If we had more than one packet, then we were working with copies
05783             of the opaque pointer, so we still need to free() the opaque
05784             pointer itself.  */
05785 
05786         SNMP_FREE(opaque);
05787 
05788         if (isp->packet_len >= MAXIMUM_PACKET_SIZE) {
05789             /*
05790              * Obviously this should never happen!  
05791              */
05792             snmp_log(LOG_ERR,
05793                      "too large packet_len = %lu, dropping connection %d\n",
05794                      (unsigned long)(isp->packet_len), transport->sock);
05795             transport->f_close(transport);
05797             return -1;
05798         } else if (isp->packet_len == 0) {
05799             /*
05800              * This is good: it means the packet buffer contained an integral
05801              * number of PDUs, so we don't have to save any data for next
05802              * time.  We can free() the buffer now to keep the memory
05803              * footprint down.
05804              */
05805             SNMP_FREE(isp->packet);
05806             isp->packet_size = 0;
05807             isp->packet_len = 0;
05808             return rc;
05809         }
05810 
05811         /*
05812          * If we get here, then there is a partial packet of length
05813          * isp->packet_len bytes starting at pptr left over.  Move that to the
05814          * start of the buffer, and then realloc() the buffer down to size to
05815          * reduce the memory footprint.  
05816          */
05817 
05818         memmove(isp->packet, pptr, isp->packet_len);
05819         DEBUGMSGTL(("sess_read", "end: memmove(%p, %p, %lu); realloc(%p, %lu)\n",
05820                     isp->packet, pptr, (unsigned long)isp->packet_len,
05821                     isp->packet, (unsigned long)isp->packet_len));
05822 
05823         if ((rxbuf = (u_char *)realloc(isp->packet, isp->packet_len)) == NULL) {
05824             /*
05825              * I don't see why this should ever fail, but it's not a big deal.
05826              */
05827             DEBUGMSGTL(("sess_read", "realloc() failed\n"));
05828         } else {
05829             DEBUGMSGTL(("sess_read", "realloc() okay, old buffer %p, new %p\n",
05830                         isp->packet, rxbuf));
05831             isp->packet = rxbuf;
05832             isp->packet_size = isp->packet_len;
05833         }
05834         return rc;
05835     } else {
05836         rc = _sess_process_packet(sessp, sp, isp, transport, opaque,
05837                                   olength, rxbuf, length);
05838         SNMP_FREE(rxbuf);
05839         return rc;
05840     }
05841 }
05842 
05843 
05844 
05845 /*
05846  * returns 0 if success, -1 if fail 
05847  */
05848 int
05849 snmp_sess_read(void *sessp, fd_set * fdset)
05850 {
05851   int rc;
05852   netsnmp_large_fd_set lfdset;
05853   
05854   netsnmp_large_fd_set_init(&lfdset, FD_SETSIZE);
05855   netsnmp_copy_fd_set_to_large_fd_set(&lfdset, fdset);
05856   rc = snmp_sess_read2(sessp, &lfdset);
05857   netsnmp_large_fd_set_cleanup(&lfdset);
05858   return rc;
05859 }
05860 
05861 int
05862 snmp_sess_read2(void *sessp, netsnmp_large_fd_set * fdset)
05863 {
05864     struct session_list *psl;
05865     netsnmp_session *pss;
05866     int             rc;
05867 
05868     rc = _sess_read(sessp, fdset);
05869     psl = (struct session_list *) sessp;
05870     pss = psl->session;
05871     if (rc && pss->s_snmp_errno) {
05872         SET_SNMP_ERROR(pss->s_snmp_errno);
05873     }
05874     return rc;
05875 }
05876 
05877 
05878 /*
05879  * Returns info about what snmp requires from a select statement.
05880  * numfds is the number of fds in the list that are significant.
05881  * All file descriptors opened for SNMP are OR'd into the fdset.
05882  * If activity occurs on any of these file descriptors, snmp_read
05883  * should be called with that file descriptor set
05884  *
05885  * The timeout is the latest time that SNMP can wait for a timeout.  The
05886  * select should be done with the minimum time between timeout and any other
05887  * timeouts necessary.  This should be checked upon each invocation of select.
05888  * If a timeout is received, snmp_timeout should be called to check if the
05889  * timeout was for SNMP.  (snmp_timeout is idempotent)
05890  *
05891  * The value of block indicates how the timeout value is interpreted.
05892  * If block is true on input, the timeout value will be treated as undefined,
05893  * but it must be available for setting in snmp_select_info.  On return,
05894  * block is set to true if the value returned for timeout is undefined;
05895  * when block is set to false, timeout may be used as a parmeter to 'select'.
05896  *
05897  * snmp_select_info returns the number of open sockets.  (i.e. The number of
05898  * sessions open)
05899  */
05900 
05901 int
05902 snmp_select_info(int *numfds,
05903                  fd_set * fdset, struct timeval *timeout, int *block)
05904     /*
05905      * input:  set to 1 if input timeout value is undefined  
05906      * set to 0 if input timeout value is defined    
05907      * output: set to 1 if output timeout value is undefined 
05908      * set to 0 if output rimeout vlaue id defined   
05909      */
05910 {
05911     return snmp_sess_select_info((void *) 0, numfds, fdset, timeout,
05912                                  block);
05913 }
05914 
05915 int
05916 snmp_select_info2(int *numfds,
05917                   netsnmp_large_fd_set * fdset,
05918                   struct timeval *timeout, int *block)
05919     /*
05920      * input:  set to 1 if input timeout value is undefined  
05921      * set to 0 if input timeout value is defined    
05922      * output: set to 1 if output timeout value is undefined 
05923      * set to 0 if output rimeout vlaue id defined   
05924      */
05925 {
05926     return snmp_sess_select_info2((void *) 0, numfds, fdset, timeout,
05927                                   block);
05928 }
05929 
05930 /*
05931  * Same as snmp_select_info, but works just one session. 
05932  */
05933 int
05934 snmp_sess_select_info(void *sessp,
05935                       int *numfds,
05936                       fd_set * fdset, struct timeval *timeout, int *block)
05937 {
05938     return snmp_sess_select_info_flags(sessp, numfds, fdset, timeout, block,
05939                                        NETSNMP_SELECT_NOFLAGS);
05940 }
05941         
05942 int
05943 snmp_sess_select_info_flags(void *sessp,
05944                             int *numfds,
05945                             fd_set * fdset, struct timeval *timeout, int *block,
05946                             int flags)
05947 {
05948   int rc;
05949   netsnmp_large_fd_set lfdset;
05950 
05951   netsnmp_large_fd_set_init(&lfdset, FD_SETSIZE);
05952   netsnmp_copy_fd_set_to_large_fd_set(&lfdset, fdset);
05953   rc = snmp_sess_select_info2_flags(sessp, numfds, &lfdset, timeout,
05954                                     block, flags);
05955   if (netsnmp_copy_large_fd_set_to_fd_set(fdset, &lfdset) < 0) {
05956       snmp_log(LOG_ERR,
05957              "Use snmp_sess_select_info2() for processing"
05958              " large file descriptors\n");
05959   }
05960   netsnmp_large_fd_set_cleanup(&lfdset);
05961   return rc;
05962 }
05963 
05964 int
05965 snmp_sess_select_info2(void *sessp,
05966                        int *numfds,
05967                        netsnmp_large_fd_set * fdset,
05968                        struct timeval *timeout, int *block)
05969 {
05970     return snmp_sess_select_info2_flags(sessp, numfds, fdset, timeout, block,
05971                                         NETSNMP_SELECT_NOFLAGS);
05972 }
05973 
05974 int
05975 snmp_sess_select_info2_flags(void *sessp,
05976                             int *numfds,
05977                             netsnmp_large_fd_set * fdset,
05978                             struct timeval *timeout, int *block, int flags)
05979 {
05980     struct session_list *slptest = (struct session_list *) sessp;
05981     struct session_list *slp, *next = NULL;
05982     netsnmp_request_list *rp;
05983     struct timeval  now, earliest, delta;
05984     int             active = 0, requests = 0;
05985     int             next_alarm = 0;
05986 
05987     timerclear(&earliest);
05988 
05989     /*
05990      * For each request outstanding, add its socket to the fdset,
05991      * and if it is the earliest timeout to expire, mark it as lowest.
05992      * If a single session is specified, do just for that session.
05993      */
05994 
05995     if (sessp) {
05996         slp = slptest;
05997     } else {
05998         slp = Sessions;
05999     }
06000 
06001     DEBUGMSGTL(("sess_select", "for %s session%s: ",
06002                 sessp ? "single" : "all", sessp ? "" : "s"));
06003 
06004     for (; slp; slp = next) {
06005         next = slp->next;
06006 
06007         if (slp->transport == NULL) {
06008             /*
06009              * Close in progress -- skip this one.  
06010              */
06011             DEBUGMSG(("sess_select", "skip "));
06012             continue;
06013         }
06014 
06015         if (slp->transport->sock == -1) {
06016             /*
06017              * This session was marked for deletion.  
06018              */
06019             DEBUGMSG(("sess_select", "delete\n"));
06020             if (sessp == NULL) {
06021                 snmp_close(slp->session);
06022             } else {
06023                 snmp_sess_close(slp);
06024             }
06025             DEBUGMSGTL(("sess_select", "for %s session%s: ",
06026                         sessp ? "single" : "all", sessp ? "" : "s"));
06027             continue;
06028         }
06029 
06030         DEBUGMSG(("sess_select", "%d ", slp->transport->sock));
06031         if ((slp->transport->sock + 1) > *numfds) {
06032             *numfds = (slp->transport->sock + 1);
06033         }
06034 
06035         NETSNMP_LARGE_FD_SET(slp->transport->sock, fdset);
06036         if (slp->internal != NULL && slp->internal->requests) {
06037             /*
06038              * Found another session with outstanding requests.  
06039              */
06040             requests++;
06041             for (rp = slp->internal->requests; rp; rp = rp->next_request) {
06042                 if ((!timerisset(&earliest)
06043                      || (timercmp(&rp->expire, &earliest, <)))) {
06044                     earliest = rp->expire;
06045                     DEBUGMSG(("verbose:sess_select","(to in %d.%06d sec) ",
06046                                (int)earliest.tv_sec, (int)earliest.tv_usec));
06047                 }
06048             }
06049         }
06050 
06051         active++;
06052         if (sessp) {
06053             /*
06054              * Single session processing.  
06055              */
06056             break;
06057         }
06058     }
06059     DEBUGMSG(("sess_select", "\n"));
06060 
06061     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
06062                                NETSNMP_DS_LIB_ALARM_DONT_USE_SIG) &&
06063         !(flags & NETSNMP_SELECT_NOALARMS)) {
06064         next_alarm = get_next_alarm_delay_time(&delta);
06065         DEBUGMSGT(("sess_select","next alarm %d.%06d sec\n",
06066                    (int)delta.tv_sec, (int)delta.tv_usec));
06067     }
06068     if (next_alarm == 0 && requests == 0) {
06069         /*
06070          * If none are active, skip arithmetic.  
06071          */
06072         DEBUGMSGT(("sess_select","blocking:no session requests or alarms.\n"));
06073         *block = 1; /* can block - timeout value is undefined if no requests */
06074         return active;
06075     }
06076 
06077     /*
06078      * * Now find out how much time until the earliest timeout.  This
06079      * * transforms earliest from an absolute time into a delta time, the
06080      * * time left until the select should timeout.
06081      */
06082     gettimeofday(&now, (struct timezone *) 0);
06083     /*
06084      * Now = now;
06085      */
06086 
06087     if (next_alarm) {
06088         delta.tv_sec += now.tv_sec;
06089         delta.tv_usec += now.tv_usec;
06090         while (delta.tv_usec >= 1000000) {
06091             delta.tv_usec -= 1000000;
06092             delta.tv_sec += 1;
06093         }
06094         if (!timerisset(&earliest) ||
06095             ((earliest.tv_sec > delta.tv_sec) ||
06096              ((earliest.tv_sec == delta.tv_sec) &&
06097               (earliest.tv_usec > delta.tv_usec)))) {
06098             earliest.tv_sec = delta.tv_sec;
06099             earliest.tv_usec = delta.tv_usec;
06100         }
06101     }
06102 
06103     if (earliest.tv_sec < now.tv_sec) {
06104         DEBUGMSGT(("verbose:sess_select","timer overdue\n"));
06105         earliest.tv_sec = 0;
06106         earliest.tv_usec = 0;
06107     } else if (earliest.tv_sec == now.tv_sec) {
06108         earliest.tv_sec = 0;
06109         earliest.tv_usec = (earliest.tv_usec - now.tv_usec);
06110         if (earliest.tv_usec < 0) {
06111             earliest.tv_usec = 100;
06112         }
06113         DEBUGMSGT(("verbose:sess_select","timer due *real* soon. %d usec\n",
06114                    (int)earliest.tv_usec));
06115     } else {
06116         earliest.tv_sec = (earliest.tv_sec - now.tv_sec);
06117         earliest.tv_usec = (earliest.tv_usec - now.tv_usec);
06118         if (earliest.tv_usec < 0) {
06119             earliest.tv_sec--;
06120             earliest.tv_usec = (1000000L + earliest.tv_usec);
06121         }
06122         DEBUGMSGT(("verbose:sess_select","timer due in %d.%06d sec\n",
06123                    (int)earliest.tv_sec, (int)earliest.tv_usec));
06124     }
06125 
06126     /*
06127      * if it was blocking before or our delta time is less, reset timeout 
06128      */
06129     if ((*block || (timercmp(&earliest, timeout, <)))) {
06130         DEBUGMSGT(("verbose:sess_select",
06131                    "setting timer to %d.%06d sec, clear block (was %d)\n",
06132                    (int)earliest.tv_sec, (int)earliest.tv_usec, *block));
06133         *timeout = earliest;
06134         *block = 0;
06135     }
06136     return active;
06137 }
06138 
06139 /*
06140  * snmp_timeout should be called whenever the timeout from snmp_select_info
06141  * expires, but it is idempotent, so snmp_timeout can be polled (probably a
06142  * cpu expensive proposition).  snmp_timeout checks to see if any of the
06143  * sessions have an outstanding request that has timed out.  If it finds one
06144  * (or more), and that pdu has more retries available, a new packet is formed
06145  * from the pdu and is resent.  If there are no more retries available, the
06146  *  callback for the session is used to alert the user of the timeout.
06147  */
06148 void
06149 snmp_timeout(void)
06150 {
06151     struct session_list *slp;
06152     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
06153     for (slp = Sessions; slp; slp = slp->next) {
06154         snmp_sess_timeout((void *) slp);
06155     }
06156     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
06157 }
06158 
06159 static int
06160 snmp_resend_request(struct session_list *slp, netsnmp_request_list *rp,
06161                     int incr_retries)
06162 {
06163     struct snmp_internal_session *isp;
06164     netsnmp_session *sp;
06165     netsnmp_transport *transport;
06166     u_char         *pktbuf = NULL, *packet = NULL;
06167     size_t          pktbuf_len = 0, offset = 0, length = 0;
06168     struct timeval  tv, now;
06169     int             result = 0;
06170 
06171     sp = slp->session;
06172     isp = slp->internal;
06173     transport = slp->transport;
06174     if (!sp || !isp || !transport) {
06175         DEBUGMSGTL(("sess_read", "resend fail: closing...\n"));
06176         return 0;
06177     }
06178 
06179     if ((pktbuf = (u_char *)malloc(2048)) == NULL) {
06180         DEBUGMSGTL(("sess_resend",
06181                     "couldn't malloc initial packet buffer\n"));
06182         return 0;
06183     } else {
06184         pktbuf_len = 2048;
06185     }
06186 
06187     if (incr_retries) {
06188         rp->retries++;
06189     }
06190 
06191     /*
06192      * Always increment msgId for resent messages.  
06193      */
06194     rp->pdu->msgid = rp->message_id = snmp_get_next_msgid();
06195 
06196     if (isp->hook_realloc_build) {
06197         result = isp->hook_realloc_build(sp, rp->pdu,
06198                                          &pktbuf, &pktbuf_len, &offset);
06199 
06200         packet = pktbuf;
06201         length = offset;
06202     } else if (isp->hook_build) {
06203         packet = pktbuf;
06204         length = pktbuf_len;
06205         result = isp->hook_build(sp, rp->pdu, pktbuf, &length);
06206     } else {
06207 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
06208         if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE)) {
06209             result =
06210                 snmp_build(&pktbuf, &pktbuf_len, &offset, sp, rp->pdu);
06211             packet = pktbuf + pktbuf_len - offset;
06212             length = offset;
06213         } else {
06214 #endif
06215             packet = pktbuf;
06216             length = pktbuf_len;
06217             result = snmp_build(&pktbuf, &length, &offset, sp, rp->pdu);
06218 #ifdef NETSNMP_USE_REVERSE_ASNENCODING
06219         }
06220 #endif
06221     }
06222 
06223     if (result < 0) {
06224         /*
06225          * This should never happen.  
06226          */
06227         DEBUGMSGTL(("sess_resend", "encoding failure\n"));
06228         SNMP_FREE(pktbuf);
06229         return -1;
06230     }
06231 
06232     DEBUGMSGTL(("sess_process_packet", "resending message id#%ld reqid#%ld "
06233                 "rp_reqid#%ld rp_msgid#%ld len %" NETSNMP_PRIz "u\n",
06234                 rp->pdu->msgid, rp->pdu->reqid, rp->request_id, rp->message_id, length));
06235     result = netsnmp_transport_send(transport, packet, length,
06236                                     &(rp->pdu->transport_data),
06237                                     &(rp->pdu->transport_data_length));
06238 
06239     /*
06240      * We are finished with the local packet buffer, if we allocated one (due
06241      * to there being no saved packet).  
06242      */
06243 
06244     if (pktbuf != NULL) {
06245         SNMP_FREE(pktbuf);
06246         packet = NULL;
06247     }
06248 
06249     if (result < 0) {
06250         sp->s_snmp_errno = SNMPERR_BAD_SENDTO;
06251         sp->s_errno = errno;
06252         snmp_set_detail(strerror(errno));
06253         return -1;
06254     } else {
06255         gettimeofday(&now, (struct timezone *) 0);
06256         tv = now;
06257         rp->time = tv;
06258         tv.tv_usec += rp->timeout;
06259         tv.tv_sec += tv.tv_usec / 1000000L;
06260         tv.tv_usec %= 1000000L;
06261         rp->expire = tv;
06262     }
06263     return 0;
06264 }
06265 
06266 
06267 
06268 void
06269 snmp_sess_timeout(void *sessp)
06270 {
06271     struct session_list *slp = (struct session_list *) sessp;
06272     netsnmp_session *sp;
06273     struct snmp_internal_session *isp;
06274     netsnmp_request_list *rp, *orp = NULL, *freeme = NULL;
06275     struct timeval  now;
06276     snmp_callback   callback;
06277     void           *magic;
06278     struct snmp_secmod_def *sptr;
06279 
06280     sp = slp->session;
06281     isp = slp->internal;
06282     if (!sp || !isp) {
06283         DEBUGMSGTL(("sess_read", "timeout fail: closing...\n"));
06284         return;
06285     }
06286 
06287     gettimeofday(&now, (struct timezone *) 0);
06288 
06289     /*
06290      * For each request outstanding, check to see if it has expired.
06291      */
06292     for (rp = isp->requests; rp; rp = rp->next_request) {
06293         if (freeme != NULL) {
06294             /*
06295              * frees rp's after the for loop goes on to the next_request 
06296              */
06297             free((char *) freeme);
06298             freeme = NULL;
06299         }
06300 
06301         if ((timercmp(&rp->expire, &now, <))) {
06302             if ((sptr = find_sec_mod(rp->pdu->securityModel)) != NULL &&
06303                 sptr->pdu_timeout != NULL) {
06304                 /*
06305                  * call security model if it needs to know about this 
06306                  */
06307                 (*sptr->pdu_timeout) (rp->pdu);
06308             }
06309 
06310             /*
06311              * this timer has expired 
06312              */
06313             if (rp->retries >= sp->retries) {
06314                 if (rp->callback) {
06315                     callback = rp->callback;
06316                     magic = rp->cb_data;
06317                 } else {
06318                     callback = sp->callback;
06319                     magic = sp->callback_magic;
06320                 }
06321 
06322                 /*
06323                  * No more chances, delete this entry 
06324                  */
06325                 if (callback) {
06326                     callback(NETSNMP_CALLBACK_OP_TIMED_OUT, sp,
06327                              rp->pdu->reqid, rp->pdu, magic);
06328                 }
06329                 if (isp->requests == rp) {
06330                     isp->requests = rp->next_request;
06331                     if (isp->requestsEnd == rp) {
06332                         isp->requestsEnd = NULL;
06333                     }
06334                 } else {
06335                     orp->next_request = rp->next_request;
06336                     if (isp->requestsEnd == rp) {
06337                         isp->requestsEnd = orp;
06338                     }
06339                 }
06340                 snmp_free_pdu(rp->pdu); /* FIX  rp is already free'd! */
06341                 freeme = rp;
06342                 continue;       /* don't update orp below */
06343             } else {
06344                 if (snmp_resend_request(slp, rp, TRUE)) {
06345                     break;
06346                 }
06347             }
06348         }
06349         orp = rp;
06350     }
06351 
06352     if (freeme != NULL) {
06353         free((char *) freeme);
06354         freeme = NULL;
06355     }
06356 }
06357 
06358 /*
06359  * lexicographical compare two object identifiers.
06360  * * Returns -1 if name1 < name2,
06361  * *          0 if name1 = name2,
06362  * *          1 if name1 > name2
06363  * *
06364  * * Caution: this method is called often by
06365  * *          command responder applications (ie, agent).
06366  */
06367 int
06368 snmp_oid_ncompare(const oid * in_name1,
06369                   size_t len1,
06370                   const oid * in_name2, size_t len2, size_t max_len)
06371 {
06372     register int    len;
06373     register const oid *name1 = in_name1;
06374     register const oid *name2 = in_name2;
06375     size_t          min_len;
06376 
06377     /*
06378      * len = minimum of len1 and len2 
06379      */
06380     if (len1 < len2)
06381         min_len = len1;
06382     else
06383         min_len = len2;
06384 
06385     if (min_len > max_len)
06386         min_len = max_len;
06387 
06388     len = min_len;
06389 
06390     /*
06391      * find first non-matching OID 
06392      */
06393     while (len-- > 0) {
06394         /*
06395          * these must be done in seperate comparisons, since
06396          * subtracting them and using that result has problems with
06397          * subids > 2^31. 
06398          */
06399         if (*(name1) != *(name2)) {
06400             if (*(name1) < *(name2))
06401                 return -1;
06402             return 1;
06403         }
06404         name1++;
06405         name2++;
06406     }
06407 
06408     if (min_len != max_len) {
06409         /*
06410          * both OIDs equal up to length of shorter OID 
06411          */
06412         if (len1 < len2)
06413             return -1;
06414         if (len2 < len1)
06415             return 1;
06416     }
06417 
06418     return 0;
06419 }
06420 
06428 int
06429 snmp_oid_compare(const oid * in_name1,
06430                  size_t len1, const oid * in_name2, size_t len2)
06431 {
06432     register int    len;
06433     register const oid *name1 = in_name1;
06434     register const oid *name2 = in_name2;
06435 
06436     /*
06437      * len = minimum of len1 and len2 
06438      */
06439     if (len1 < len2)
06440         len = len1;
06441     else
06442         len = len2;
06443     /*
06444      * find first non-matching OID 
06445      */
06446     while (len-- > 0) {
06447         /*
06448          * these must be done in seperate comparisons, since
06449          * subtracting them and using that result has problems with
06450          * subids > 2^31. 
06451          */
06452         if (*(name1) != *(name2)) {
06453             if (*(name1) < *(name2))
06454                 return -1;
06455             return 1;
06456         }
06457         name1++;
06458         name2++;
06459     }
06460     /*
06461      * both OIDs equal up to length of shorter OID 
06462      */
06463     if (len1 < len2)
06464         return -1;
06465     if (len2 < len1)
06466         return 1;
06467     return 0;
06468 }
06469 
06477 int
06478 netsnmp_oid_compare_ll(const oid * in_name1,
06479                        size_t len1, const oid * in_name2, size_t len2,
06480                        size_t *offpt)
06481 {
06482     register int    len;
06483     register const oid *name1 = in_name1;
06484     register const oid *name2 = in_name2;
06485     int initlen;
06486 
06487     /*
06488      * len = minimum of len1 and len2 
06489      */
06490     if (len1 < len2)
06491         initlen = len = len1;
06492     else
06493         initlen = len = len2;
06494     /*
06495      * find first non-matching OID 
06496      */
06497     while (len-- > 0) {
06498         /*
06499          * these must be done in seperate comparisons, since
06500          * subtracting them and using that result has problems with
06501          * subids > 2^31. 
06502          */
06503         if (*(name1) != *(name2)) {
06504             *offpt = initlen - len;
06505             if (*(name1) < *(name2))
06506                 return -1;
06507             return 1;
06508         }
06509         name1++;
06510         name2++;
06511     }
06512     /*
06513      * both OIDs equal up to length of shorter OID 
06514      */
06515     *offpt = initlen - len;
06516     if (len1 < len2)
06517         return -1;
06518     if (len2 < len1)
06519         return 1;
06520     return 0;
06521 }
06522 
06530 int
06531 snmp_oidtree_compare(const oid * in_name1,
06532                      size_t len1, const oid * in_name2, size_t len2)
06533 {
06534     int             len = ((len1 < len2) ? len1 : len2);
06535 
06536     return (snmp_oid_compare(in_name1, len, in_name2, len));
06537 }
06538 
06539 int
06540 snmp_oidsubtree_compare(const oid * in_name1,
06541                      size_t len1, const oid * in_name2, size_t len2)
06542 {
06543     int             len = ((len1 < len2) ? len1 : len2);
06544 
06545     return (snmp_oid_compare(in_name1, len1, in_name2, len));
06546 }
06547 
06558 int
06559 netsnmp_oid_equals(const oid * in_name1,
06560                    size_t len1, const oid * in_name2, size_t len2)
06561 {
06562     register const oid *name1 = in_name1;
06563     register const oid *name2 = in_name2;
06564     register int    len = len1;
06565 
06566     /*
06567      * len = minimum of len1 and len2 
06568      */
06569     if (len1 != len2)
06570         return 1;
06571     /*
06572      * Handle 'null' OIDs
06573      */
06574     if (len1 == 0)
06575         return 0;   /* Two null OIDs are (trivially) the same */
06576     if (!name1 || !name2)
06577         return 1;   /* Otherwise something's wrong, so report a non-match */
06578     /*
06579      * find first non-matching OID 
06580      */
06581     while (len-- > 0) {
06582         /*
06583          * these must be done in seperate comparisons, since
06584          * subtracting them and using that result has problems with
06585          * subids > 2^31. 
06586          */
06587         if (*(name1++) != *(name2++))
06588             return 1;
06589     }
06590     return 0;
06591 }
06592 
06593 #ifndef NETSNMP_FEATURE_REMOVE_OID_IS_SUBTREE
06594 
06602 int
06603 netsnmp_oid_is_subtree(const oid * in_name1,
06604                        size_t len1, const oid * in_name2, size_t len2)
06605 {
06606     if (len1 > len2)
06607         return 1;
06608 
06609     if (memcmp(in_name1, in_name2, len1 * sizeof(oid)))
06610         return 1;
06611 
06612     return 0;
06613 }
06614 #endif /* NETSNMP_FEATURE_REMOVE_OID_IS_SUBTREE */
06615 
06624 int
06625 netsnmp_oid_find_prefix(const oid * in_name1, size_t len1,
06626                         const oid * in_name2, size_t len2)
06627 {
06628     int i;
06629     size_t min_size;
06630 
06631     if (!in_name1 || !in_name2 || !len1 || !len2)
06632         return -1;
06633 
06634     if (in_name1[0] != in_name2[0])
06635         return 0;   /* No match */
06636     min_size = SNMP_MIN(len1, len2);
06637     for(i = 0; i < (int)min_size; i++) {
06638         if (in_name1[i] != in_name2[i])
06639             return i;    /* 'í' is the first differing subidentifier
06640                             So the common prefix is 0..(i-1), of length i */
06641     }
06642     return min_size;    /* The shorter OID is a prefix of the longer, and
06643                            hence is precisely the common prefix of the two.
06644                            Return its length. */
06645 }
06646 
06647 static int _check_range(struct tree *tp, long ltmp, int *resptr,
06648                         const char *errmsg)
06649 {
06650     char *cp   = NULL;
06651     char *temp = NULL;
06652     int   temp_len = 0;
06653     int check = !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
06654                                         NETSNMP_DS_LIB_DONT_CHECK_RANGE);
06655   
06656     if (check && tp && tp->ranges) {
06657         struct range_list *rp = tp->ranges;
06658         while (rp) {
06659             if (rp->low <= ltmp && ltmp <= rp->high) break;
06660                                   /* Allow four digits per range value */
06661             temp_len += ((rp->low != rp->high) ? 14 : 8 );
06662             rp = rp->next;
06663         }
06664         if (!rp) {
06665             *resptr = SNMPERR_RANGE;
06666             temp = (char *)malloc( temp_len+strlen(errmsg)+7);
06667             if ( temp ) {
06668                 /* Append the Display Hint range information to the error message */
06669                 sprintf( temp, "%s :: {", errmsg );
06670                 cp = temp+(strlen(temp));
06671                 for ( rp = tp->ranges; rp; rp=rp->next ) {
06672                     if ( rp->low != rp->high ) 
06673                         sprintf( cp, "(%d..%d), ", rp->low, rp->high );
06674                     else
06675                         sprintf( cp, "(%d), ", rp->low );
06676                     cp += strlen(cp);
06677                 }
06678                 *(cp-2) = '}';   /* Replace the final comma with a '}' */
06679                 *(cp-1) = 0;
06680                 snmp_set_detail(temp);
06681                 free(temp);
06682             }
06683             return 0;
06684         }
06685     }
06686     free(temp);
06687     return 1;
06688 }
06689         
06690 
06691 /*
06692  * Add a variable with the requested name to the end of the list of
06693  * variables for this pdu.
06694  */
06695 netsnmp_variable_list *
06696 snmp_pdu_add_variable(netsnmp_pdu *pdu,
06697                       const oid * name,
06698                       size_t name_length,
06699                       u_char type, const void * value, size_t len)
06700 {
06701     return snmp_varlist_add_variable(&pdu->variables, name, name_length,
06702                                      type, value, len);
06703 }
06704 
06705 /*
06706  * Add a variable with the requested name to the end of the list of
06707  * variables for this pdu.
06708  */
06709 netsnmp_variable_list *
06710 snmp_varlist_add_variable(netsnmp_variable_list ** varlist,
06711                           const oid * name,
06712                           size_t name_length,
06713                           u_char type, const void * value, size_t len)
06714 {
06715     netsnmp_variable_list *vars, *vtmp;
06716     int rc;
06717 
06718     if (varlist == NULL)
06719         return NULL;
06720 
06721     vars = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
06722     if (vars == NULL)
06723         return NULL;
06724 
06725     vars->type = type;
06726 
06727     rc = snmp_set_var_value( vars, value, len );
06728     if (( 0 != rc ) ||
06729         (name != NULL && snmp_set_var_objid(vars, name, name_length))) {
06730         snmp_free_var(vars);
06731         return NULL;
06732     }
06733 
06734     /*
06735      * put only qualified variable onto varlist 
06736      */
06737     if (*varlist == NULL) {
06738         *varlist = vars;
06739     } else {
06740         for (vtmp = *varlist; vtmp->next_variable;
06741              vtmp = vtmp->next_variable);
06742 
06743         vtmp->next_variable = vars;
06744     }
06745 
06746     return vars;
06747 }
06748 
06749 
06750 
06751 /*
06752  * Add a variable with the requested name to the end of the list of
06753  * variables for this pdu.
06754  * Returns:
06755  * may set these error types :
06756  * SNMPERR_RANGE - type, value, or length not found or out of range
06757  * SNMPERR_VALUE - value is not correct
06758  * SNMPERR_VAR_TYPE - type is not correct
06759  * SNMPERR_BAD_NAME - name is not found
06760  *
06761  * returns 0 if success, error if failure.
06762  */
06763 int
06764 snmp_add_var(netsnmp_pdu *pdu,
06765              const oid * name, size_t name_length, char type, const char *value)
06766 {
06767     char           *st;
06768     const char     *cp;
06769     char           *ecp, *vp;
06770     int             result = SNMPERR_SUCCESS;
06771 #ifndef NETSNMP_DISABLE_MIB_LOADING
06772     int             check = !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
06773                                              NETSNMP_DS_LIB_DONT_CHECK_RANGE);
06774     int             do_hint = !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
06775                                              NETSNMP_DS_LIB_NO_DISPLAY_HINT);
06776     u_char         *hintptr;
06777     struct tree    *tp;
06778 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06779     u_char         *buf = NULL;
06780     const u_char   *buf_ptr = NULL;
06781     size_t          buf_len = 0, value_len = 0, tint;
06782     in_addr_t       atmp;
06783     long            ltmp;
06784     int             itmp;
06785     struct enum_list *ep;
06786 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
06787     double          dtmp;
06788     float           ftmp;
06789 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
06790     struct counter64 c64tmp;
06791 
06792 #ifndef NETSNMP_DISABLE_MIB_LOADING
06793     tp = get_tree(name, name_length, get_tree_head());
06794     if (!tp || !tp->type || tp->type > TYPE_SIMPLE_LAST) {
06795         check = 0;
06796     }
06797     if (!(tp && tp->hint))
06798         do_hint = 0;
06799 
06800     if (tp && type == '=') {
06801         /*
06802          * generic assignment - let the tree node decide value format 
06803          */
06804         switch (tp->type) {
06805         case TYPE_INTEGER:
06806         case TYPE_INTEGER32:
06807             type = 'i';
06808             break;
06809         case TYPE_GAUGE:
06810         case TYPE_UNSIGNED32:
06811             type = 'u';
06812             break;
06813         case TYPE_UINTEGER:
06814             type = '3';
06815             break;
06816         case TYPE_COUNTER:
06817             type = 'c';
06818             break;
06819         case TYPE_COUNTER64:
06820             type = 'C';
06821             break;
06822         case TYPE_TIMETICKS:
06823             type = 't';
06824             break;
06825         case TYPE_OCTETSTR:
06826             type = 's';
06827             break;
06828         case TYPE_BITSTRING:
06829             type = 'b';
06830             break;
06831         case TYPE_IPADDR:
06832             type = 'a';
06833             break;
06834         case TYPE_OBJID:
06835             type = 'o';
06836             break;
06837         }
06838     }
06839 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06840 
06841     switch (type) {
06842     case 'i':
06843 #ifndef NETSNMP_DISABLE_MIB_LOADING
06844         if (check && tp->type != TYPE_INTEGER
06845             && tp->type != TYPE_INTEGER32) {
06846             value = "INTEGER";
06847             result = SNMPERR_VALUE;
06848             goto type_error;
06849         }
06850 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06851         if (!*value)
06852             goto fail;
06853         ltmp = strtol(value, &ecp, 10);
06854         if (*ecp) {
06855 #ifndef NETSNMP_DISABLE_MIB_LOADING
06856             ep = tp ? tp->enums : NULL;
06857             while (ep) {
06858                 if (strcmp(value, ep->label) == 0) {
06859                     ltmp = ep->value;
06860                     break;
06861                 }
06862                 ep = ep->next;
06863             }
06864             if (!ep) {
06865 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06866                 result = SNMPERR_RANGE;   /* ?? or SNMPERR_VALUE; */
06867                 snmp_set_detail(value);
06868                 break;
06869 #ifndef NETSNMP_DISABLE_MIB_LOADING
06870             }
06871 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06872         }
06873 
06874 #ifndef NETSNMP_DISABLE_MIB_LOADING
06875         if (!_check_range(tp, ltmp, &result, value))
06876             break;
06877 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06878         snmp_pdu_add_variable(pdu, name, name_length, ASN_INTEGER,
06879                               &ltmp, sizeof(ltmp));
06880         break;
06881 
06882     case 'u':
06883 #ifndef NETSNMP_DISABLE_MIB_LOADING
06884         if (check && tp->type != TYPE_GAUGE && tp->type != TYPE_UNSIGNED32) {
06885             value = "Unsigned32";
06886             result = SNMPERR_VALUE;
06887             goto type_error;
06888         }
06889 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06890         ltmp = strtoul(value, &ecp, 10);
06891         if (*value && !*ecp)
06892             snmp_pdu_add_variable(pdu, name, name_length, ASN_UNSIGNED,
06893                                   &ltmp, sizeof(ltmp));
06894         else
06895             goto fail;
06896         break;
06897 
06898     case '3':
06899 #ifndef NETSNMP_DISABLE_MIB_LOADING
06900         if (check && tp->type != TYPE_UINTEGER) {
06901             value = "UInteger32";
06902             result = SNMPERR_VALUE;
06903             goto type_error;
06904         }
06905 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06906         ltmp = strtoul(value, &ecp, 10);
06907         if (*value && !*ecp)
06908             snmp_pdu_add_variable(pdu, name, name_length, ASN_UINTEGER,
06909                                   &ltmp, sizeof(ltmp));
06910         else
06911             goto fail;
06912         break;
06913 
06914     case 'c':
06915 #ifndef NETSNMP_DISABLE_MIB_LOADING
06916         if (check && tp->type != TYPE_COUNTER) {
06917             value = "Counter32";
06918             result = SNMPERR_VALUE;
06919             goto type_error;
06920         }
06921 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06922         ltmp = strtoul(value, &ecp, 10);
06923         if (*value && !*ecp)
06924             snmp_pdu_add_variable(pdu, name, name_length, ASN_COUNTER,
06925                                   &ltmp, sizeof(ltmp));
06926         else
06927             goto fail;
06928         break;
06929 
06930     case 'C':
06931 #ifndef NETSNMP_DISABLE_MIB_LOADING
06932         if (check && tp->type != TYPE_COUNTER64) {
06933             value = "Counter64";
06934             result = SNMPERR_VALUE;
06935             goto type_error;
06936         }
06937 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06938         if (read64(&c64tmp, value))
06939             snmp_pdu_add_variable(pdu, name, name_length, ASN_COUNTER64,
06940                                   &c64tmp, sizeof(c64tmp));
06941         else
06942             goto fail;
06943         break;
06944 
06945     case 't':
06946 #ifndef NETSNMP_DISABLE_MIB_LOADING
06947         if (check && tp->type != TYPE_TIMETICKS) {
06948             value = "Timeticks";
06949             result = SNMPERR_VALUE;
06950             goto type_error;
06951         }
06952 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06953         ltmp = strtoul(value, &ecp, 10);
06954         if (*value && !*ecp)
06955             snmp_pdu_add_variable(pdu, name, name_length, ASN_TIMETICKS,
06956                                   &ltmp, sizeof(long));
06957         else
06958             goto fail;
06959         break;
06960 
06961     case 'a':
06962 #ifndef NETSNMP_DISABLE_MIB_LOADING
06963         if (check && tp->type != TYPE_IPADDR) {
06964             value = "IpAddress";
06965             result = SNMPERR_VALUE;
06966             goto type_error;
06967         }
06968 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06969         atmp = inet_addr(value);
06970         if (atmp != (in_addr_t) -1 || !strcmp(value, "255.255.255.255"))
06971             snmp_pdu_add_variable(pdu, name, name_length, ASN_IPADDRESS,
06972                                   &atmp, sizeof(atmp));
06973         else
06974             goto fail;
06975         break;
06976 
06977     case 'o':
06978 #ifndef NETSNMP_DISABLE_MIB_LOADING
06979         if (check && tp->type != TYPE_OBJID) {
06980             value = "OBJECT IDENTIFIER";
06981             result = SNMPERR_VALUE;
06982             goto type_error;
06983         }
06984 #endif /* NETSNMP_DISABLE_MIB_LOADING */
06985         if ((buf = (u_char *)malloc(sizeof(oid) * MAX_OID_LEN)) == NULL) {
06986             result = SNMPERR_MALLOC;
06987         } else {
06988             tint = MAX_OID_LEN;
06989             if (snmp_parse_oid(value, (oid *) buf, &tint)) {
06990                 snmp_pdu_add_variable(pdu, name, name_length, ASN_OBJECT_ID,
06991                                       buf, sizeof(oid) * tint);
06992             } else {
06993                 result = snmp_errno;    /*MTCRITICAL_RESOURCE */
06994             }
06995         }
06996         break;
06997 
06998     case 's':
06999     case 'x':
07000     case 'd':
07001 #ifndef NETSNMP_DISABLE_MIB_LOADING
07002         if (check && tp->type != TYPE_OCTETSTR && tp->type != TYPE_BITSTRING) {
07003             value = "OCTET STRING";
07004             result = SNMPERR_VALUE;
07005             goto type_error;
07006         }
07007         if ('s' == type && do_hint && !parse_octet_hint(tp->hint, value, &hintptr, &itmp)) {
07008             if (_check_range(tp, itmp, &result, "Value does not match DISPLAY-HINT")) {
07009                 snmp_pdu_add_variable(pdu, name, name_length, ASN_OCTET_STR,
07010                                       hintptr, itmp);
07011             }
07012             SNMP_FREE(hintptr);
07013             hintptr = buf;
07014             break;
07015         }
07016 #endif /* NETSNMP_DISABLE_MIB_LOADING */
07017         if (type == 'd') {
07018             if (!snmp_decimal_to_binary
07019                 (&buf, &buf_len, &value_len, 1, value)) {
07020                 result = SNMPERR_VALUE;
07021                 snmp_set_detail(value);
07022                 break;
07023             }
07024             buf_ptr = buf;
07025         } else if (type == 'x') {
07026             if (!snmp_hex_to_binary(&buf, &buf_len, &value_len, 1, value)) {
07027                 result = SNMPERR_VALUE;
07028                 snmp_set_detail(value);
07029                 break;
07030             }
07031             /* initialize itmp value so that range check below works */
07032             itmp = value_len;
07033             buf_ptr = buf;
07034         } else if (type == 's') {
07035             buf_ptr = (const u_char *)value;
07036             value_len = strlen(value);
07037         }
07038 #ifndef NETSNMP_DISABLE_MIB_LOADING
07039         if (!_check_range(tp, value_len, &result, "Bad string length"))
07040             break;
07041 #endif /* NETSNMP_DISABLE_MIB_LOADING */
07042         snmp_pdu_add_variable(pdu, name, name_length, ASN_OCTET_STR,
07043                               buf_ptr, value_len);
07044         break;
07045 
07046     case 'n':
07047         snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
07048         break;
07049 
07050     case 'b':
07051 #ifndef NETSNMP_DISABLE_MIB_LOADING
07052         if (check && (tp->type != TYPE_BITSTRING || !tp->enums)) {
07053             value = "BITS";
07054             result = SNMPERR_VALUE;
07055             goto type_error;
07056         }
07057 #endif /* NETSNMP_DISABLE_MIB_LOADING */
07058         tint = 0;
07059         if ((buf = (u_char *) malloc(256)) == NULL) {
07060             result = SNMPERR_MALLOC;
07061             break;
07062         } else {
07063             buf_len = 256;
07064             memset(buf, 0, buf_len);
07065         }
07066 
07067 #ifndef NETSNMP_DISABLE_MIB_LOADING
07068         for (ep = tp ? tp->enums : NULL; ep; ep = ep->next) {
07069             if (ep->value / 8 >= (int) tint) {
07070                 tint = ep->value / 8 + 1;
07071             }
07072         }
07073 #endif /* NETSNMP_DISABLE_MIB_LOADING */
07074 
07075         vp = strdup(value);
07076         for (cp = strtok_r(vp, " ,\t", &st); cp; cp = strtok_r(NULL, " ,\t", &st)) {
07077             int             ix, bit;
07078 
07079             ltmp = strtoul(cp, &ecp, 0);
07080             if (*ecp != 0) {
07081 #ifndef NETSNMP_DISABLE_MIB_LOADING
07082                 for (ep = tp ? tp->enums : NULL; ep != NULL; ep = ep->next) {
07083                     if (strncmp(ep->label, cp, strlen(ep->label)) == 0) {
07084                         break;
07085                     }
07086                 }
07087                 if (ep != NULL) {
07088                     ltmp = ep->value;
07089                 } else {
07090 #endif /* NETSNMP_DISABLE_MIB_LOADING */
07091                     result = SNMPERR_RANGE;   /* ?? or SNMPERR_VALUE; */
07092                     snmp_set_detail(cp);
07093                     SNMP_FREE(buf);
07094                     SNMP_FREE(vp);
07095                     goto out;
07096 #ifndef NETSNMP_DISABLE_MIB_LOADING
07097                 }
07098 #endif /* NETSNMP_DISABLE_MIB_LOADING */
07099             }
07100 
07101             ix = ltmp / 8;
07102             if (ix >= (int) tint) {
07103                 tint = ix + 1;
07104             }
07105             if (ix >= (int)buf_len && !snmp_realloc(&buf, &buf_len)) {
07106                 result = SNMPERR_MALLOC;
07107                 break;
07108             }
07109             bit = 0x80 >> ltmp % 8;
07110             buf[ix] |= bit;
07111             
07112         }
07113         SNMP_FREE(vp);
07114         snmp_pdu_add_variable(pdu, name, name_length, ASN_OCTET_STR,
07115                               buf, tint);
07116         break;
07117 
07118 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
07119     case 'U':
07120         if (read64(&c64tmp, value))
07121             snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_U64,
07122                                   &c64tmp, sizeof(c64tmp));
07123         else
07124             goto fail;
07125         break;
07126 
07127     case 'I':
07128         if (read64(&c64tmp, value))
07129             snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_I64,
07130                                   &c64tmp, sizeof(c64tmp));
07131         else
07132             goto fail;
07133         break;
07134 
07135     case 'F':
07136         if (sscanf(value, "%f", &ftmp) == 1)
07137             snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_FLOAT,
07138                                   &ftmp, sizeof(ftmp));
07139         else
07140             goto fail;
07141         break;
07142 
07143     case 'D':
07144         if (sscanf(value, "%lf", &dtmp) == 1)
07145             snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_DOUBLE,
07146                                   &dtmp, sizeof(dtmp));
07147         else
07148             goto fail;
07149         break;
07150 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
07151 
07152     default:
07153         result = SNMPERR_VAR_TYPE;
07154         buf = (u_char *)calloc(1, 4);
07155         if (buf != NULL) {
07156             sprintf((char *)buf, "\"%c\"", type);
07157             snmp_set_detail((char *)buf);
07158         }
07159         break;
07160     }
07161 
07162     SNMP_FREE(buf);
07163     SET_SNMP_ERROR(result);
07164     return result;
07165 
07166 #ifndef NETSNMP_DISABLE_MIB_LOADING
07167   type_error:
07168     {
07169         char            error_msg[256];
07170         char            undef_msg[32];
07171         const char     *var_type;
07172         switch (tp->type) {
07173         case TYPE_OBJID:
07174             var_type = "OBJECT IDENTIFIER";
07175             break;
07176         case TYPE_OCTETSTR:
07177             var_type = "OCTET STRING";
07178             break;
07179         case TYPE_INTEGER:
07180             var_type = "INTEGER";
07181             break;
07182         case TYPE_NETADDR:
07183             var_type = "NetworkAddress";
07184             break;
07185         case TYPE_IPADDR:
07186             var_type = "IpAddress";
07187             break;
07188         case TYPE_COUNTER:
07189             var_type = "Counter32";
07190             break;
07191         case TYPE_GAUGE:
07192             var_type = "Gauge32";
07193             break;
07194         case TYPE_TIMETICKS:
07195             var_type = "Timeticks";
07196             break;
07197         case TYPE_OPAQUE:
07198             var_type = "Opaque";
07199             break;
07200         case TYPE_NULL:
07201             var_type = "Null";
07202             break;
07203         case TYPE_COUNTER64:
07204             var_type = "Counter64";
07205             break;
07206         case TYPE_BITSTRING:
07207             var_type = "BITS";
07208             break;
07209         case TYPE_NSAPADDRESS:
07210             var_type = "NsapAddress";
07211             break;
07212         case TYPE_UINTEGER:
07213             var_type = "UInteger";
07214             break;
07215         case TYPE_UNSIGNED32:
07216             var_type = "Unsigned32";
07217             break;
07218         case TYPE_INTEGER32:
07219             var_type = "Integer32";
07220             break;
07221         default:
07222             sprintf(undef_msg, "TYPE_%d", tp->type);
07223             var_type = undef_msg;
07224         }
07225         snprintf(error_msg, sizeof(error_msg),
07226                "Type of attribute is %s, not %s", var_type, value);
07227         error_msg[ sizeof(error_msg)-1 ] = 0;
07228         result = SNMPERR_VAR_TYPE;
07229         snmp_set_detail(error_msg);
07230         goto out;
07231     }
07232 #endif /* NETSNMP_DISABLE_MIB_LOADING */
07233   fail:
07234     result = SNMPERR_VALUE;
07235     snmp_set_detail(value);
07236   out:
07237     SET_SNMP_ERROR(result);
07238     return result;
07239 }
07240 
07241 /*
07242  * returns NULL or internal pointer to session
07243  * use this pointer for the other snmp_sess* routines,
07244  * which guarantee action will occur ONLY for this given session.
07245  */
07246 void           *
07247 snmp_sess_pointer(netsnmp_session * session)
07248 {
07249     struct session_list *slp;
07250 
07251     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
07252     for (slp = Sessions; slp; slp = slp->next) {
07253         if (slp->session == session) {
07254             break;
07255         }
07256     }
07257     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
07258 
07259     if (slp == NULL) {
07260         snmp_errno = SNMPERR_BAD_SESSION;       /*MTCRITICAL_RESOURCE */
07261         return (NULL);
07262     }
07263     return ((void *) slp);
07264 }
07265 
07266 /*
07267  * Input : an opaque pointer, returned by snmp_sess_open.
07268  * returns NULL or pointer to session.
07269  */
07270 netsnmp_session *
07271 snmp_sess_session(void *sessp)
07272 {
07273     struct session_list *slp = (struct session_list *) sessp;
07274     if (slp == NULL)
07275         return (NULL);
07276     return (slp->session);
07277 }
07278 
07288 netsnmp_session *
07289 snmp_sess_session_lookup(void *sessp)
07290 {
07291     struct session_list *slp;
07292 
07293     snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
07294     for (slp = Sessions; slp; slp = slp->next) {
07295         if (slp == sessp) {
07296             break;
07297         }
07298     }
07299     snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
07300 
07301     return (netsnmp_session *)slp;
07302 }
07303 
07304 
07305 /*
07306  * snmp_sess_transport: takes an opaque pointer (as returned by
07307  * snmp_sess_open or snmp_sess_pointer) and returns the corresponding
07308  * netsnmp_transport pointer (or NULL if the opaque pointer does not correspond
07309  * to an active internal session).  
07310  */
07311 
07312 netsnmp_transport *
07313 snmp_sess_transport(void *sessp)
07314 {
07315     struct session_list *slp = (struct session_list *) sessp;
07316     if (slp == NULL) {
07317         return NULL;
07318     } else {
07319         return slp->transport;
07320     }
07321 }
07322 
07323 
07324 
07325 /*
07326  * snmp_sess_transport_set: set the transport pointer for the opaque
07327  * session pointer sp.  
07328  */
07329 
07330 void
07331 snmp_sess_transport_set(void *sp, netsnmp_transport *t)
07332 {
07333     struct session_list *slp = (struct session_list *) sp;
07334     if (slp != NULL) {
07335         slp->transport = t;
07336     }
07337 }
07338 
07339 
07340 /*
07341  * snmp_duplicate_objid: duplicates (mallocs) an objid based on the
07342  * input objid 
07343  */
07344 oid            *
07345 snmp_duplicate_objid(const oid * objToCopy, size_t objToCopyLen)
07346 {
07347     oid            *returnOid;
07348     if (objToCopy != NULL && objToCopyLen != 0) {
07349         returnOid = (oid *) malloc(objToCopyLen * sizeof(oid));
07350         if (returnOid) {
07351             memcpy(returnOid, objToCopy, objToCopyLen * sizeof(oid));
07352         }
07353     } else
07354         returnOid = NULL;
07355     return returnOid;
07356 }
07357 
07358 #ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
07359 /*
07360  * generic statistics counter functions 
07361  */
07362 static u_int    statistics[NETSNMP_STAT_MAX_STATS];
07363 
07364 u_int
07365 snmp_increment_statistic(int which)
07366 {
07367     if (which >= 0 && which < NETSNMP_STAT_MAX_STATS) {
07368         statistics[which]++;
07369         return statistics[which];
07370     }
07371     return 0;
07372 }
07373 
07374 u_int
07375 snmp_increment_statistic_by(int which, int count)
07376 {
07377     if (which >= 0 && which < NETSNMP_STAT_MAX_STATS) {
07378         statistics[which] += count;
07379         return statistics[which];
07380     }
07381     return 0;
07382 }
07383 
07384 u_int
07385 snmp_get_statistic(int which)
07386 {
07387     if (which >= 0 && which < NETSNMP_STAT_MAX_STATS)
07388         return statistics[which];
07389     return 0;
07390 }
07391 
07392 void
07393 snmp_init_statistics(void)
07394 {
07395     memset(statistics, 0, sizeof(statistics));
07396 }
07398 #endif /* NETSNMP_FEATURE_REMOVE_STATISTICS */