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