xref: /openbsd/usr.sbin/snmpd/util.c (revision 76d0caae)
1 /*	$OpenBSD: util.c,v 1.12 2021/08/09 18:14:53 martijn 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 <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <netdb.h>
30 #include <event.h>
31 
32 #include "snmp.h"
33 #include "snmpd.h"
34 
35 ssize_t
36 sendtofrom(int s, void *buf, size_t len, int flags, struct sockaddr *to,
37     socklen_t tolen, struct sockaddr *from, socklen_t fromlen)
38 {
39 	struct iovec		 iov;
40 	struct msghdr		 msg;
41 	struct cmsghdr		*cmsg;
42 	struct in6_pktinfo	*pkt6;
43 	struct sockaddr_in	*in;
44 	struct sockaddr_in6	*in6;
45 	union {
46 		struct cmsghdr	hdr;
47 		char		inbuf[CMSG_SPACE(sizeof(struct in_addr))];
48 		char		in6buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
49 	} cmsgbuf;
50 
51 	bzero(&msg, sizeof(msg));
52 	bzero(&cmsgbuf, sizeof(cmsgbuf));
53 
54 	iov.iov_base = buf;
55 	iov.iov_len = len;
56 	msg.msg_iov = &iov;
57 	msg.msg_iovlen = 1;
58 	msg.msg_name = to;
59 	msg.msg_namelen = tolen;
60 	msg.msg_control = &cmsgbuf;
61 	msg.msg_controllen = sizeof(cmsgbuf);
62 
63 	cmsg = CMSG_FIRSTHDR(&msg);
64 	switch (to->sa_family) {
65 	case AF_INET:
66 		msg.msg_controllen = sizeof(cmsgbuf.inbuf);
67 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
68 		cmsg->cmsg_level = IPPROTO_IP;
69 		cmsg->cmsg_type = IP_SENDSRCADDR;
70 		in = (struct sockaddr_in *)from;
71 		memcpy(CMSG_DATA(cmsg), &in->sin_addr, sizeof(struct in_addr));
72 		break;
73 	case AF_INET6:
74 		msg.msg_controllen = sizeof(cmsgbuf.in6buf);
75 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
76 		cmsg->cmsg_level = IPPROTO_IPV6;
77 		cmsg->cmsg_type = IPV6_PKTINFO;
78 		in6 = (struct sockaddr_in6 *)from;
79 		pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
80 		pkt6->ipi6_addr = in6->sin6_addr;
81 		break;
82 	}
83 
84 	return sendmsg(s, &msg, flags);
85 }
86 
87 ssize_t
88 recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from,
89     socklen_t *fromlen, struct sockaddr *to, socklen_t *tolen)
90 {
91 	struct iovec		 iov;
92 	struct msghdr		 msg;
93 	struct cmsghdr		*cmsg;
94 	struct in6_pktinfo	*pkt6;
95 	struct sockaddr_in	*in;
96 	struct sockaddr_in6	*in6;
97 	ssize_t			 ret;
98 	union {
99 		struct cmsghdr hdr;
100 		char	buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
101 	} cmsgbuf;
102 
103 	bzero(&msg, sizeof(msg));
104 	bzero(&cmsgbuf.buf, sizeof(cmsgbuf.buf));
105 
106 	iov.iov_base = buf;
107 	iov.iov_len = len;
108 	msg.msg_iov = &iov;
109 	msg.msg_iovlen = 1;
110 	msg.msg_name = from;
111 	msg.msg_namelen = *fromlen;
112 	msg.msg_control = &cmsgbuf.buf;
113 	msg.msg_controllen = sizeof(cmsgbuf.buf);
114 
115 	if ((ret = recvmsg(s, &msg, flags)) == -1)
116 		return (-1);
117 
118 	*fromlen = from->sa_len;
119 	*tolen = 0;
120 
121 	if (getsockname(s, to, tolen) != 0)
122 		*tolen = 0;
123 
124 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
125 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
126 		switch (from->sa_family) {
127 		case AF_INET:
128 			if (cmsg->cmsg_level == IPPROTO_IP &&
129 			    cmsg->cmsg_type == IP_RECVDSTADDR) {
130 				in = (struct sockaddr_in *)to;
131 				in->sin_family = AF_INET;
132 				in->sin_len = *tolen = sizeof(*in);
133 				memcpy(&in->sin_addr, CMSG_DATA(cmsg),
134 				    sizeof(struct in_addr));
135 			}
136 			break;
137 		case AF_INET6:
138 			if (cmsg->cmsg_level == IPPROTO_IPV6 &&
139 			    cmsg->cmsg_type == IPV6_PKTINFO) {
140 				in6 = (struct sockaddr_in6 *)to;
141 				in6->sin6_family = AF_INET6;
142 				in6->sin6_len = *tolen = sizeof(*in6);
143 				pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
144 				memcpy(&in6->sin6_addr, &pkt6->ipi6_addr,
145 				    sizeof(struct in6_addr));
146 				if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
147 					in6->sin6_scope_id =
148 					    pkt6->ipi6_ifindex;
149 			}
150 			break;
151 		}
152 	}
153 
154 	return (ret);
155 }
156 
157 const char *
158 log_in6addr(const struct in6_addr *addr)
159 {
160 	static char		buf[NI_MAXHOST];
161 	struct sockaddr_in6	sa_in6;
162 	u_int16_t		tmp16;
163 
164 	bzero(&sa_in6, sizeof(sa_in6));
165 	sa_in6.sin6_len = sizeof(sa_in6);
166 	sa_in6.sin6_family = AF_INET6;
167 	memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
168 
169 	/* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
170 	if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
171 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) {
172 		memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
173 		sa_in6.sin6_scope_id = ntohs(tmp16);
174 		sa_in6.sin6_addr.s6_addr[2] = 0;
175 		sa_in6.sin6_addr.s6_addr[3] = 0;
176 	}
177 
178 	return (print_host((struct sockaddr_storage *)&sa_in6, buf,
179 	    NI_MAXHOST));
180 }
181 
182 const char *
183 print_host(struct sockaddr_storage *ss, char *buf, size_t len)
184 {
185 	if (getnameinfo((struct sockaddr *)ss, ss->ss_len,
186 	    buf, len, NULL, 0, NI_NUMERICHOST) != 0) {
187 		buf[0] = '\0';
188 		return (NULL);
189 	}
190 	return (buf);
191 }
192 
193 char *
194 tohexstr(uint8_t *bstr, int len)
195 {
196 #define MAXHEXSTRLEN		256
197 	static char hstr[2 * MAXHEXSTRLEN + 1];
198 	static const char hex[] = "0123456789abcdef";
199 	int i;
200 
201 	if (len > MAXHEXSTRLEN)
202 		len = MAXHEXSTRLEN;	/* truncate */
203 	for (i = 0; i < len; i++) {
204 		hstr[i + i] = hex[bstr[i] >> 4];
205 		hstr[i + i + 1] = hex[bstr[i] & 0x0f];
206 	}
207 	hstr[i + i] = '\0';
208 	return hstr;
209 }
210 
211 uint8_t *
212 fromhexstr(uint8_t *bstr, const char *hstr, size_t len)
213 {
214 	size_t i;
215 	char hex[3];
216 
217 	if (len % 2 != 0)
218 		return NULL;
219 
220 	hex[2] = '\0';
221 	for (i = 0; i < len; i += 2) {
222 		if (!isxdigit(hstr[i]) || !isxdigit(hstr[i + 1]))
223 			return NULL;
224 		hex[0] = hstr[i];
225 		hex[1] = hstr[i + 1];
226 		bstr[i / 2] = strtol(hex, NULL, 16);
227 	}
228 
229 	return bstr;
230 }
231