xref: /openbsd/usr.sbin/snmpd/util.c (revision 274d7c50)
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