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