/src/net-snmp/snmplib/transports/snmpIPv6BaseDomain.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* IPV6 base transport support functions |
2 | | * |
3 | | * Portions of this file are subject to the following copyright(s). See |
4 | | * the Net-SNMP's COPYING file for more details and other copyrights |
5 | | * that may apply: |
6 | | * |
7 | | * Portions of this file are copyrighted by: |
8 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
9 | | * Use is subject to license terms specified in the COPYING file |
10 | | * distributed with the Net-SNMP package. |
11 | | */ |
12 | | |
13 | | #include <net-snmp/net-snmp-config.h> |
14 | | |
15 | | #ifdef NETSNMP_ENABLE_IPV6 |
16 | | |
17 | | #include <net-snmp/types.h> |
18 | | #include "snmpIPBaseDomain.h" |
19 | | #include <net-snmp/library/snmpIPv6BaseDomain.h> |
20 | | #include <net-snmp/library/system.h> |
21 | | #include <net-snmp/library/snmp_assert.h> |
22 | | |
23 | | #include <stddef.h> |
24 | | #include <stdio.h> |
25 | | #include <sys/types.h> |
26 | | #include <ctype.h> |
27 | | #ifdef HAVE_STDLIB_H |
28 | | #include <stdlib.h> |
29 | | #endif |
30 | | #ifdef HAVE_STRING_H |
31 | | #include <string.h> |
32 | | #else |
33 | | #include <strings.h> |
34 | | #endif |
35 | | #ifdef HAVE_SYS_SOCKET_H |
36 | | #include <sys/socket.h> |
37 | | #endif |
38 | | #ifdef HAVE_NETINET_IN_H |
39 | | #include <netinet/in.h> |
40 | | #endif |
41 | | #ifdef HAVE_ARPA_INET_H |
42 | | #include <arpa/inet.h> |
43 | | #endif |
44 | | #ifdef HAVE_NETDB_H |
45 | | #include <netdb.h> |
46 | | #endif |
47 | | #ifdef HAVE_NET_IF_H |
48 | | #include <net/if.h> |
49 | | #endif |
50 | | |
51 | | #include <net-snmp/types.h> |
52 | | #include <net-snmp/library/snmp.h> |
53 | | #include <net-snmp/library/snmp_debug.h> |
54 | | #include <net-snmp/library/default_store.h> |
55 | | #include <net-snmp/library/snmp_logging.h> |
56 | | |
57 | | #include "inet_ntop.h" |
58 | | #include "inet_pton.h" |
59 | | |
60 | | |
61 | | #if defined(WIN32) && !defined(IF_NAMESIZE) |
62 | | #define IF_NAMESIZE 12 |
63 | | #endif |
64 | | |
65 | | |
66 | | #if defined(HAVE_WINSOCK_H) && !defined(mingw32) |
67 | | static const struct in6_addr in6addr_any; /*IN6ADDR_ANY_INIT*/ |
68 | | #endif |
69 | | |
70 | | |
71 | | #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID |
72 | | static unsigned |
73 | | netsnmp_if_nametoindex(const char *ifname) |
74 | 3 | { |
75 | | #if defined(WIN32) |
76 | | return atoi(ifname); |
77 | | #elif defined(HAVE_IF_NAMETOINDEX) |
78 | 3 | int res; |
79 | | |
80 | 3 | res = if_nametoindex(ifname); |
81 | 3 | if (res == 0) |
82 | 2 | res = atoi(ifname); |
83 | | |
84 | 3 | return res; |
85 | | #else |
86 | | return 0; |
87 | | #endif |
88 | 3 | } |
89 | | |
90 | | static char * |
91 | | netsnmp_if_indextoname(unsigned ifindex, char *ifname) |
92 | 0 | { |
93 | | #if defined(WIN32) |
94 | | snprintf(ifname, IF_NAMESIZE, "%u", ifindex); |
95 | | return ifname; |
96 | | #elif defined(HAVE_IF_NAMETOINDEX) |
97 | 0 | return if_indextoname(ifindex, ifname); |
98 | | #else |
99 | | return NULL; |
100 | | #endif |
101 | 0 | } |
102 | | #endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */ |
103 | | |
104 | | char * |
105 | | netsnmp_ipv6_fmtaddr(const char *prefix, netsnmp_transport *t, |
106 | | const void *data, int len) |
107 | 774 | { |
108 | 774 | const struct sockaddr_in6 *to; |
109 | 774 | char scope_id[IF_NAMESIZE + 1] = ""; |
110 | 774 | char addr[INET6_ADDRSTRLEN]; |
111 | 774 | char *tmp; |
112 | | |
113 | 774 | DEBUGMSGTL(("netsnmp_ipv6", "fmtaddr: t = %p, data = %p, len = %d\n", t, |
114 | 774 | data, len)); |
115 | | |
116 | 774 | if (t && !data) { |
117 | 0 | data = t->data; |
118 | 0 | len = t->data_length; |
119 | 0 | } |
120 | | |
121 | 774 | switch (data ? len : 0) { |
122 | 32 | case sizeof(struct sockaddr_in6): |
123 | 32 | to = data; |
124 | 32 | break; |
125 | 11 | case sizeof(netsnmp_indexed_addr_pair): { |
126 | 11 | const netsnmp_indexed_addr_pair *addr_pair = data; |
127 | | |
128 | 11 | to = (const struct sockaddr_in6 *)&addr_pair->remote_addr; |
129 | 11 | break; |
130 | 0 | } |
131 | 731 | default: |
132 | 731 | if (asprintf(&tmp, "%s: unknown", prefix) < 0) |
133 | 0 | tmp = NULL; |
134 | 731 | return tmp; |
135 | 774 | } |
136 | | |
137 | 43 | if (to->sin6_family != AF_INET6) |
138 | 43 | return strdup("unsupported address family"); |
139 | | |
140 | 0 | if (t && t->flags & NETSNMP_TRANSPORT_FLAG_HOSTNAME) { |
141 | 0 | struct hostent *host; |
142 | 0 | host = netsnmp_gethostbyaddr(&to->sin6_addr, sizeof(struct in6_addr), AF_INET6); |
143 | 0 | return (host ? strdup(host->h_name) : NULL); |
144 | 0 | } else { |
145 | 0 | #if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID) |
146 | 0 | if (to->sin6_scope_id && |
147 | 0 | netsnmp_if_indextoname(to->sin6_scope_id, &scope_id[1])) |
148 | 0 | scope_id[0] = '%'; |
149 | 0 | #endif |
150 | 0 | inet_ntop(AF_INET6, &to->sin6_addr, addr, sizeof(addr)); |
151 | 0 | if (asprintf(&tmp, "%s: [%s%s]:%hu", prefix, addr, scope_id, |
152 | 0 | ntohs(to->sin6_port)) < 0) |
153 | 0 | tmp = NULL; |
154 | 0 | } |
155 | 0 | return tmp; |
156 | 0 | } |
157 | | |
158 | | void netsnmp_ipv6_get_taddr(struct netsnmp_transport_s *t, void **addr, |
159 | | size_t *addr_len) |
160 | 0 | { |
161 | 0 | struct sockaddr_in6 *sin6 = t->remote; |
162 | |
|
163 | 0 | netsnmp_assert(t->remote_length == sizeof(*sin6)); |
164 | |
|
165 | 0 | *addr_len = 18; |
166 | 0 | if ((*addr = malloc(*addr_len))) { |
167 | 0 | unsigned char *p = *addr; |
168 | |
|
169 | 0 | memcpy(p, &sin6->sin6_addr, 16); |
170 | 0 | memcpy(p + 16, &sin6->sin6_port, 2); |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | | int netsnmp_ipv6_ostring_to_sockaddr(struct sockaddr_in6 *sin6, const void *o, |
175 | | size_t o_len) |
176 | 0 | { |
177 | 0 | const char *p = o; |
178 | |
|
179 | 0 | if (o_len != 18) |
180 | 0 | return 0; |
181 | | |
182 | 0 | memset(sin6, 0, sizeof(*sin6)); |
183 | 0 | sin6->sin6_family = AF_INET6; |
184 | 0 | memcpy(&sin6->sin6_addr, p + 0, 16); |
185 | 0 | memcpy(&sin6->sin6_port, p + 16, 2); |
186 | 0 | return 1; |
187 | 0 | } |
188 | | |
189 | | static int netsnmp_resolve_v6_hostname(struct in6_addr *addr, |
190 | | const char *hostname) |
191 | 9 | { |
192 | 9 | #ifdef HAVE_GETADDRINFO |
193 | 9 | struct addrinfo hint = { 0 }; |
194 | 9 | struct addrinfo *addrs; |
195 | 9 | int err; |
196 | | |
197 | 9 | hint.ai_family = PF_INET6; |
198 | 9 | hint.ai_socktype = SOCK_DGRAM; |
199 | 9 | err = netsnmp_getaddrinfo(hostname, NULL, &hint, &addrs); |
200 | 9 | if (err) |
201 | 9 | return 0; |
202 | | |
203 | 0 | if (addrs) { |
204 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_in6", "hostname (resolved okay)\n")); |
205 | 0 | *addr = ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr; |
206 | 0 | freeaddrinfo(addrs); |
207 | 0 | } else { |
208 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_in6", "Failed to resolve IPv6 hostname\n")); |
209 | 0 | } |
210 | 0 | return 1; |
211 | | #elif defined(HAVE_GETIPNODEBYNAME) |
212 | | struct hostent *hp; |
213 | | int err; |
214 | | |
215 | | hp = getipnodebyname(hostname, AF_INET6, 0, &err); |
216 | | if (hp == NULL) { |
217 | | DEBUGMSGTL(("netsnmp_sockaddr_in6", |
218 | | "hostname (couldn't resolve = %d)\n", err)); |
219 | | return 0; |
220 | | } |
221 | | DEBUGMSGTL(("netsnmp_sockaddr_in6", "hostname (resolved okay)\n")); |
222 | | memcpy(addr, hp->h_addr, hp->h_length); |
223 | | return 1; |
224 | | #elif defined(HAVE_GETHOSTBYNAME) |
225 | | struct hostent *hp; |
226 | | |
227 | | hp = netsnmp_gethostbyname(hostname); |
228 | | if (hp == NULL) { |
229 | | DEBUGMSGTL(("netsnmp_sockaddr_in6", |
230 | | "hostname (couldn't resolve)\n")); |
231 | | return 0; |
232 | | } |
233 | | if (hp->h_addrtype != AF_INET6) { |
234 | | DEBUGMSGTL(("netsnmp_sockaddr_in6", "hostname (not AF_INET6!)\n")); |
235 | | return 0; |
236 | | } |
237 | | DEBUGMSGTL(("netsnmp_sockaddr_in6", "hostname (resolved okay)\n")); |
238 | | memcpy(addr, hp->h_addr, hp->h_length); |
239 | | return 1; |
240 | | #else /*HAVE_GETHOSTBYNAME */ |
241 | | /* |
242 | | * There is no name resolving function available. |
243 | | */ |
244 | | snmp_log(LOG_ERR, |
245 | | "no getaddrinfo()/getipnodebyname()/gethostbyname()\n"); |
246 | | return 0; |
247 | | #endif /*HAVE_GETHOSTBYNAME */ |
248 | 9 | } |
249 | | |
250 | | int |
251 | | netsnmp_sockaddr_in6_2(struct sockaddr_in6 *addr, |
252 | | const char *inpeername, const char *default_target) |
253 | 774 | { |
254 | 774 | struct netsnmp_ep ep; |
255 | 774 | int ret; |
256 | | |
257 | 774 | ret = netsnmp_sockaddr_in6_3(&ep, inpeername, default_target); |
258 | 774 | if (ret == 0) |
259 | 11 | return 0; |
260 | 763 | *addr = ep.a.sin6; |
261 | 763 | return ret; |
262 | 774 | } |
263 | | |
264 | | int |
265 | | netsnmp_sockaddr_in6_3(struct netsnmp_ep *ep, |
266 | | const char *inpeername, const char *default_target) |
267 | 774 | { |
268 | 774 | struct sockaddr_in6 *addr = &ep->a.sin6; |
269 | 774 | struct netsnmp_ep_str ep_str; |
270 | 774 | char debug_addr[INET6_ADDRSTRLEN]; |
271 | 774 | int port; |
272 | | |
273 | 774 | if (!ep) |
274 | 0 | return 0; |
275 | | |
276 | 774 | DEBUGMSGTL(("netsnmp_sockaddr_in6", |
277 | 774 | "ep %p, peername \"%s\", default_target \"%s\"\n", |
278 | 774 | ep, inpeername ? inpeername : "[NIL]", |
279 | 774 | default_target ? default_target : "[NIL]")); |
280 | | |
281 | 774 | memset(ep, 0, sizeof(*ep)); |
282 | 774 | addr->sin6_family = AF_INET6; |
283 | 774 | addr->sin6_addr = in6addr_any; |
284 | 774 | addr->sin6_port = htons(SNMP_PORT); |
285 | | |
286 | 774 | memset(&ep_str, 0, sizeof(ep_str)); |
287 | 774 | port = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, |
288 | 774 | NETSNMP_DS_LIB_DEFAULT_PORT); |
289 | 774 | if (port != 0) |
290 | 0 | snprintf(ep_str.port, sizeof(ep_str.port), "%d", port); |
291 | 774 | else if (default_target && |
292 | 774 | !netsnmp_parse_ep_str(&ep_str, default_target)) |
293 | 0 | snmp_log(LOG_ERR, "Invalid default target %s\n", |
294 | 0 | default_target); |
295 | | |
296 | 774 | if (!inpeername || !netsnmp_parse_ep_str(&ep_str, inpeername)) |
297 | 2 | return 0; |
298 | | |
299 | 772 | if (ep_str.port[0]) |
300 | 772 | addr->sin6_port = htons(atoi(ep_str.port)); |
301 | 772 | if (ep_str.iface[0]) |
302 | 95 | strlcpy(ep->iface, ep_str.iface, sizeof(ep->iface)); |
303 | 772 | if (ep_str.addr && ep_str.addr[0]) { |
304 | 15 | char *scope_id; |
305 | | |
306 | 15 | scope_id = strchr(ep_str.addr, '%'); |
307 | 15 | if (scope_id) { |
308 | 3 | *scope_id = '\0'; |
309 | 3 | #if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID) |
310 | 3 | addr->sin6_scope_id = netsnmp_if_nametoindex(scope_id + 1); |
311 | 3 | #endif |
312 | 3 | } |
313 | 15 | if (!inet_pton(AF_INET6, ep_str.addr, &addr->sin6_addr) && |
314 | 15 | !netsnmp_resolve_v6_hostname(&addr->sin6_addr, ep_str.addr)) { |
315 | 9 | DEBUGMSGTL(("netsnmp_sockaddr_in6", "failed to parse %s\n", |
316 | 9 | ep_str.addr)); |
317 | 9 | free(ep_str.addr); |
318 | 9 | return 0; |
319 | 9 | } |
320 | 15 | } |
321 | | |
322 | 763 | DEBUGMSGTL(("netsnmp_sockaddr_in6", "return { AF_INET6, [%s%%%d]:%hu }\n", |
323 | 763 | inet_ntop(AF_INET6, &addr->sin6_addr, debug_addr, |
324 | 763 | sizeof(debug_addr)), (int)addr->sin6_scope_id, |
325 | 763 | ntohs(addr->sin6_port))); |
326 | 763 | free(ep_str.addr); |
327 | 763 | return 1; |
328 | 772 | } |
329 | | |
330 | | |
331 | | int |
332 | | netsnmp_sockaddr_in6(struct sockaddr_in6 *addr, |
333 | | const char *inpeername, int remote_port) |
334 | 774 | { |
335 | 774 | char buf[sizeof(remote_port) * 3 + 2]; |
336 | 774 | sprintf(buf, ":%u", remote_port); |
337 | 774 | return netsnmp_sockaddr_in6_2(addr, inpeername, remote_port ? buf : NULL); |
338 | 774 | } |
339 | | |
340 | | #endif /* NETSNMP_ENABLE_IPV6 */ |