xref: /openbsd-src/usr.sbin/snmpd/util.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: util.c,v 1.13 2022/10/06 14:41:08 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 print_host(struct sockaddr_storage *ss, char *buf, size_t len)
159 {
160 	if (getnameinfo((struct sockaddr *)ss, ss->ss_len,
161 	    buf, len, NULL, 0, NI_NUMERICHOST) != 0) {
162 		buf[0] = '\0';
163 		return (NULL);
164 	}
165 	return (buf);
166 }
167 
168 char *
169 tohexstr(uint8_t *bstr, int len)
170 {
171 #define MAXHEXSTRLEN		256
172 	static char hstr[2 * MAXHEXSTRLEN + 1];
173 	static const char hex[] = "0123456789abcdef";
174 	int i;
175 
176 	if (len > MAXHEXSTRLEN)
177 		len = MAXHEXSTRLEN;	/* truncate */
178 	for (i = 0; i < len; i++) {
179 		hstr[i + i] = hex[bstr[i] >> 4];
180 		hstr[i + i + 1] = hex[bstr[i] & 0x0f];
181 	}
182 	hstr[i + i] = '\0';
183 	return hstr;
184 }
185 
186 uint8_t *
187 fromhexstr(uint8_t *bstr, const char *hstr, size_t len)
188 {
189 	size_t i;
190 	char hex[3];
191 
192 	if (len % 2 != 0)
193 		return NULL;
194 
195 	hex[2] = '\0';
196 	for (i = 0; i < len; i += 2) {
197 		if (!isxdigit(hstr[i]) || !isxdigit(hstr[i + 1]))
198 			return NULL;
199 		hex[0] = hstr[i];
200 		hex[1] = hstr[i + 1];
201 		bstr[i / 2] = strtol(hex, NULL, 16);
202 	}
203 
204 	return bstr;
205 }
206