Mercurial > illumos > onarm
view usr/src/cmd/agents/snmp/snmplib/snmp_api.c @ 13:f60a82e85167 default tip
Revert NEC's changes to fix krb5 build
author | Andrew Stormont <andyjstormont@gmail.com> |
---|---|
date | Fri, 02 Mar 2012 22:25:26 +0000 |
parents | 1a15d5aaf794 |
children |
line wrap: on
line source
/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1998 by Sun Microsystems, Inc. * All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" #include <stdio.h> #include <sys/types.h> #include <sys/param.h> #include <sys/time.h> #include <netinet/in.h> #include <sys/socket.h> #include <netdb.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "snmp_msg.h" #include "snmp_api.h" #include "error.h" /***** GLOBAL VARIABLES *****/ int snmp_errno = 0; /***** LOCAL CONSTANTS *****/ #define DEFAULT_COMMUNITY "public" #define DEFAULT_RETRIES 4 #define DEFAULT_TIMEOUT 1000000L #define DEFAULT_REMPORT SNMP_PORT #define DEFAULT_LOCPORT 0 #define DEFAULT_ENTERPRISE &sun_oid /***** LOCAL TYPES *****/ /* * A list of all the outstanding requests * for a particular session */ typedef struct _SNMP_request_list { struct _SNMP_request_list *next_request; uint32_t request_id; /* request id */ int predefined_id; int retries; /* Number of retries */ uint32_t timeout; /* length to wait for timeout in usec */ struct timeval time; /* Time this request was made */ struct timeval expire; /* time this request is due to expire */ SNMP_pdu *pdu; /* The pdu for this request (saved so it can be retransmitted */ } SNMP_request_list; /* * Internal information about the state of the snmp session */ typedef struct _SNMP_internal_session { int sd; /* socket descriptor for this connection */ Address address; /* address of connected peer */ SNMP_request_list *requests; /* Info about outstanding requests */ } SNMP_internal_session; /* * The list of active/open sessions. */ typedef struct _SNMP_session_list { struct _SNMP_session_list *next; SNMP_session *session; SNMP_internal_session *internal; } SNMP_session_list; /***** STATIC VARIABLES *****/ static SNMP_session_list *first_session = NULL; static uint32_t static_request_id = 0; static char *snmp_api_errors[5] = { "System error", "Unknown session", "Unknown host", "Invalid local port", "Unknown Error" }; static char static_error_label[500] = ""; /***** STATIC FUNCTIONS *****/ static char *api_errstring(int snmp_errnumber); /* static init_snmp(); */ static void free_request_list(SNMP_request_list *rp); static int snmp_session_read_loop(fd_set *fdset); static int snmp_session_timeout_loop(); /*******************************************************************/ static char *api_errstring(int snmp_errnumber) { if(snmp_errnumber <= SNMPERR_SYSERR && snmp_errnumber >= SNMPERR_GENERR) { return snmp_api_errors[snmp_errnumber + 5]; } else { return "Unknown Error"; } } /*******************************************************************/ /* * Gets initial request ID for all transactions */ /* static init_snmp() { struct timeval tv; (void)gettimeofday(&tv, (struct timezone *) 0); srandom(tv.tv_sec ^ tv.tv_usec); static_request_id = random(); } */ /*******************************************************************/ SNMP_session *snmp_session_open_default(char *peername, void callback(), void *callback_magic, char *error_label) { return snmp_session_open(peername, NULL, SNMP_DEFAULT_RETRIES, SNMP_DEFAULT_TIMEOUT, callback, callback_magic, error_label); } /*******************************************************************/ SNMP_session *snmp_session_open(char *peername, char *community, int retries, int32_t timeout, void callback(), void *callback_magic, char *error_label) { SNMP_session_list *slp; SNMP_internal_session *isp; SNMP_session *session; char *peername_dup; char *community_dup; u_short remote_port = SNMP_DEFAULT_REMPORT; u_short local_port = SNMP_DEFAULT_LOCPORT; struct sockaddr_in me; IPAddress ip_address; error_label[0] = '\0'; if(peername == NULL) { sprintf(error_label, "BUG: snmp_session_open(): peername is NULL"); return NULL; } if(callback == NULL) { sprintf(error_label, "BUG: snmp_session_open(): callback is NULL"); return NULL; } if(community == SNMP_DEFAULT_COMMUNITY) { community = DEFAULT_COMMUNITY; } if(retries == SNMP_DEFAULT_RETRIES) { retries = DEFAULT_RETRIES; } if(timeout == SNMP_DEFAULT_TIMEOUT) { timeout = DEFAULT_TIMEOUT; } if(remote_port == SNMP_DEFAULT_REMPORT) { remote_port = SNMP_PORT; } if(local_port == SNMP_DEFAULT_LOCPORT) { local_port = DEFAULT_LOCPORT; } if(name_to_ip_address(peername, &ip_address, error_label)) { snmp_errno = SNMPERR_BAD_ADDRESS; return NULL; } /****************************************/ /* 1) allocate the different structures */ /****************************************/ peername_dup = strdup(peername); if(peername_dup == NULL) { sprintf(error_label, ERR_MSG_ALLOC); snmp_errno = SNMPERR_GENERR; return NULL; } community_dup = strdup(community); if(community_dup == NULL) { sprintf(error_label, ERR_MSG_ALLOC); snmp_errno = SNMPERR_GENERR; free(peername_dup); return NULL; } slp = (SNMP_session_list *) malloc(sizeof(SNMP_session_list)); if(slp == NULL) { sprintf(error_label, ERR_MSG_ALLOC); snmp_errno = SNMPERR_GENERR; free(peername_dup); free(community_dup); return NULL; } memset(slp, 0, sizeof(SNMP_session_list)); isp = (SNMP_internal_session *) malloc(sizeof(SNMP_internal_session)); if(isp == NULL) { sprintf(error_label, ERR_MSG_ALLOC); snmp_errno = SNMPERR_GENERR; free(peername_dup); free(community_dup); free(slp); return NULL; } memset(isp, 0, sizeof(SNMP_internal_session)); slp->internal = isp; slp->internal->sd = -1; /* mark it not set */ session = (SNMP_session *) malloc(sizeof(SNMP_session)); if(session == NULL) { sprintf(error_label, ERR_MSG_ALLOC); snmp_errno = SNMPERR_GENERR; free(peername_dup); free(community_dup); free(slp); free(isp); return NULL; } memset(session, 0, sizeof(SNMP_session)); slp->session = session; /*************************************/ /* 2) now link the SNMP_session_list */ /*************************************/ slp->next = first_session; first_session = slp; /***************************************/ /* 3) initialize SNMP_session */ /***************************************/ session->community = community_dup; session->retries = retries; session->timeout = timeout; session->peername = peername_dup; session->remote_port = remote_port; session->local_port = local_port; session->callback = callback; session->callback_magic = callback_magic; /***************************************/ /* 4) initialize SNMP_internal_session */ /***************************************/ /* Set up connections */ isp->sd = socket(AF_INET, SOCK_DGRAM, 0); if(isp->sd < 0) { sprintf(error_label, ERR_MSG_SOCKET, errno_string()); snmp_errno = SNMPERR_SYSERR; if(snmp_session_close(session, static_error_label)) { (void)fprintf(stderr, ERR_MSG_CAN_NOT_ABORT_SESSION, static_error_label, api_errstring(snmp_errno)); exit(1); } return NULL; } /* initialize address */ isp->address.sin_addr.s_addr = ip_address.s_addr; isp->address.sin_family = AF_INET; isp->address.sin_port = session->remote_port; /* byte swap is done in pdu.c */ /* bind */ me.sin_family = AF_INET; me.sin_addr.s_addr = INADDR_ANY; me.sin_port = htons(session->local_port); if(bind(isp->sd, (struct sockaddr *)&me, sizeof(me)) != 0) { sprintf(error_label, ERR_MSG_BIND, errno_string()); snmp_errno = SNMPERR_BAD_LOCPORT; if(snmp_session_close(session, static_error_label)) { (void)fprintf(stderr, ERR_MSG_CAN_NOT_ABORT_SESSION, static_error_label, api_errstring(snmp_errno)); exit(1); } return NULL; } /* request list */ isp->requests = NULL; session->sd = isp->sd; return session; } /*******************************************************************/ /* * Free each element in the input request list. */ static void free_request_list(SNMP_request_list *rp) { SNMP_request_list *orp; while(rp) { orp = rp; rp = rp->next_request; if(orp->pdu != NULL) { snmp_pdu_free(orp->pdu); } free(orp); } return; } /*******************************************************************/ int snmp_session_close(SNMP_session *session, char *error_label) { SNMP_session_list *slp = NULL; SNMP_session_list *oslp = NULL; error_label[0] = '\0'; if(first_session->session == session) { /* If first entry */ slp = first_session; first_session = slp->next; } else { for(slp = first_session; slp; slp = slp->next) { if(slp->session == session) { if(oslp) /* if we found entry that points here */ { oslp->next = slp->next; /* link around this entry */ } break; } oslp = slp; } } /* If we found the session, free all data associated with it */ if(slp) { if(slp->session->community) { free(slp->session->community); } if(slp->session->peername) { free(slp->session->peername); } free(slp->session); if(slp->internal->sd != -1) { if(close(slp->internal->sd) == -1) { (void)fprintf(stderr, "close(%s) failed %s\n", slp->internal->sd, errno_string()); } } free_request_list(slp->internal->requests); free((char *)slp->internal); free((char *)slp); } else { snmp_errno = SNMPERR_BAD_SESSION; return -1; } return 0; } /*******************************************************************/ /* * 1) sends the input pdu on the specified session * 2) if this request is a pdu, add it to the request list * * Upon success, 0 is returned. * On any error, -1 is returned and error_label is set. * * The pdu is freed by snmp_session_send() unless a failure occured. */ int snmp_session_send(SNMP_session *session, int predefined_id, SNMP_pdu *pdu, char *error_label) { SNMP_session_list *slp; SNMP_internal_session *isp = NULL; SNMP_request_list *rp; struct timeval tv; error_label[0] = '\0'; for(slp = first_session; slp; slp = slp->next) { if(slp->session == session) { isp = slp->internal; break; } } if(isp == NULL) { snmp_errno = SNMPERR_BAD_SESSION; return -1; } if(pdu->community == NULL) { pdu->community = strdup(session->community); if(pdu->community == NULL) { sprintf(error_label, ERR_MSG_ALLOC); snmp_errno = SNMPERR_GENERR; return -1; } } if(pdu->type == GET_REQ_MSG || pdu->type == GETNEXT_REQ_MSG || pdu->type == GET_RSP_MSG || pdu->type == SET_REQ_MSG) { pdu->request_id = ++static_request_id; } else { pdu->request_id = 0; } if( (pdu->type == GET_REQ_MSG) || (pdu->type == GETNEXT_REQ_MSG) || (pdu->type == SET_REQ_MSG) ) { /* set up to expect a response */ rp = (SNMP_request_list *) malloc(sizeof(SNMP_request_list)); if(rp == NULL) { sprintf(error_label, ERR_MSG_ALLOC); snmp_errno = SNMPERR_GENERR; return -1; } memset(rp, 0, sizeof(SNMP_request_list)); } (void)gettimeofday(&tv, (struct timezone *) 0); if(snmp_pdu_send(isp->sd, &(isp->address), pdu, error_label)) { snmp_errno = SNMPERR_GENERR; return -1; } if( (pdu->type == GET_REQ_MSG) || (pdu->type == GETNEXT_REQ_MSG) || (pdu->type == SET_REQ_MSG) ) { rp->next_request = isp->requests; isp->requests = rp; rp->pdu = pdu; rp->request_id = pdu->request_id; rp->retries = 1; rp->timeout = session->timeout; rp->predefined_id = predefined_id; rp->time.tv_sec = tv.tv_sec; rp->time.tv_usec = tv.tv_usec; /* printf("%d NOW: %d sec and %d usec\n", rp->retries, tv.tv_sec, tv.tv_usec); */ tv.tv_usec += rp->timeout; tv.tv_sec += tv.tv_usec / 1000000L; tv.tv_usec %= 1000000L; rp->expire.tv_sec = tv.tv_sec; rp->expire.tv_usec = tv.tv_usec; /* printf("%d EXPIRE: %d sec and %d usec\n\n", rp->retries, tv.tv_sec, tv.tv_usec); */ } else { snmp_pdu_free(pdu); } return 0; } /*******************************************************************/ void snmp_session_read(fd_set *fdset) { while(snmp_session_read_loop(fdset)); } /* * We need this function because the user may close the session * in the callback and then corrupt the session list */ static int snmp_session_read_loop(fd_set *fdset) { SNMP_session_list *slp; SNMP_session *sp; SNMP_internal_session *isp; SNMP_pdu *pdu; SNMP_request_list *rp, *orp; for(slp = first_session; slp; slp = slp->next) { if(FD_ISSET(slp->internal->sd, fdset)) { Address address; FD_CLR(slp->internal->sd, fdset); sp = slp->session; isp = slp->internal; pdu = snmp_pdu_receive(isp->sd, &address, static_error_label); if(pdu == NULL) { (void)fprintf(stderr, ERR_MSG_RECEIVED_MANGLED_PACKET, static_error_label); return 0; } if(pdu->type == GET_RSP_MSG) { for(rp = isp->requests; rp; rp = rp->next_request) { if(rp->request_id == pdu->request_id) { /* delete request */ orp = rp; if(isp->requests == orp) { /* first in list */ isp->requests = orp->next_request; } else { for(rp = isp->requests; rp; rp = rp->next_request) { if(rp->next_request == orp) { rp->next_request = orp->next_request; /* link around it */ break; } } } sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, orp->predefined_id, pdu, sp->callback_magic); snmp_pdu_free(orp->pdu); free(orp); /* * Then we should return as soon as possible * because may have closed the session and * corrupted the pointers */ break; } } } else if( (pdu->type == GET_REQ_MSG) || (pdu->type == GETNEXT_REQ_MSG) || (pdu->type == TRP_REQ_MSG) || (pdu->type == SET_REQ_MSG) ) { sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, 0, pdu, sp->callback_magic); /* * Then we should return as soon as possible * because may have closed the session and * corrupted the pointers */ } snmp_pdu_free(pdu); return 1; } } return 0; } void snmp_session_read_2(int fd) { SNMP_session_list *slp; SNMP_session *sp; SNMP_internal_session *isp; SNMP_pdu *pdu; SNMP_request_list *rp, *orp; for(slp = first_session; slp; slp = slp->next) { if(slp->internal->sd == fd) { Address address; sp = slp->session; isp = slp->internal; pdu = snmp_pdu_receive(isp->sd, &address, static_error_label); if(pdu == NULL) { (void)fprintf(stderr, ERR_MSG_RECEIVED_MANGLED_PACKET, static_error_label); return; } if(pdu->type == GET_RSP_MSG) { for(rp = isp->requests; rp; rp = rp->next_request) { if(rp->request_id == pdu->request_id) { /* delete request */ orp = rp; if(isp->requests == orp) { /* first in list */ isp->requests = orp->next_request; } else { for(rp = isp->requests; rp; rp = rp->next_request) { if(rp->next_request == orp) { rp->next_request = orp->next_request; /* link around it */ break; } } } sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, orp->predefined_id, pdu, sp->callback_magic); snmp_pdu_free(orp->pdu); free(orp); /* * Then we should return as soon as possible * because may have closed the session and * corrupted the pointers */ break; } } } else if( (pdu->type == GET_REQ_MSG) || (pdu->type == GETNEXT_REQ_MSG) || (pdu->type == TRP_REQ_MSG) || (pdu->type == SET_REQ_MSG) ) { sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, 0, pdu, sp->callback_magic); /* * Then we should return as soon as possible * because may have closed the session and * corrupted the pointers */ } snmp_pdu_free(pdu); return; } } return; } /*******************************************************************/ int snmp_session_select_info(int *numfds, fd_set *fdset, struct timeval *timeout) { SNMP_session_list *slp; SNMP_internal_session *isp; SNMP_request_list *rp; struct timeval now, earliest; int active = 0, requests = 0; timerclear(&earliest); /* * For each request outstanding, add it's socket to the fdset, * and if it is the earliest timeout to expire, mark it as lowest. */ for(slp = first_session; slp; slp = slp->next) { active++; isp = slp->internal; if((isp->sd + 1) > *numfds) { *numfds = (isp->sd + 1); } FD_SET(isp->sd, fdset); if(isp->requests) { /* found another session with outstanding requests */ for(rp = isp->requests; rp; rp = rp->next_request) { requests++; if(!timerisset(&earliest) || timercmp(&rp->expire, &earliest, <)) { earliest.tv_sec = rp->expire.tv_sec; earliest.tv_usec = rp->expire.tv_usec; } } } } /* printf("NUM REQUESTS: %d\n", requests); */ if(requests == 0) { /* if none are active, skip arithmetic */ return 0; } /* printf("EARLIEST TIMEOUT: %d sec and %d usec\n\n", earliest.tv_sec, earliest.tv_usec); */ /* * Now find out how much time until the earliest timeout. This * transforms earliest from an absolute time into a delta time, the * time left until the select should timeout. */ (void)gettimeofday(&now, (struct timezone *)0); earliest.tv_sec--; /* adjust time to make arithmetic easier */ earliest.tv_usec += 1000000L; earliest.tv_sec -= now.tv_sec; earliest.tv_usec -= now.tv_usec; while(earliest.tv_usec >= 1000000L) { earliest.tv_usec -= 1000000L; earliest.tv_sec += 1; } if(earliest.tv_sec < 0) { earliest.tv_sec = 0; earliest.tv_usec = 0; } if((earliest.tv_sec == 0) && (earliest.tv_usec == 0)) { earliest.tv_sec = 0; earliest.tv_usec = 1; } if(timercmp(&earliest, timeout, <)) { timeout->tv_sec = earliest.tv_sec; timeout->tv_usec = earliest.tv_usec; } else if((timeout->tv_sec == 0) && (timeout->tv_usec == 0)) { timeout->tv_sec = earliest.tv_sec; timeout->tv_usec = earliest.tv_usec; } /* printf("NEW TIMEOUT: %d sec and %d usec\n\n", timeout->tv_sec, timeout->tv_usec); */ return requests; } int snmp_session_itimeout_info(struct itimerval *itimeout) { int numfds = 0; fd_set fdset; FD_ZERO(&fdset); return snmp_session_select_info(&numfds, &fdset, &(itimeout->it_value)); } /*******************************************************************/ /* * It may remain some bugs in this function * because the user may close the session in the callback * and then corrupt the list */ void snmp_session_timeout() { while(snmp_session_timeout_loop()); } static int snmp_session_timeout_loop() { SNMP_session_list *slp; SNMP_session *sp; SNMP_internal_session *isp; SNMP_request_list *rp, *orp; struct timeval now; (void)gettimeofday(&now, (struct timezone *) 0); /* * For each request outstanding, check to see if it has expired. */ for(slp = first_session; slp; slp = slp->next) { sp = slp->session; isp = slp->internal; orp = NULL; for(rp = isp->requests; rp; rp = rp->next_request) { if(timercmp(&rp->expire, &now, <)) { /* this timer has expired */ if (rp->retries >= sp->retries) { /* No more chances, delete this entry */ if(orp == NULL) { isp->requests = rp->next_request; } else { orp->next_request = rp->next_request; } sp->callback(TIMED_OUT, sp, rp->pdu->request_id, rp->predefined_id, rp->pdu, sp->callback_magic); snmp_pdu_free(rp->pdu); free(rp); return 1; } else { /* retransmit this pdu */ struct timeval tv; rp->retries++; rp->timeout <<= 1; (void)gettimeofday(&tv, (struct timezone *) 0); if(snmp_pdu_send(isp->sd, &(isp->address), rp->pdu, static_error_label)) { (void)fprintf(stderr, "snmp_pdu_send() failed: %s\n", static_error_label); } rp->time.tv_sec = tv.tv_sec; rp->time.tv_usec = tv.tv_usec; /* printf("%d NOW: %d sec and %d usec\n", rp->retries, tv.tv_sec, tv.tv_usec); */ tv.tv_usec += rp->timeout; tv.tv_sec += tv.tv_usec / 1000000L; tv.tv_usec %= 1000000L; rp->expire.tv_sec = tv.tv_sec; rp->expire.tv_usec = tv.tv_usec; /* printf("%d EXPIRE: %d sec and %d usec\n\n", rp->retries, tv.tv_sec, tv.tv_usec); */ } } orp = rp; } } return 0; }