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