1 /* $OpenBSD: util.c,v 1.9 2019/10/24 12:39:27 tb Exp $ */ 2 /* 3 * Copyright (c) 2014 Bret Stephen Lambert <blambert@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/queue.h> 20 #include <sys/socket.h> 21 22 #include <net/if.h> 23 24 #include <ber.h> 25 #include <stdio.h> 26 #include <string.h> 27 #include <netdb.h> 28 #include <event.h> 29 30 #include "snmp.h" 31 #include "snmpd.h" 32 33 /* 34 * Convert variable bindings from AgentX to SNMP dialect. 35 */ 36 int 37 varbind_convert(struct agentx_pdu *pdu, struct agentx_varbind_hdr *vbhdr, 38 struct ber_element **varbind, struct ber_element **iter) 39 { 40 struct ber_oid oid; 41 u_int32_t d; 42 u_int64_t l; 43 int slen; 44 char *str; 45 struct ber_element *a; 46 int ret = AGENTX_ERR_NONE; 47 48 if (snmp_agentx_read_oid(pdu, (struct snmp_oid *)&oid) == -1) { 49 ret = AGENTX_ERR_PARSE_ERROR; 50 goto done; 51 } 52 53 *iter = ober_add_sequence(*iter); 54 if (*varbind == NULL) 55 *varbind = *iter; 56 57 a = ober_add_oid(*iter, &oid); 58 59 switch (vbhdr->type) { 60 case AGENTX_NO_SUCH_OBJECT: 61 case AGENTX_NO_SUCH_INSTANCE: 62 case AGENTX_END_OF_MIB_VIEW: 63 case AGENTX_NULL: 64 a = ober_add_null(a); 65 break; 66 67 case AGENTX_IP_ADDRESS: 68 case AGENTX_OPAQUE: 69 case AGENTX_OCTET_STRING: 70 str = snmp_agentx_read_octetstr(pdu, &slen); 71 if (str == NULL) { 72 ret = AGENTX_ERR_PARSE_ERROR; 73 goto done; 74 } 75 a = ober_add_nstring(a, str, slen); 76 break; 77 78 case AGENTX_OBJECT_IDENTIFIER: 79 if (snmp_agentx_read_oid(pdu, 80 (struct snmp_oid *)&oid) == -1) { 81 ret = AGENTX_ERR_PARSE_ERROR; 82 goto done; 83 } 84 a = ober_add_oid(a, &oid); 85 break; 86 87 case AGENTX_INTEGER: 88 case AGENTX_COUNTER32: 89 case AGENTX_GAUGE32: 90 case AGENTX_TIME_TICKS: 91 if (snmp_agentx_read_int(pdu, &d) == -1) { 92 ret = AGENTX_ERR_PARSE_ERROR; 93 goto done; 94 } 95 a = ober_add_integer(a, d); 96 break; 97 98 case AGENTX_COUNTER64: 99 if (snmp_agentx_read_int64(pdu, &l) == -1) { 100 ret = AGENTX_ERR_PARSE_ERROR; 101 goto done; 102 } 103 a = ober_add_integer(a, l); 104 break; 105 106 default: 107 log_debug("unknown data type '%i'", vbhdr->type); 108 ret = AGENTX_ERR_PARSE_ERROR; 109 goto done; 110 } 111 112 /* AgentX types correspond to BER types */ 113 switch (vbhdr->type) { 114 case BER_TYPE_INTEGER: 115 case BER_TYPE_BITSTRING: 116 case BER_TYPE_OCTETSTRING: 117 case BER_TYPE_NULL: 118 case BER_TYPE_OBJECT: 119 /* universal types */ 120 break; 121 122 /* Convert AgentX error types to SNMP error types */ 123 case AGENTX_NO_SUCH_OBJECT: 124 ober_set_header(a, BER_CLASS_CONTEXT, 0); 125 break; 126 case AGENTX_NO_SUCH_INSTANCE: 127 ober_set_header(a, BER_CLASS_CONTEXT, 1); 128 break; 129 130 case AGENTX_COUNTER32: 131 ober_set_header(a, BER_CLASS_APPLICATION, SNMP_COUNTER32); 132 break; 133 134 case AGENTX_GAUGE32: 135 ober_set_header(a, BER_CLASS_APPLICATION, SNMP_GAUGE32); 136 break; 137 138 case AGENTX_COUNTER64: 139 ober_set_header(a, BER_CLASS_APPLICATION, SNMP_COUNTER64); 140 break; 141 142 case AGENTX_IP_ADDRESS: 143 /* application 0 implicit 4-byte octet string per SNMPv2-SMI */ 144 break; 145 146 default: 147 /* application-specific types */ 148 ober_set_header(a, BER_CLASS_APPLICATION, vbhdr->type); 149 break; 150 } 151 done: 152 return (ret); 153 } 154 155 ssize_t 156 sendtofrom(int s, void *buf, size_t len, int flags, struct sockaddr *to, 157 socklen_t tolen, struct sockaddr *from, socklen_t fromlen) 158 { 159 struct iovec iov; 160 struct msghdr msg; 161 struct cmsghdr *cmsg; 162 struct in6_pktinfo *pkt6; 163 struct sockaddr_in *in; 164 struct sockaddr_in6 *in6; 165 union { 166 struct cmsghdr hdr; 167 char inbuf[CMSG_SPACE(sizeof(struct in_addr))]; 168 char in6buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; 169 } cmsgbuf; 170 171 bzero(&msg, sizeof(msg)); 172 bzero(&cmsgbuf, sizeof(cmsgbuf)); 173 174 iov.iov_base = buf; 175 iov.iov_len = len; 176 msg.msg_iov = &iov; 177 msg.msg_iovlen = 1; 178 msg.msg_name = to; 179 msg.msg_namelen = tolen; 180 msg.msg_control = &cmsgbuf; 181 msg.msg_controllen = sizeof(cmsgbuf); 182 183 cmsg = CMSG_FIRSTHDR(&msg); 184 switch (to->sa_family) { 185 case AF_INET: 186 msg.msg_controllen = sizeof(cmsgbuf.inbuf); 187 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 188 cmsg->cmsg_level = IPPROTO_IP; 189 cmsg->cmsg_type = IP_SENDSRCADDR; 190 in = (struct sockaddr_in *)from; 191 memcpy(CMSG_DATA(cmsg), &in->sin_addr, sizeof(struct in_addr)); 192 break; 193 case AF_INET6: 194 msg.msg_controllen = sizeof(cmsgbuf.in6buf); 195 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 196 cmsg->cmsg_level = IPPROTO_IPV6; 197 cmsg->cmsg_type = IPV6_PKTINFO; 198 in6 = (struct sockaddr_in6 *)from; 199 pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); 200 pkt6->ipi6_addr = in6->sin6_addr; 201 break; 202 } 203 204 return sendmsg(s, &msg, flags); 205 } 206 207 ssize_t 208 recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from, 209 socklen_t *fromlen, struct sockaddr *to, socklen_t *tolen) 210 { 211 struct iovec iov; 212 struct msghdr msg; 213 struct cmsghdr *cmsg; 214 struct in6_pktinfo *pkt6; 215 struct sockaddr_in *in; 216 struct sockaddr_in6 *in6; 217 ssize_t ret; 218 union { 219 struct cmsghdr hdr; 220 char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))]; 221 } cmsgbuf; 222 223 bzero(&msg, sizeof(msg)); 224 bzero(&cmsgbuf.buf, sizeof(cmsgbuf.buf)); 225 226 iov.iov_base = buf; 227 iov.iov_len = len; 228 msg.msg_iov = &iov; 229 msg.msg_iovlen = 1; 230 msg.msg_name = from; 231 msg.msg_namelen = *fromlen; 232 msg.msg_control = &cmsgbuf.buf; 233 msg.msg_controllen = sizeof(cmsgbuf.buf); 234 235 if ((ret = recvmsg(s, &msg, flags)) == -1) 236 return (-1); 237 238 *fromlen = from->sa_len; 239 *tolen = 0; 240 241 if (getsockname(s, to, tolen) != 0) 242 *tolen = 0; 243 244 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 245 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 246 switch (from->sa_family) { 247 case AF_INET: 248 if (cmsg->cmsg_level == IPPROTO_IP && 249 cmsg->cmsg_type == IP_RECVDSTADDR) { 250 in = (struct sockaddr_in *)to; 251 in->sin_family = AF_INET; 252 in->sin_len = *tolen = sizeof(*in); 253 memcpy(&in->sin_addr, CMSG_DATA(cmsg), 254 sizeof(struct in_addr)); 255 } 256 break; 257 case AF_INET6: 258 if (cmsg->cmsg_level == IPPROTO_IPV6 && 259 cmsg->cmsg_type == IPV6_PKTINFO) { 260 in6 = (struct sockaddr_in6 *)to; 261 in6->sin6_family = AF_INET6; 262 in6->sin6_len = *tolen = sizeof(*in6); 263 pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); 264 memcpy(&in6->sin6_addr, &pkt6->ipi6_addr, 265 sizeof(struct in6_addr)); 266 if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) 267 in6->sin6_scope_id = 268 pkt6->ipi6_ifindex; 269 } 270 break; 271 } 272 } 273 274 return (ret); 275 } 276 277 void 278 print_debug(const char *emsg, ...) 279 { 280 va_list ap; 281 282 if (log_getverbose() > 2) { 283 va_start(ap, emsg); 284 vfprintf(stderr, emsg, ap); 285 va_end(ap); 286 } 287 } 288 289 void 290 print_verbose(const char *emsg, ...) 291 { 292 va_list ap; 293 294 if (log_getverbose()) { 295 va_start(ap, emsg); 296 vfprintf(stderr, emsg, ap); 297 va_end(ap); 298 } 299 } 300 301 const char * 302 log_in6addr(const struct in6_addr *addr) 303 { 304 static char buf[NI_MAXHOST]; 305 struct sockaddr_in6 sa_in6; 306 u_int16_t tmp16; 307 308 bzero(&sa_in6, sizeof(sa_in6)); 309 sa_in6.sin6_len = sizeof(sa_in6); 310 sa_in6.sin6_family = AF_INET6; 311 memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); 312 313 /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */ 314 if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) || 315 IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) { 316 memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16)); 317 sa_in6.sin6_scope_id = ntohs(tmp16); 318 sa_in6.sin6_addr.s6_addr[2] = 0; 319 sa_in6.sin6_addr.s6_addr[3] = 0; 320 } 321 322 return (print_host((struct sockaddr_storage *)&sa_in6, buf, 323 NI_MAXHOST)); 324 } 325 326 const char * 327 print_host(struct sockaddr_storage *ss, char *buf, size_t len) 328 { 329 if (getnameinfo((struct sockaddr *)ss, ss->ss_len, 330 buf, len, NULL, 0, NI_NUMERICHOST) != 0) { 331 buf[0] = '\0'; 332 return (NULL); 333 } 334 return (buf); 335 } 336