Annotation of src/usr.bin/snmp/snmp.c, Revision 1.1
1.1 ! martijn 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
! 5: * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
! 6: *
! 7: * Permission to use, copy, modify, and distribute this software for any
! 8: * purpose with or without fee is hereby granted, provided that the above
! 9: * copyright notice and this permission notice appear in all copies.
! 10: *
! 11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 18: */
! 19:
! 20: #include <sys/socket.h>
! 21:
! 22: #include <errno.h>
! 23: #include <poll.h>
! 24: #include <stdlib.h>
! 25: #include <string.h>
! 26: #include <stdio.h>
! 27: #include <time.h>
! 28:
! 29: #include "ber.h"
! 30: #include "smi.h"
! 31: #include "snmp.h"
! 32:
! 33: static struct ber_element *
! 34: snmp_resolve(struct snmp_agent *, struct ber_element *, int);
! 35:
! 36: struct snmp_agent *
! 37: snmp_connect_v12(int fd, enum snmp_version version, const char *community)
! 38: {
! 39: struct snmp_agent *agent;
! 40:
! 41: if (version != SNMP_V1 && version != SNMP_V2C) {
! 42: errno = EINVAL;
! 43: return NULL;
! 44: }
! 45: if ((agent = malloc(sizeof(*agent))) == NULL)
! 46: return NULL;
! 47: agent->fd = fd;
! 48: agent->version = version;
! 49: if ((agent->community = strdup(community)) == NULL)
! 50: goto fail;
! 51: agent->timeout = 1;
! 52: agent->retries = 5;
! 53: return agent;
! 54:
! 55: fail:
! 56: free(agent->community);
! 57: free(agent);
! 58: return NULL;
! 59: }
! 60:
! 61: void
! 62: snmp_free_agent(struct snmp_agent *agent)
! 63: {
! 64: free(agent->community);
! 65: free(agent);
! 66: }
! 67:
! 68: struct ber_element *
! 69: snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
! 70: {
! 71: struct ber_element *pdu, *varbind;
! 72: size_t i;
! 73:
! 74: if ((pdu = ber_add_sequence(NULL)) == NULL)
! 75: return NULL;
! 76: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
! 77: SNMP_C_GETREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
! 78: goto fail;
! 79: for (i = 0; i < len; i++)
! 80: varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
! 81: if (varbind == NULL)
! 82: goto fail;
! 83:
! 84: return snmp_resolve(agent, pdu, 1);
! 85: fail:
! 86: ber_free_elements(pdu);
! 87: return NULL;
! 88: }
! 89:
! 90: struct ber_element *
! 91: snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
! 92: {
! 93: struct ber_element *pdu, *varbind;
! 94: size_t i;
! 95:
! 96: if ((pdu = ber_add_sequence(NULL)) == NULL)
! 97: return NULL;
! 98: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
! 99: SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
! 100: goto fail;
! 101: for (i = 0; i < len; i++)
! 102: varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
! 103: if (varbind == NULL)
! 104: goto fail;
! 105:
! 106: return snmp_resolve(agent, pdu, 1);
! 107: fail:
! 108: ber_free_elements(pdu);
! 109: return NULL;
! 110: }
! 111:
! 112: int
! 113: snmp_trap(struct snmp_agent *agent, struct timespec *uptime,
! 114: struct ber_oid *oid, struct ber_element *custvarbind)
! 115: {
! 116: struct ber_element *pdu, *varbind;
! 117: struct ber_oid sysuptime, trap;
! 118: long long ticks;
! 119:
! 120: if ((pdu = ber_add_sequence(NULL)) == NULL)
! 121: return -1;
! 122: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
! 123: SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL)
! 124: goto fail;
! 125:
! 126: ticks = uptime->tv_sec * 100;
! 127: ticks += uptime->tv_nsec / 10000000;
! 128: if (smi_string2oid("sysUpTime.0", &sysuptime) == -1)
! 129: goto fail;
! 130: if ((varbind = ber_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
! 131: BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
! 132: goto fail;
! 133: if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
! 134: goto fail;
! 135: if ((varbind = ber_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
! 136: goto fail;
! 137: if (custvarbind != NULL)
! 138: ber_link_elements(varbind, custvarbind);
! 139:
! 140: snmp_resolve(agent, pdu, 0);
! 141: return 0;
! 142: fail:
! 143: ber_free_elements(pdu);
! 144: return -1;
! 145: }
! 146:
! 147: struct ber_element *
! 148: snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len,
! 149: int non_repeaters, int max_repetitions)
! 150: {
! 151: struct ber_element *pdu, *varbind;
! 152: size_t i;
! 153:
! 154: if ((pdu = ber_add_sequence(NULL)) == NULL)
! 155: return NULL;
! 156: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
! 157: SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
! 158: max_repetitions)) == NULL)
! 159: goto fail;
! 160: for (i = 0; i < len; i++)
! 161: varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
! 162: if (varbind == NULL)
! 163: goto fail;
! 164:
! 165: return snmp_resolve(agent, pdu, 1);
! 166: fail:
! 167: ber_free_elements(pdu);
! 168: return NULL;
! 169: }
! 170:
! 171: static struct ber_element *
! 172: snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
! 173: {
! 174: struct ber_element *message, *varbind;
! 175: struct ber_oid oid;
! 176: struct timespec start, now;
! 177: struct pollfd pfd;
! 178: struct ber ber;
! 179: ssize_t len;
! 180: long long reqid, rreqid;
! 181: long long version;
! 182: char *community;
! 183: short direction;
! 184: int to, nfds, ret;
! 185: int tries;
! 186: void *ptr;
! 187: char buf[READ_BUF_SIZE];
! 188:
! 189: if (ber_scanf_elements(pdu, "{i", &reqid) != 0) {
! 190: errno = EINVAL;
! 191: ber_free_elements(pdu);
! 192: return NULL;
! 193: }
! 194:
! 195: if ((message = ber_add_sequence(NULL)) == NULL) {
! 196: ber_free_elements(pdu);
! 197: return NULL;
! 198: }
! 199: if (ber_printf_elements(message, "dse", agent->version,
! 200: agent->community, pdu) == NULL) {
! 201: ber_free_elements(pdu);
! 202: ber_free_elements(message);
! 203: return NULL;
! 204: }
! 205: memset(&ber, 0, sizeof(ber));
! 206: ber_set_application(&ber, smi_application);
! 207: len = ber_write_elements(&ber, message);
! 208: ber_free_elements(message);
! 209: message = NULL;
! 210: if (ber_get_writebuf(&ber, &ptr) < 1)
! 211: goto fail;
! 212:
! 213: clock_gettime(CLOCK_MONOTONIC, &start);
! 214: memcpy(&now, &start, sizeof(now));
! 215: direction = POLLOUT;
! 216: tries = agent->retries + 1;
! 217: while (tries) {
! 218: pfd.fd = agent->fd;
! 219: pfd.events = direction;
! 220: if (agent->timeout > 0) {
! 221: to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000;
! 222: to -= (now.tv_nsec - start.tv_nsec) / 1000000;
! 223: } else
! 224: to = INFTIM;
! 225: nfds = poll(&pfd, 1, to);
! 226: if (nfds == 0) {
! 227: errno = ETIMEDOUT;
! 228: direction = POLLOUT;
! 229: tries--;
! 230: continue;
! 231: }
! 232: if (nfds == -1) {
! 233: if (errno == EINTR)
! 234: continue;
! 235: else
! 236: goto fail;
! 237: }
! 238: if (direction == POLLOUT) {
! 239: ret = send(agent->fd, ptr, len, MSG_DONTWAIT);
! 240: if (ret == -1)
! 241: goto fail;
! 242: if (ret < len) {
! 243: errno = EBADMSG;
! 244: goto fail;
! 245: }
! 246: if (!reply)
! 247: return NULL;
! 248: direction = POLLIN;
! 249: continue;
! 250: }
! 251: ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT);
! 252: if (ret == 0)
! 253: errno = ECONNRESET;
! 254: if (ret <= 0)
! 255: goto fail;
! 256: ber_set_readbuf(&ber, buf, ret);
! 257: if ((message = ber_read_elements(&ber, NULL)) == NULL)
! 258: goto fail;
! 259: if (ber_scanf_elements(message, "{ise", &version, &community,
! 260: &pdu) != 0)
! 261: goto fail;
! 262: /* Skip invalid packets; should not happen */
! 263: if (version != agent->version ||
! 264: strcmp(community, agent->community) != 0)
! 265: continue;
! 266: /* Validate pdu format and check request id */
! 267: if (ber_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
! 268: varbind->be_encoding != BER_TYPE_SEQUENCE)
! 269: goto fail;
! 270: if (rreqid != reqid)
! 271: continue;
! 272: for (varbind = varbind->be_sub; varbind != NULL;
! 273: varbind = varbind->be_next) {
! 274: if (ber_scanf_elements(varbind, "{oS}", &oid) != 0)
! 275: goto fail;
! 276: }
! 277:
! 278: ber_unlink_elements(message->be_sub->be_next);
! 279: ber_free_elements(message);
! 280: ber_free(&ber);
! 281: return pdu;
! 282: }
! 283:
! 284: fail:
! 285: ber_free_elements(message);
! 286: ber_free(&ber);
! 287: return NULL;
! 288: }