/src/wireshark/epan/addr_resolv.c
Line | Count | Source |
1 | | /* addr_resolv.c |
2 | | * Routines for network object lookup |
3 | | * |
4 | | * Laurent Deniel <laurent.deniel@free.fr> |
5 | | * |
6 | | * Add option to resolv VLAN ID to describing name |
7 | | * Uli Heilmeier, March 2016 |
8 | | * |
9 | | * Wireshark - Network traffic analyzer |
10 | | * By Gerald Combs <gerald@wireshark.org> |
11 | | * Copyright 1998 Gerald Combs |
12 | | * |
13 | | * SPDX-License-Identifier: GPL-2.0-or-later |
14 | | */ |
15 | | |
16 | | #include "config.h" |
17 | | |
18 | | #include <stdio.h> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | #include <errno.h> |
22 | | |
23 | | #include <wsutil/strtoi.h> |
24 | | #include <wsutil/ws_assert.h> |
25 | | |
26 | | #include "iana-info.h" |
27 | | #include "manuf.h" |
28 | | |
29 | | /* |
30 | | * Win32 doesn't have SIGALRM (and it's the OS where name lookup calls |
31 | | * are most likely to take a long time, given the way address-to-name |
32 | | * lookups are done over NBNS). |
33 | | * |
34 | | * macOS does have SIGALRM, but if you longjmp() out of a name resolution |
35 | | * call in a signal handler, you might crash, because the state of the |
36 | | * resolution code that sends messages to lookupd might be inconsistent |
37 | | * if you jump out of it in middle of a call. |
38 | | * |
39 | | * There's no guarantee that longjmp()ing out of name resolution calls |
40 | | * will work on *any* platform; OpenBSD got rid of the alarm/longjmp |
41 | | * code in tcpdump, to avoid those sorts of problems, and that was |
42 | | * picked up by tcpdump.org tcpdump. |
43 | | * |
44 | | * So, for now, we do not use alarm() and SIGALRM to time out host name |
45 | | * lookups. If we get a lot of complaints about lookups taking a long time, |
46 | | * we can reconsider that decision. (Note that tcpdump originally added |
47 | | * such a timeout mechanism that for the benefit of systems using NIS to |
48 | | * look up host names; that might now be fixed in NIS implementations, for |
49 | | * those sites still using NIS rather than DNS for that.... tcpdump no |
50 | | * longer does that, for the same reasons that we don't.) |
51 | | * |
52 | | * If we're using an asynchronous DNS resolver, that shouldn't be an issue. |
53 | | * If we're using a synchronous name lookup mechanism (which we'd do mainly |
54 | | * to support resolving addresses and host names using more mechanisms than |
55 | | * just DNS, such as NIS, NBNS, or Mr. Hosts File), we could do that in |
56 | | * a separate thread, making it, in effect, asynchronous. |
57 | | */ |
58 | | |
59 | | #ifdef HAVE_NETINET_IN_H |
60 | | # include <netinet/in.h> |
61 | | #endif |
62 | | |
63 | | #ifdef HAVE_NETDB_H |
64 | | #include <netdb.h> |
65 | | #endif |
66 | | |
67 | | #ifdef HAVE_SYS_SOCKET_H |
68 | | #include <sys/socket.h> /* needed to define AF_ values on UNIX */ |
69 | | #endif |
70 | | |
71 | | #ifdef _WIN32 |
72 | | #include <winsock2.h> /* needed to define AF_ values on Windows */ |
73 | | #include <ws2tcpip.h> |
74 | | #endif |
75 | | |
76 | | #ifdef _WIN32 |
77 | | # define socklen_t unsigned int |
78 | | #endif |
79 | | #include <ares.h> |
80 | | #include <ares_version.h> |
81 | | |
82 | | #include <glib.h> |
83 | | |
84 | | #include <epan/packet.h> |
85 | | #include "addr_resolv.h" |
86 | | #include "wsutil/filesystem.h" |
87 | | |
88 | | #include <wsutil/report_message.h> |
89 | | #include <wsutil/file_util.h> |
90 | | #include <wsutil/pint.h> |
91 | | #include <wsutil/inet_cidr.h> |
92 | | |
93 | | #include <epan/strutil.h> |
94 | | #include <epan/to_str.h> |
95 | | #include <epan/maxmind_db.h> |
96 | | #include <epan/prefs.h> |
97 | | #include <epan/uat.h> |
98 | | |
99 | 56 | #define ENAME_HOSTS "hosts" |
100 | 84 | #define ENAME_SUBNETS "subnets" |
101 | 84 | #define ENAME_SUBNETS_V6 "subnetsipv6" |
102 | 84 | #define ENAME_ETHERS "ethers" |
103 | 84 | #define ENAME_IPXNETS "ipxnets" |
104 | 84 | #define ENAME_MANUF "manuf" |
105 | 28 | #define ENAME_WKA "wka" |
106 | 84 | #define ENAME_SERVICES "services" |
107 | 56 | #define ENAME_VLANS "vlans" |
108 | 28 | #define ENAME_SS7PCS "ss7pcs" |
109 | 84 | #define ENAME_ENTERPRISES "enterprises" |
110 | 28 | #define ENAME_TACS "tacs" |
111 | | |
112 | | #define HASHETHSIZE 2048 |
113 | 0 | #define HASHHOSTSIZE 2048 |
114 | | #define HASHIPXNETSIZE 256 |
115 | 1.46k | #define SUBNETLENGTHSIZE 32 /*1-32 inc.*/ |
116 | 5.41k | #define SUBNETLENGTHSIZE_V6 128 /*1-128 inc.*/ |
117 | | |
118 | | /* hash table used for IPv4 lookup */ |
119 | | |
120 | 0 | #define HASH_IPV4_ADDRESS(addr) (g_htonl(addr) & (HASHHOSTSIZE - 1)) |
121 | | |
122 | | |
123 | | typedef struct sub_net_hashipv4 { |
124 | | unsigned addr; |
125 | | /* XXX: No longer needed?*/ |
126 | | uint8_t flags; /* B0 dummy_entry, B1 resolve, B2 If the address is used in the trace */ |
127 | | struct sub_net_hashipv4 *next; |
128 | | char name[MAXNAMELEN]; |
129 | | } sub_net_hashipv4_t; |
130 | | |
131 | | /* Array of entries of subnets of different lengths */ |
132 | | typedef struct { |
133 | | size_t mask_length; /*1-32*/ |
134 | | uint32_t mask; /* e.g. 255.255.255.*/ |
135 | | sub_net_hashipv4_t** subnet_addresses; /* Hash table of subnet addresses */ |
136 | | } subnet_length_entry_t; |
137 | | |
138 | | |
139 | | /* IPv6 subnet lookup structures */ |
140 | | typedef struct sub_net_hashipv6 { |
141 | | uint8_t addr[16]; /* masked network address */ |
142 | | uint8_t flags; |
143 | | struct sub_net_hashipv6 *next; |
144 | | char name[MAXNAMELEN]; |
145 | | } sub_net_hashipv6_t; |
146 | | |
147 | | typedef struct { |
148 | | size_t mask_length; /* 1-128 */ |
149 | | uint8_t mask[16]; /* byte mask */ |
150 | | sub_net_hashipv6_t **subnet_addresses; /* hash table */ |
151 | | } subnet_length_entry_v6_t; |
152 | | |
153 | | typedef struct { |
154 | | uint8_t mask[16]; |
155 | | size_t mask_length; |
156 | | const char *name; |
157 | | } subnet_entry_v6_t; |
158 | | |
159 | | /* hash table used for IPX network lookup */ |
160 | | |
161 | | /* XXX - check goodness of hash function */ |
162 | | |
163 | | #define HASH_IPX_NET(net) ((net) & (HASHIPXNETSIZE - 1)) |
164 | | |
165 | | typedef struct hashipxnet { |
166 | | unsigned addr; |
167 | | struct hashipxnet *next; |
168 | | char name[MAXNAMELEN]; |
169 | | } hashipxnet_t; |
170 | | |
171 | | typedef struct hashvlan { |
172 | | unsigned id; |
173 | | /* struct hashvlan *next; */ |
174 | | char name[MAXVLANNAMELEN]; |
175 | | } hashvlan_t; |
176 | | |
177 | | typedef struct ss7pc { |
178 | | uint32_t id; /* 1st byte NI, 3 following bytes: Point Code */ |
179 | | char pc_addr[MAXNAMELEN]; |
180 | | char name[MAXNAMELEN]; |
181 | | } hashss7pc_t; |
182 | | |
183 | | /* hash tables used for ethernet and manufacturer lookup */ |
184 | | struct hashether { |
185 | | uint8_t flags; /* (See above) */ |
186 | | uint8_t addr[6]; |
187 | | char hexaddr[6*3]; |
188 | | char resolved_name[MAXNAMELEN]; |
189 | | }; |
190 | | |
191 | | struct hasheui64 { |
192 | | uint8_t flags; /* (See above) */ |
193 | | uint8_t addr[EUI64_ADDR_LEN]; |
194 | | char hexaddr[EUI64_ADDR_LEN*3]; |
195 | | char resolved_name[MAXNAMELEN]; |
196 | | }; |
197 | | |
198 | | struct hashwka { |
199 | | uint8_t flags; /* (See above) */ |
200 | | char* name; |
201 | | }; |
202 | | |
203 | | struct hashmanuf { |
204 | | uint8_t flags; /* (See above) */ |
205 | | uint8_t addr[3]; |
206 | | char hexaddr[3*3]; |
207 | | char resolved_name[MAXNAMELEN]; |
208 | | char resolved_longname[MAXNAMELEN]; |
209 | | }; |
210 | | |
211 | | /* internal type used when reading ethers file (or wka, manuf) */ |
212 | | typedef struct _ether |
213 | | { |
214 | | uint8_t addr[8]; |
215 | | char name[MAXNAMELEN]; |
216 | | char longname[MAXNAMELEN]; |
217 | | } ether_t; |
218 | | |
219 | | /* internal ipxnet type */ |
220 | | typedef struct _ipxnet |
221 | | { |
222 | | unsigned addr; |
223 | | char name[MAXNAMELEN]; |
224 | | } ipxnet_t; |
225 | | |
226 | | /* internal vlan type */ |
227 | | typedef struct _vlan |
228 | | { |
229 | | unsigned id; |
230 | | char name[MAXVLANNAMELEN]; |
231 | | } vlan_t; |
232 | | |
233 | | static wmem_allocator_t *addr_resolv_scope; |
234 | | |
235 | | // Maps unsigned -> hashipxnet_t* |
236 | | static wmem_map_t *ipxnet_hash_table; |
237 | | static wmem_map_t *ipv4_hash_table; |
238 | | static wmem_map_t *ipv6_hash_table; |
239 | | // Maps unsigned -> hashvlan_t* |
240 | | static wmem_map_t *vlan_hash_table; |
241 | | static wmem_map_t *ss7pc_hash_table; |
242 | | static wmem_map_t *tac_hash_table; |
243 | | |
244 | | // Maps IP address -> manually set hostname. |
245 | | static wmem_map_t *manually_resolved_ipv4_list; |
246 | | static wmem_map_t *manually_resolved_ipv6_list; |
247 | | |
248 | | static addrinfo_lists_t addrinfo_lists; |
249 | | |
250 | | struct cb_serv_data { |
251 | | char *service; |
252 | | port_type proto; |
253 | | }; |
254 | | |
255 | | // Maps unsigned -> hashmanuf_t* |
256 | | // XXX: Note that hashmanuf_t* only accommodates 24-bit OUIs. |
257 | | // We might want to store vendor names from MA-M and MA-S to |
258 | | // present in the Resolved Addresses dialog. |
259 | | static wmem_map_t *manuf_hashtable; |
260 | | // Maps address -> hashwka_t* |
261 | | static wmem_map_t *wka_hashtable; |
262 | | // Maps address -> hashether_t* |
263 | | static wmem_map_t *eth_hashtable; |
264 | | // Maps address -> hasheui64_t* |
265 | | static wmem_map_t *eui64_hashtable; |
266 | | // Maps unsigned -> serv_port_t* |
267 | | static wmem_map_t *serv_port_hashtable; |
268 | | static wmem_map_t *serv_port_custom_hashtable; |
269 | | |
270 | | // Maps enterprise-id -> enterprise-desc (only used for user additions) |
271 | | static GHashTable *enterprises_hashtable; |
272 | | |
273 | | static subnet_length_entry_t subnet_length_entries[SUBNETLENGTHSIZE]; /* Ordered array of entries */ |
274 | | static bool have_subnet_entry; |
275 | | |
276 | | static subnet_length_entry_v6_t subnet_length_entries_v6[SUBNETLENGTHSIZE_V6]; /* IPv6 subnet entries */ |
277 | | static bool have_subnet_entry_v6; |
278 | | |
279 | | static bool new_resolved_objects; |
280 | | |
281 | | static GPtrArray* extra_hosts_files; |
282 | | |
283 | | static hashether_t *add_eth_name(const uint8_t *addr, const char *name, bool static_entry); |
284 | | static hasheui64_t *add_eui64_name(const uint8_t *addr, const char *name, bool static_entry); |
285 | | static void add_serv_port_cb(const uint32_t port, void *ptr); |
286 | | |
287 | | /* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx#existing |
288 | | * One-at-a-Time hash |
289 | | */ |
290 | | unsigned |
291 | | ipv6_oat_hash(const void *key) |
292 | 618 | { |
293 | 618 | int len = 16; |
294 | 618 | const unsigned char *p = (const unsigned char *)key; |
295 | 618 | unsigned h = 0; |
296 | 618 | int i; |
297 | | |
298 | 10.5k | for ( i = 0; i < len; i++ ) { |
299 | 9.88k | h += p[i]; |
300 | 9.88k | h += ( h << 10 ); |
301 | 9.88k | h ^= ( h >> 6 ); |
302 | 9.88k | } |
303 | | |
304 | 618 | h += ( h << 3 ); |
305 | 618 | h ^= ( h >> 11 ); |
306 | 618 | h += ( h << 15 ); |
307 | | |
308 | 618 | return h; |
309 | 618 | } |
310 | | |
311 | | unsigned |
312 | | ws_ipv6_hash(const void* key) |
313 | 0 | { |
314 | | #ifdef HAVE_XXHASH |
315 | | return wmem_strong_hash(key, 16); |
316 | | #else |
317 | 0 | return ipv6_oat_hash(key); |
318 | 0 | #endif |
319 | 0 | } |
320 | | |
321 | | gboolean |
322 | | ipv6_equal(const void *v1, const void *v2) |
323 | 236 | { |
324 | | |
325 | 236 | if (memcmp(v1, v2, sizeof (ws_in6_addr)) == 0) { |
326 | 233 | return true; |
327 | 233 | } |
328 | | |
329 | 3 | return false; |
330 | 236 | } |
331 | | |
332 | | /* |
333 | | * Flag controlling what names to resolve. |
334 | | */ |
335 | | e_addr_resolve gbl_resolv_flags = { |
336 | | true, /* mac_name */ |
337 | | false, /* network_name */ |
338 | | false, /* transport_name */ |
339 | | true, /* dns_pkt_addr_resolution */ |
340 | | false, /* handshake_sni_addr_resolution */ |
341 | | true, /* use_external_net_name_resolver */ |
342 | | false, /* vlan_name */ |
343 | | false, /* ss7 point code names */ |
344 | | true, /* maxmind_geoip */ |
345 | | false, /* tac_name */ |
346 | | }; |
347 | | |
348 | | /* XXX - ares_init_options(3) says: |
349 | | * "The recommended concurrent query limit is about 32k queries" |
350 | | */ |
351 | | static unsigned name_resolve_concurrency = 500; |
352 | | static bool resolve_synchronously; |
353 | | |
354 | | /* |
355 | | * Global variables (can be changed in GUI sections) |
356 | | * XXX - they could be changed in GUI code, but there's currently no |
357 | | * GUI code to change them. |
358 | | */ |
359 | | |
360 | | static char *g_ethers_path; /* global ethers file */ |
361 | | static char *g_pethers_path; /* personal ethers file */ |
362 | | static char *g_wka_path; /* global well-known-addresses file */ |
363 | | static char *g_manuf_path; /* global manuf file */ |
364 | | static char *g_pmanuf_path; /* personal manuf file */ |
365 | | static char *g_ipxnets_path; /* global ipxnets file */ |
366 | | static char *g_pipxnets_path; /* personal ipxnets file */ |
367 | | static char *g_services_path; /* global services file */ |
368 | | static char *g_pservices_path; /* personal services file */ |
369 | | static char *g_pvlan_path; /* personal vlans file */ |
370 | | static char *g_enterprises_path; /* global enterprises file */ |
371 | | static char *g_penterprises_path; /* personal enterprises file */ |
372 | | /* first resolving call */ |
373 | | |
374 | | /* |
375 | | * Submitted asynchronous queries trigger a callback (c_ares_ghba_cb()). |
376 | | * Queries are added to c_ares_queue_head. During processing, queries are |
377 | | * popped off the front of c_ares_queue_head and submitted using |
378 | | * ares_gethostbyaddr(). |
379 | | * The callback processes the response, then frees the request. |
380 | | */ |
381 | | typedef struct _async_dns_queue_msg |
382 | | { |
383 | | union { |
384 | | uint32_t ip4; |
385 | | ws_in6_addr ip6; |
386 | | } addr; |
387 | | int family; |
388 | | } async_dns_queue_msg_t; |
389 | | |
390 | | typedef struct _async_hostent { |
391 | | int addr_size; |
392 | | int copied; |
393 | | void *addrp; |
394 | | } async_hostent_t; |
395 | | |
396 | | static void |
397 | | c_ares_ghba_cb(void *arg, int status, int timeouts _U_, struct hostent *he); |
398 | | |
399 | | /* |
400 | | * Submitted synchronous queries trigger a callback (c_ares_ghba_sync_cb()). |
401 | | * The callback processes the response, sets completed to true if |
402 | | * completed is non-NULL, then frees the request. |
403 | | */ |
404 | | typedef struct _sync_dns_data |
405 | | { |
406 | | union { |
407 | | uint32_t ip4; |
408 | | ws_in6_addr ip6; |
409 | | } addr; |
410 | | int family; |
411 | | bool *completed; |
412 | | } sync_dns_data_t; |
413 | | |
414 | | static ares_channel ghba_chan; /* ares_gethostbyaddr -- Usually non-interactive, no timeout */ |
415 | | static ares_channel ghbn_chan; /* ares_gethostbyname -- Usually interactive, timeout */ |
416 | | |
417 | | static bool async_dns_initialized; |
418 | | static unsigned async_dns_in_flight; |
419 | | static wmem_list_t *async_dns_queue_head; |
420 | | static GMutex async_dns_queue_mtx; |
421 | | |
422 | | //UAT for providing a list of DNS servers to C-ARES for name resolution |
423 | | static bool use_custom_dns_server_list; |
424 | | struct dns_server_data { |
425 | | char *ipaddr; |
426 | | uint32_t udp_port; |
427 | | uint32_t tcp_port; |
428 | | }; |
429 | | |
430 | 0 | UAT_CSTRING_CB_DEF(dnsserverlist_uats, ipaddr, struct dns_server_data) |
431 | 0 | UAT_DEC_CB_DEF(dnsserverlist_uats, tcp_port, struct dns_server_data) Unexecuted instantiation: addr_resolv.c:dnsserverlist_uats_tcp_port_set_cb Unexecuted instantiation: addr_resolv.c:dnsserverlist_uats_tcp_port_tostr_cb |
432 | 0 | UAT_DEC_CB_DEF(dnsserverlist_uats, udp_port, struct dns_server_data) Unexecuted instantiation: addr_resolv.c:dnsserverlist_uats_udp_port_set_cb Unexecuted instantiation: addr_resolv.c:dnsserverlist_uats_udp_port_tostr_cb |
433 | | |
434 | | static uat_t *dnsserver_uat; |
435 | | static struct dns_server_data *dnsserverlist_uats; |
436 | | static unsigned ndnsservers; |
437 | | |
438 | | static void |
439 | | dns_server_free_cb(void *data) |
440 | 0 | { |
441 | 0 | struct dns_server_data *h = (struct dns_server_data*)data; |
442 | |
|
443 | 0 | g_free(h->ipaddr); |
444 | 0 | } |
445 | | |
446 | | static void* |
447 | | dns_server_copy_cb(void *dst_, const void *src_, size_t len _U_) |
448 | 0 | { |
449 | 0 | const struct dns_server_data *src = (const struct dns_server_data *)src_; |
450 | 0 | struct dns_server_data *dst = (struct dns_server_data *)dst_; |
451 | |
|
452 | 0 | dst->ipaddr = g_strdup(src->ipaddr); |
453 | 0 | dst->udp_port = src->udp_port; |
454 | 0 | dst->tcp_port = src->tcp_port; |
455 | |
|
456 | 0 | return dst; |
457 | 0 | } |
458 | | |
459 | | static bool |
460 | | dnsserver_uat_fld_ip_chk_cb(void* r _U_, const char* ipaddr, unsigned len _U_, const void* u1 _U_, const void* u2 _U_, char** err) |
461 | 0 | { |
462 | | //Check for a valid IPv4 or IPv6 address. |
463 | 0 | if (ipaddr && g_hostname_is_ip_address(ipaddr)) { |
464 | 0 | *err = NULL; |
465 | 0 | return true; |
466 | 0 | } |
467 | | |
468 | 0 | *err = ws_strdup_printf("No valid IP address given."); |
469 | 0 | return false; |
470 | 0 | } |
471 | | |
472 | | static bool |
473 | | dnsserver_uat_fld_port_chk_cb(void* r _U_, const char* p, unsigned len _U_, const void* u1 _U_, const void* u2 _U_, char** err) |
474 | 0 | { |
475 | 0 | if (!p || strlen(p) == 0u) { |
476 | | // This should be removed in favor of Decode As. Make it optional. |
477 | 0 | *err = NULL; |
478 | 0 | return true; |
479 | 0 | } |
480 | | |
481 | 0 | if (strcmp(p, "53") != 0){ |
482 | 0 | uint16_t port; |
483 | 0 | if (!ws_strtou16(p, NULL, &port)) { |
484 | 0 | *err = g_strdup("Invalid port given."); |
485 | 0 | return false; |
486 | 0 | } |
487 | 0 | } |
488 | | |
489 | 0 | *err = NULL; |
490 | 0 | return true; |
491 | 0 | } |
492 | | |
493 | | static void |
494 | 0 | c_ares_ghba_sync_cb(void *arg, int status, int timeouts _U_, struct hostent *he) { |
495 | 0 | sync_dns_data_t *sdd = (sync_dns_data_t *)arg; |
496 | 0 | char **p; |
497 | |
|
498 | 0 | if (status == ARES_SUCCESS) { |
499 | 0 | for (p = he->h_addr_list; *p != NULL; p++) { |
500 | 0 | switch(sdd->family) { |
501 | 0 | case AF_INET: |
502 | 0 | add_ipv4_name(sdd->addr.ip4, he->h_name, false); |
503 | 0 | break; |
504 | 0 | case AF_INET6: |
505 | 0 | add_ipv6_name(&sdd->addr.ip6, he->h_name, false); |
506 | 0 | break; |
507 | 0 | default: |
508 | | /* Throw an exception? */ |
509 | 0 | break; |
510 | 0 | } |
511 | 0 | } |
512 | |
|
513 | 0 | } |
514 | | |
515 | | /* |
516 | | * Let our caller know that this is complete. |
517 | | */ |
518 | 0 | *sdd->completed = true; |
519 | | |
520 | | /* |
521 | | * Free the structure for this call. |
522 | | */ |
523 | 0 | g_free(sdd); |
524 | 0 | } |
525 | | |
526 | | static void |
527 | 0 | wait_for_sync_resolv(bool *completed) { |
528 | 0 | int nfds; |
529 | 0 | fd_set rfds, wfds; |
530 | 0 | struct timeval tv; |
531 | |
|
532 | 0 | while (!*completed) { |
533 | | /* |
534 | | * Not yet resolved; wait for something to show up on the |
535 | | * address-to-name C-ARES channel. |
536 | | * |
537 | | * To quote the source code for ares_timeout() as of C-ARES |
538 | | * 1.12.0, "WARNING: Beware that this is linear in the number |
539 | | * of outstanding requests! You are probably far better off |
540 | | * just calling ares_process() once per second, rather than |
541 | | * calling ares_timeout() to figure out when to next call |
542 | | * ares_process().", although we should have only one request |
543 | | * outstanding. |
544 | | * As of C-ARES 1.20.0, the ares_timeout() function is now O(1), |
545 | | * but we don't require that minimum version. |
546 | | * https://github.com/c-ares/c-ares/commit/cf99c025cfb3e21295b59923876a31a68ea2cb4b |
547 | | * |
548 | | * And, yes, we have to reset it each time, as select(), in |
549 | | * some OSes modifies the timeout to reflect the time remaining |
550 | | * (e.g., Linux) and select() in other OSes doesn't (most if not |
551 | | * all other UN*Xes, Windows?), so we can't rely on *either* |
552 | | * behavior. |
553 | | */ |
554 | 0 | tv.tv_sec = 1; |
555 | 0 | tv.tv_usec = 0; |
556 | |
|
557 | 0 | FD_ZERO(&rfds); |
558 | 0 | FD_ZERO(&wfds); |
559 | 0 | nfds = ares_fds(ghba_chan, &rfds, &wfds); |
560 | 0 | if (nfds > 0) { |
561 | 0 | if (select(nfds, &rfds, &wfds, NULL, &tv) == -1) { /* call to select() failed */ |
562 | | /* If it's interrupted by a signal, no need to put out a message */ |
563 | 0 | if (errno != EINTR) |
564 | 0 | fprintf(stderr, "Warning: call to select() failed, error is %s\n", g_strerror(errno)); |
565 | 0 | return; |
566 | 0 | } |
567 | 0 | ares_process(ghba_chan, &rfds, &wfds); |
568 | 0 | } |
569 | 0 | } |
570 | 0 | } |
571 | | |
572 | | static void |
573 | | process_async_dns_queue(void) |
574 | 0 | { |
575 | 0 | wmem_list_frame_t* head; |
576 | 0 | async_dns_queue_msg_t *caqm; |
577 | |
|
578 | 0 | if (async_dns_queue_head == NULL) |
579 | 0 | return; |
580 | | |
581 | 0 | if (!g_mutex_trylock(&async_dns_queue_mtx)) |
582 | 0 | return; |
583 | | |
584 | 0 | head = wmem_list_head(async_dns_queue_head); |
585 | |
|
586 | 0 | while (head != NULL && async_dns_in_flight <= name_resolve_concurrency) { |
587 | 0 | caqm = (async_dns_queue_msg_t *)wmem_list_frame_data(head); |
588 | 0 | wmem_list_remove_frame(async_dns_queue_head, head); |
589 | 0 | if (caqm->family == AF_INET) { |
590 | 0 | ares_gethostbyaddr(ghba_chan, &caqm->addr.ip4, sizeof(uint32_t), AF_INET, |
591 | 0 | c_ares_ghba_cb, caqm); |
592 | 0 | async_dns_in_flight++; |
593 | 0 | } else if (caqm->family == AF_INET6) { |
594 | 0 | ares_gethostbyaddr(ghba_chan, &caqm->addr.ip6, sizeof(ws_in6_addr), |
595 | 0 | AF_INET6, c_ares_ghba_cb, caqm); |
596 | 0 | async_dns_in_flight++; |
597 | 0 | } |
598 | |
|
599 | 0 | head = wmem_list_head(async_dns_queue_head); |
600 | 0 | } |
601 | |
|
602 | 0 | g_mutex_unlock(&async_dns_queue_mtx); |
603 | 0 | } |
604 | | |
605 | | static void |
606 | | wait_for_async_queue(void) |
607 | 0 | { |
608 | 0 | struct timeval tv = { 0, 0 }; |
609 | 0 | int nfds; |
610 | 0 | fd_set rfds, wfds; |
611 | |
|
612 | 0 | new_resolved_objects = false; |
613 | |
|
614 | 0 | if (!async_dns_initialized) { |
615 | 0 | maxmind_db_lookup_process(); |
616 | 0 | return; |
617 | 0 | } |
618 | | |
619 | 0 | while (1) { |
620 | | /* We're switching to synchronous lookups, so process anything in |
621 | | * the asynchronous queue. There might be more in the queue than |
622 | | * name_resolve_concurrency allows, so check each cycle. |
623 | | */ |
624 | 0 | process_async_dns_queue(); |
625 | |
|
626 | 0 | FD_ZERO(&rfds); |
627 | 0 | FD_ZERO(&wfds); |
628 | 0 | nfds = ares_fds(ghba_chan, &rfds, &wfds); |
629 | 0 | if (nfds == 0) { |
630 | | /* No more requests waiting for reply; we're done here. */ |
631 | 0 | break; |
632 | 0 | } |
633 | | |
634 | | /* See comment in wait_for_sync_resolv() about ares_timeout() being |
635 | | * O(N) in the number of outstanding requests until c-ares 1.20, and |
636 | | * why we might as well just set a 1 second to select(). |
637 | | */ |
638 | 0 | tv.tv_sec = 1; |
639 | 0 | tv.tv_usec = 0; |
640 | |
|
641 | 0 | if (select(nfds, &rfds, &wfds, NULL, &tv) == -1) { /* call to select() failed */ |
642 | | /* If it's interrupted by a signal, no need to put out a message */ |
643 | 0 | if (errno != EINTR) |
644 | 0 | fprintf(stderr, "Warning: call to select() failed, error is %s\n", g_strerror(errno)); |
645 | 0 | return; |
646 | 0 | } |
647 | 0 | ares_process(ghba_chan, &rfds, &wfds); |
648 | 0 | } |
649 | | |
650 | 0 | maxmind_db_lookup_process(); |
651 | 0 | return; |
652 | 0 | } |
653 | | |
654 | | static void |
655 | | sync_lookup_ip4(const uint32_t addr) |
656 | 0 | { |
657 | 0 | bool completed = false; |
658 | 0 | sync_dns_data_t *sdd; |
659 | |
|
660 | 0 | if (!async_dns_initialized) { |
661 | | /* |
662 | | * c-ares not initialized. Bail out. |
663 | | */ |
664 | 0 | return; |
665 | 0 | } |
666 | | |
667 | | /* |
668 | | * Start the request. |
669 | | */ |
670 | 0 | sdd = g_new(sync_dns_data_t, 1); |
671 | 0 | sdd->family = AF_INET; |
672 | 0 | sdd->addr.ip4 = addr; |
673 | 0 | sdd->completed = &completed; |
674 | 0 | ares_gethostbyaddr(ghba_chan, &addr, sizeof(uint32_t), AF_INET, |
675 | 0 | c_ares_ghba_sync_cb, sdd); |
676 | | |
677 | | /* |
678 | | * Now wait for it to finish. |
679 | | */ |
680 | 0 | wait_for_sync_resolv(&completed); |
681 | 0 | } |
682 | | |
683 | | static void |
684 | | sync_lookup_ip6(const ws_in6_addr *addrp) |
685 | 0 | { |
686 | 0 | bool completed = false; |
687 | 0 | sync_dns_data_t *sdd; |
688 | |
|
689 | 0 | if (!async_dns_initialized) { |
690 | | /* |
691 | | * c-ares not initialized. Bail out. |
692 | | */ |
693 | 0 | return; |
694 | 0 | } |
695 | | |
696 | | /* |
697 | | * Start the request. |
698 | | */ |
699 | 0 | sdd = g_new(sync_dns_data_t, 1); |
700 | 0 | sdd->family = AF_INET6; |
701 | 0 | memcpy(&sdd->addr.ip6, addrp, sizeof(sdd->addr.ip6)); |
702 | 0 | sdd->completed = &completed; |
703 | 0 | ares_gethostbyaddr(ghba_chan, addrp, sizeof(ws_in6_addr), AF_INET6, |
704 | 0 | c_ares_ghba_sync_cb, sdd); |
705 | | |
706 | | /* |
707 | | * Now wait for it to finish. |
708 | | */ |
709 | 0 | wait_for_sync_resolv(&completed); |
710 | 0 | } |
711 | | |
712 | | void |
713 | | set_resolution_synchrony(bool synchronous) |
714 | 0 | { |
715 | 0 | resolve_synchronously = synchronous; |
716 | 0 | maxmind_db_set_synchrony(synchronous); |
717 | |
|
718 | 0 | if (synchronous) { |
719 | 0 | wait_for_async_queue(); |
720 | 0 | } |
721 | 0 | } |
722 | | |
723 | | static void |
724 | | c_ares_set_dns_servers(void) |
725 | 42 | { |
726 | 42 | if ((!async_dns_initialized) || (!use_custom_dns_server_list)) |
727 | 42 | return; |
728 | | |
729 | 0 | if (ndnsservers == 0) { |
730 | | //clear the list of servers. This may effectively disable name resolution |
731 | 0 | ares_set_servers_ports(ghba_chan, NULL); |
732 | 0 | ares_set_servers_ports(ghbn_chan, NULL); |
733 | 0 | } else { |
734 | 0 | struct ares_addr_port_node* servers = wmem_alloc_array(NULL, struct ares_addr_port_node, ndnsservers); |
735 | 0 | ws_in4_addr ipv4addr; |
736 | 0 | ws_in6_addr ipv6addr; |
737 | 0 | bool invalid_IP_found = false; |
738 | 0 | struct ares_addr_port_node* server; |
739 | 0 | unsigned i; |
740 | 0 | for (i = 0, server = servers; i < ndnsservers-1; i++, server++) { |
741 | 0 | if (ws_inet_pton6(dnsserverlist_uats[i].ipaddr, &ipv6addr)) { |
742 | 0 | server->family = AF_INET6; |
743 | 0 | memcpy(&server->addr.addr6, &ipv6addr, 16); |
744 | 0 | } else if (ws_inet_pton4(dnsserverlist_uats[i].ipaddr, &ipv4addr)) { |
745 | 0 | server->family = AF_INET; |
746 | 0 | memcpy(&server->addr.addr4, &ipv4addr, 4); |
747 | 0 | } else { |
748 | | //This shouldn't happen, but just in case... |
749 | 0 | invalid_IP_found = true; |
750 | 0 | server->family = 0; |
751 | 0 | memset(&server->addr.addr4, 0, 4); |
752 | 0 | break; |
753 | 0 | } |
754 | | |
755 | 0 | server->udp_port = (int)dnsserverlist_uats[i].udp_port; |
756 | 0 | server->tcp_port = (int)dnsserverlist_uats[i].tcp_port; |
757 | |
|
758 | 0 | server->next = (server+1); |
759 | 0 | } |
760 | 0 | if (!invalid_IP_found) { |
761 | 0 | if (ws_inet_pton6(dnsserverlist_uats[i].ipaddr, &ipv6addr)) { |
762 | 0 | server->family = AF_INET6; |
763 | 0 | memcpy(&server->addr.addr6, &ipv6addr, 16); |
764 | 0 | } |
765 | 0 | else if (ws_inet_pton4(dnsserverlist_uats[i].ipaddr, &ipv4addr)) { |
766 | 0 | server->family = AF_INET; |
767 | 0 | memcpy(&server->addr.addr4, &ipv4addr, 4); |
768 | 0 | } else { |
769 | | //This shouldn't happen, but just in case... |
770 | 0 | server->family = 0; |
771 | 0 | memset(&server->addr.addr4, 0, 4); |
772 | 0 | } |
773 | 0 | } |
774 | 0 | server->udp_port = (int)dnsserverlist_uats[i].udp_port; |
775 | 0 | server->tcp_port = (int)dnsserverlist_uats[i].tcp_port; |
776 | |
|
777 | 0 | server->next = NULL; |
778 | |
|
779 | 0 | ares_set_servers_ports(ghba_chan, servers); |
780 | 0 | ares_set_servers_ports(ghbn_chan, servers); |
781 | 0 | wmem_free(NULL, servers); |
782 | 0 | } |
783 | 0 | } |
784 | | |
785 | | typedef struct { |
786 | | uint32_t mask; |
787 | | size_t mask_length; |
788 | | const char* name; /* Shallow copy */ |
789 | | } subnet_entry_t; |
790 | | |
791 | | /* Maximum supported line length of hosts, services, manuf, etc. */ |
792 | | #define MAX_LINELEN 1024 |
793 | | |
794 | | /** Read a line without trailing (CR)LF. Returns -1 on failure. */ |
795 | | static int |
796 | | fgetline(char *buf, int size, FILE *fp) |
797 | 0 | { |
798 | 0 | if (fgets(buf, size, fp)) { |
799 | 0 | int len = (int)strcspn(buf, "\r\n"); |
800 | 0 | buf[len] = '\0'; |
801 | 0 | return len; |
802 | 0 | } |
803 | 0 | return -1; |
804 | |
|
805 | 0 | } /* fgetline */ |
806 | | |
807 | | |
808 | | /* |
809 | | * Local function definitions |
810 | | */ |
811 | | static subnet_entry_t subnet_lookup(const uint32_t addr); |
812 | | static void subnet_entry_set(uint32_t subnet_addr, const uint8_t mask_length, const char* name); |
813 | | |
814 | | static unsigned serv_port_custom_hash(const void *k) |
815 | 0 | { |
816 | 0 | const serv_port_key_t *key = (const serv_port_key_t*)k; |
817 | 0 | return key->port + (key->type << 16); |
818 | 0 | } |
819 | | |
820 | | static gboolean serv_port_custom_equal(const void *k1, const void *k2) |
821 | 0 | { |
822 | 0 | const serv_port_key_t *key1 = (const serv_port_key_t*)k1; |
823 | 0 | const serv_port_key_t *key2 = (const serv_port_key_t*)k2; |
824 | |
|
825 | 0 | return (key1->port == key2->port) && (key1->type == key2->type); |
826 | 0 | } |
827 | | |
828 | | static void |
829 | | add_custom_service_name(port_type proto, const unsigned port, const char *service_name) |
830 | 0 | { |
831 | 0 | char *name; |
832 | 0 | serv_port_key_t *key, *orig_key; |
833 | |
|
834 | 0 | key = wmem_new(addr_resolv_scope, serv_port_key_t); |
835 | 0 | key->port = (uint16_t)port; |
836 | 0 | key->type = proto; |
837 | |
|
838 | 0 | if (wmem_map_lookup_extended(serv_port_custom_hashtable, key, (const void**)&orig_key, (void**)&name)) { |
839 | 0 | wmem_free(addr_resolv_scope, orig_key); |
840 | 0 | wmem_free(addr_resolv_scope, name); |
841 | 0 | } |
842 | |
|
843 | 0 | name = wmem_strdup(addr_resolv_scope, service_name); |
844 | 0 | wmem_map_insert(serv_port_custom_hashtable, key, name); |
845 | | |
846 | | // A new custom entry is not a new resolved object. |
847 | | // new_resolved_objects = true; |
848 | 0 | } |
849 | | |
850 | | static void |
851 | | add_service_name(port_type proto, const unsigned port, const char *service_name) |
852 | 0 | { |
853 | 0 | serv_port_key_t *key = wmem_new(addr_resolv_scope, serv_port_key_t); |
854 | 0 | key->port = (uint16_t)port; |
855 | 0 | key->type = proto; |
856 | |
|
857 | 0 | wmem_map_insert(serv_port_hashtable, key, (void*)service_name); |
858 | |
|
859 | 0 | new_resolved_objects = true; |
860 | 0 | } |
861 | | |
862 | | static void |
863 | | parse_service_line (char *line) |
864 | 0 | { |
865 | 0 | char *cp; |
866 | 0 | char *service; |
867 | 0 | char *port; |
868 | 0 | port_type proto; |
869 | 0 | struct cb_serv_data cb_data; |
870 | 0 | range_t *port_rng = NULL; |
871 | |
|
872 | 0 | if ((cp = strchr(line, '#'))) |
873 | 0 | *cp = '\0'; |
874 | |
|
875 | 0 | if ((cp = strtok(line, " \t")) == NULL) |
876 | 0 | return; |
877 | | |
878 | 0 | service = cp; |
879 | |
|
880 | 0 | if ((cp = strtok(NULL, " \t")) == NULL) |
881 | 0 | return; |
882 | | |
883 | 0 | port = cp; |
884 | |
|
885 | 0 | if (strtok(cp, "/") == NULL) |
886 | 0 | return; |
887 | | |
888 | 0 | if (range_convert_str(NULL, &port_rng, port, UINT16_MAX) != CVT_NO_ERROR) { |
889 | 0 | wmem_free (NULL, port_rng); |
890 | 0 | return; |
891 | 0 | } |
892 | | |
893 | 0 | while ((cp = strtok(NULL, "/")) != NULL) { |
894 | 0 | if (strcmp(cp, "tcp") == 0) { |
895 | 0 | proto = PT_TCP; |
896 | 0 | } |
897 | 0 | else if (strcmp(cp, "udp") == 0) { |
898 | 0 | proto = PT_UDP; |
899 | 0 | } |
900 | 0 | else if (strcmp(cp, "sctp") == 0) { |
901 | 0 | proto = PT_SCTP; |
902 | 0 | } |
903 | 0 | else if (strcmp(cp, "dccp") == 0) { |
904 | 0 | proto = PT_DCCP; |
905 | 0 | } |
906 | 0 | else { |
907 | 0 | break; |
908 | 0 | } |
909 | 0 | cb_data.service = service; |
910 | 0 | cb_data.proto = proto; |
911 | 0 | range_foreach(port_rng, add_serv_port_cb, &cb_data); |
912 | 0 | } |
913 | |
|
914 | 0 | wmem_free (NULL, port_rng); |
915 | 0 | } /* parse_service_line */ |
916 | | |
917 | | |
918 | | static void |
919 | | add_serv_port_cb(const uint32_t port, void *ptr) |
920 | 0 | { |
921 | 0 | struct cb_serv_data *cb_data = (struct cb_serv_data *)ptr; |
922 | |
|
923 | 0 | if ( port ) { |
924 | 0 | add_custom_service_name(cb_data->proto, port, cb_data->service); |
925 | 0 | } |
926 | 0 | } |
927 | | |
928 | | |
929 | | static bool |
930 | | parse_services_file(const char * path) |
931 | 84 | { |
932 | 84 | FILE *serv_p; |
933 | 84 | char buf[MAX_LINELEN]; |
934 | | |
935 | | /* services hash table initialization */ |
936 | 84 | serv_p = ws_fopen(path, "r"); |
937 | | |
938 | 84 | if (serv_p == NULL) |
939 | 84 | return false; |
940 | | |
941 | 0 | while (fgetline(buf, sizeof(buf), serv_p) >= 0) { |
942 | 0 | parse_service_line(buf); |
943 | 0 | } |
944 | |
|
945 | 0 | fclose(serv_p); |
946 | 0 | return true; |
947 | 84 | } |
948 | | |
949 | | /* ----------------- |
950 | | * unsigned integer to ascii |
951 | | */ |
952 | | static char * |
953 | | wmem_utoa(wmem_allocator_t *allocator, unsigned port) |
954 | 89.6k | { |
955 | 89.6k | char *bp = (char *)wmem_alloc(allocator, MAXNAMELEN); |
956 | | |
957 | | /* XXX, uint32_to_str() ? */ |
958 | 89.6k | uint32_to_str_buf(port, bp, MAXNAMELEN); |
959 | 89.6k | return bp; |
960 | 89.6k | } |
961 | | |
962 | | static const char * |
963 | | _serv_name_lookup(port_type proto, unsigned port) |
964 | 0 | { |
965 | 0 | const char* name = NULL; |
966 | 0 | ws_services_proto_t p; |
967 | 0 | ws_services_entry_t const *serv; |
968 | |
|
969 | 0 | const serv_port_key_t custom_key = { (uint16_t)port, proto }; |
970 | | /* Look in the cache. Use an extended lookup so we can distinguish a port |
971 | | * we already tried but had no name from one we haven't tried. */ |
972 | 0 | if (!wmem_map_lookup_extended(serv_port_hashtable, &custom_key, NULL, (void **)&name)) { |
973 | | /* Try the user custom table */ |
974 | 0 | name = wmem_map_lookup(serv_port_custom_hashtable, &custom_key); |
975 | |
|
976 | 0 | if (name == NULL) { |
977 | | /* now look in the global tables */ |
978 | 0 | bool valid_proto = true; |
979 | 0 | switch(proto) { |
980 | 0 | case PT_TCP: p = ws_tcp; break; |
981 | 0 | case PT_UDP: p = ws_udp; break; |
982 | 0 | case PT_SCTP: p = ws_sctp; break; |
983 | 0 | case PT_DCCP: p = ws_dccp; break; |
984 | 0 | default: valid_proto = false; |
985 | 0 | } |
986 | 0 | if (valid_proto) { |
987 | 0 | serv = global_services_lookup(port, p); |
988 | 0 | if (serv) { |
989 | 0 | name = serv->name; |
990 | 0 | } |
991 | 0 | } |
992 | 0 | } |
993 | | |
994 | | /* Cache result (even if NULL, so we can know we have no result.) */ |
995 | 0 | add_service_name(proto, port, name); |
996 | 0 | } |
997 | | |
998 | 0 | return name; |
999 | 0 | } |
1000 | | |
1001 | | const char * |
1002 | | try_serv_name_lookup(port_type proto, unsigned port) |
1003 | 0 | { |
1004 | 0 | return (proto == PT_NONE) ? NULL : _serv_name_lookup(proto, port); |
1005 | 0 | } |
1006 | | |
1007 | | const char * |
1008 | | serv_name_lookup(port_type proto, unsigned port) |
1009 | 0 | { |
1010 | 0 | const char *name; |
1011 | | |
1012 | | /* first look for the name */ |
1013 | 0 | name = _serv_name_lookup(proto, port); |
1014 | 0 | if (name != NULL) |
1015 | 0 | return name; |
1016 | | |
1017 | | /* No resolved name. Do we have a cached numeric string? */ |
1018 | 0 | const serv_port_key_t key = { (uint16_t)port, PT_NONE }; |
1019 | 0 | name = (const char*)wmem_map_lookup(serv_port_hashtable, &key); |
1020 | | /* No name; create the numeric string. */ |
1021 | 0 | if (name == NULL) { |
1022 | 0 | name = wmem_strdup_printf(addr_resolv_scope, "%u", port); |
1023 | 0 | add_service_name(PT_NONE, port, name); |
1024 | 0 | } |
1025 | |
|
1026 | 0 | return name; |
1027 | 0 | } |
1028 | | |
1029 | | static void |
1030 | | initialize_services(const char* app_env_var_prefix) |
1031 | 28 | { |
1032 | 28 | ws_assert(serv_port_hashtable == NULL); |
1033 | 28 | serv_port_hashtable = wmem_map_new(addr_resolv_scope, serv_port_custom_hash, serv_port_custom_equal); |
1034 | 28 | ws_assert(serv_port_custom_hashtable == NULL); |
1035 | 28 | serv_port_custom_hashtable = wmem_map_new(addr_resolv_scope, serv_port_custom_hash, serv_port_custom_equal); |
1036 | | |
1037 | | /* Compute the pathname of the global services file. */ |
1038 | 28 | if (g_services_path == NULL) { |
1039 | 28 | g_services_path = get_datafile_path(ENAME_SERVICES, app_env_var_prefix); |
1040 | 28 | } |
1041 | 28 | parse_services_file(g_services_path); |
1042 | | |
1043 | | /* Compute the pathname of the personal services file */ |
1044 | 28 | if (g_pservices_path == NULL) { |
1045 | | /* Check profile directory before personal configuration */ |
1046 | 28 | g_pservices_path = get_persconffile_path(ENAME_SERVICES, true, app_env_var_prefix); |
1047 | 28 | if (!parse_services_file(g_pservices_path)) { |
1048 | 28 | g_free(g_pservices_path); |
1049 | 28 | g_pservices_path = get_persconffile_path(ENAME_SERVICES, false, app_env_var_prefix); |
1050 | 28 | parse_services_file(g_pservices_path); |
1051 | 28 | } |
1052 | 28 | } |
1053 | 28 | } |
1054 | | |
1055 | | static void |
1056 | | service_name_lookup_cleanup(void) |
1057 | 14 | { |
1058 | 14 | serv_port_hashtable = NULL; |
1059 | 14 | serv_port_custom_hashtable = NULL; |
1060 | 14 | g_free(g_services_path); |
1061 | 14 | g_services_path = NULL; |
1062 | 14 | g_free(g_pservices_path); |
1063 | 14 | g_pservices_path = NULL; |
1064 | 14 | } |
1065 | | |
1066 | | static void |
1067 | | parse_enterprises_line (char *line) |
1068 | 0 | { |
1069 | 0 | char *tok, *dec_str, *org_str; |
1070 | 0 | uint32_t dec; |
1071 | 0 | bool had_comment = false; |
1072 | | |
1073 | | /* Stop the line at any comment found */ |
1074 | 0 | if ((tok = strchr(line, '#'))) { |
1075 | 0 | *tok = '\0'; |
1076 | 0 | had_comment = true; |
1077 | 0 | } |
1078 | | /* Get enterprise number */ |
1079 | 0 | dec_str = strtok(line, " \t"); |
1080 | 0 | if (!dec_str) |
1081 | 0 | return; |
1082 | | /* Get enterprise name */ |
1083 | 0 | org_str = strtok(NULL, ""); /* everything else */ |
1084 | 0 | if (org_str && had_comment) { |
1085 | | /* Only need to strip after (between name and where comment was) */ |
1086 | 0 | org_str = g_strchomp(org_str); |
1087 | 0 | } |
1088 | 0 | if (!org_str) |
1089 | 0 | return; |
1090 | | |
1091 | | /* Add entry using number as key */ |
1092 | 0 | if (!ws_strtou32(dec_str, NULL, &dec)) |
1093 | 0 | return; |
1094 | 0 | g_hash_table_insert(enterprises_hashtable, GUINT_TO_POINTER(dec), g_strdup(org_str)); |
1095 | 0 | } |
1096 | | |
1097 | | |
1098 | | static bool |
1099 | | parse_enterprises_file(const char * path) |
1100 | 56 | { |
1101 | 56 | FILE *fp; |
1102 | 56 | char buf[MAX_LINELEN]; |
1103 | | |
1104 | 56 | fp = ws_fopen(path, "r"); |
1105 | 56 | if (fp == NULL) |
1106 | 56 | return false; |
1107 | | |
1108 | 0 | while (fgetline(buf, sizeof(buf), fp) >= 0) { |
1109 | 0 | parse_enterprises_line(buf); |
1110 | 0 | } |
1111 | |
|
1112 | 0 | fclose(fp); |
1113 | 0 | return true; |
1114 | 56 | } |
1115 | | |
1116 | | static void |
1117 | | initialize_enterprises(const char* app_env_var_prefix) |
1118 | 28 | { |
1119 | 28 | ws_assert(enterprises_hashtable == NULL); |
1120 | 28 | enterprises_hashtable = g_hash_table_new_full(NULL, NULL, NULL, g_free); |
1121 | | |
1122 | 28 | if (g_enterprises_path == NULL) { |
1123 | 28 | g_enterprises_path = get_datafile_path(ENAME_ENTERPRISES, app_env_var_prefix); |
1124 | 28 | } |
1125 | 28 | parse_enterprises_file(g_enterprises_path); |
1126 | | |
1127 | | /* Populate entries from profile or personal */ |
1128 | 28 | if (g_penterprises_path == NULL) { |
1129 | | /* Check profile directory before personal configuration */ |
1130 | 28 | g_penterprises_path = get_persconffile_path(ENAME_ENTERPRISES, true, app_env_var_prefix); |
1131 | 28 | if (!file_exists(g_penterprises_path)) { |
1132 | 28 | g_free(g_penterprises_path); |
1133 | 28 | g_penterprises_path = get_persconffile_path(ENAME_ENTERPRISES, false, app_env_var_prefix); |
1134 | 28 | } |
1135 | 28 | } |
1136 | | /* Parse personal file (if present) */ |
1137 | 28 | parse_enterprises_file(g_penterprises_path); |
1138 | 28 | } |
1139 | | |
1140 | | const char * |
1141 | | try_enterprises_lookup(uint32_t value) |
1142 | 1.29k | { |
1143 | | /* Trying extra entries first. N.B. This does allow entries to be overwritten and found.. */ |
1144 | 1.29k | const char *name = (const char *)g_hash_table_lookup(enterprises_hashtable, GUINT_TO_POINTER(value)); |
1145 | 1.29k | if (name) |
1146 | 0 | return name; |
1147 | | |
1148 | 1.29k | return val_to_str_ext_const(value, &enterprise_val_ext, "Unknown"); |
1149 | 1.29k | } |
1150 | | |
1151 | | const char * |
1152 | | enterprises_lookup(uint32_t value, const char *unknown_str) |
1153 | 1.22k | { |
1154 | 1.22k | const char *s; |
1155 | | |
1156 | 1.22k | s = try_enterprises_lookup(value); |
1157 | 1.22k | if (s != NULL) |
1158 | 1.22k | return s; |
1159 | 0 | if (unknown_str != NULL) |
1160 | 0 | return unknown_str; |
1161 | 0 | return "<Unknown>"; |
1162 | 0 | } |
1163 | | |
1164 | | void |
1165 | | enterprises_base_custom(char *buf, uint32_t value) |
1166 | 0 | { |
1167 | 0 | const char *s; |
1168 | |
|
1169 | 0 | if ((s = try_enterprises_lookup(value)) == NULL) |
1170 | 0 | s = ITEM_LABEL_UNKNOWN_STR; |
1171 | 0 | snprintf(buf, ITEM_LABEL_LENGTH, "%s (%u)", s, value); |
1172 | 0 | } |
1173 | | |
1174 | | static void |
1175 | | enterprises_cleanup(void) |
1176 | 14 | { |
1177 | 14 | ws_assert(enterprises_hashtable); |
1178 | 14 | g_hash_table_destroy(enterprises_hashtable); |
1179 | 14 | enterprises_hashtable = NULL; |
1180 | 14 | g_free(g_enterprises_path); |
1181 | 14 | g_enterprises_path = NULL; |
1182 | 14 | g_free(g_penterprises_path); |
1183 | 14 | g_penterprises_path = NULL; |
1184 | 14 | } |
1185 | | |
1186 | | /* Fill in an IP4 structure with info from subnets file or just with the |
1187 | | * string form of the address. |
1188 | | */ |
1189 | | bool |
1190 | | fill_dummy_ip4(const unsigned addr, hashipv4_t* volatile tp) |
1191 | 82 | { |
1192 | 82 | subnet_entry_t subnet_entry; |
1193 | | |
1194 | | /* return value : true if addr matches any subnet */ |
1195 | 82 | bool cidr_covered = false; |
1196 | | |
1197 | | /* Overwrite if we get async DNS reply */ |
1198 | | |
1199 | | /* Do we have a subnet for this address? */ |
1200 | 82 | subnet_entry = subnet_lookup(addr); |
1201 | 82 | if (0 != subnet_entry.mask) { |
1202 | | /* Print name, then '.' then IP address after subnet mask */ |
1203 | 0 | uint32_t host_addr; |
1204 | 0 | char buffer[WS_INET_ADDRSTRLEN]; |
1205 | 0 | char* paddr; |
1206 | 0 | size_t i; |
1207 | |
|
1208 | 0 | host_addr = addr & (~subnet_entry.mask); |
1209 | 0 | ip_addr_to_str_buf(&host_addr, buffer, WS_INET_ADDRSTRLEN); |
1210 | 0 | paddr = buffer; |
1211 | | |
1212 | | /* Skip to first octet that is not totally masked |
1213 | | * If length of mask is 32, we chomp the whole address. |
1214 | | * If the address string starts '.' (should not happen?), |
1215 | | * we skip that '.'. |
1216 | | */ |
1217 | 0 | i = subnet_entry.mask_length / 8; |
1218 | 0 | while(*(paddr) != '\0' && i > 0) { |
1219 | 0 | if (*(++paddr) == '.') { |
1220 | 0 | --i; |
1221 | 0 | } |
1222 | 0 | } |
1223 | | |
1224 | | /* XXX - the subnet entry name could be up to MAXDNSNAMELEN, and |
1225 | | * buffer is WS_INET_ADDRSTRLEN chars, so the total length of |
1226 | | * this string could be up to MAXDNSNAMELEN+WS_INET_ADDRSTRLEN, |
1227 | | * which won't fit in the name field of a hashipv4_t, which is only |
1228 | | * MAXDNSNAMELEN chars. |
1229 | | * |
1230 | | * For now, we do it this way, to suppress compiler warnings. |
1231 | | * g_strlcpy() returns the length of the string being copied, which |
1232 | | * should be < MAXDNSNAMELEN as it does not include the trailing NUL, |
1233 | | * but let's be cautious. |
1234 | | */ |
1235 | 0 | size_t subnet_entry_name_len; |
1236 | 0 | subnet_entry_name_len = g_strlcpy(tp->name, subnet_entry.name, MAXDNSNAMELEN); |
1237 | 0 | if (subnet_entry_name_len < MAXDNSNAMELEN) |
1238 | 0 | g_strlcpy(tp->name + subnet_entry_name_len, paddr, MAXDNSNAMELEN - subnet_entry_name_len); |
1239 | | |
1240 | | /* Evaluate the subnet in CIDR notation |
1241 | | * Reuse buffers built above |
1242 | | */ |
1243 | 0 | uint32_t subnet_addr; |
1244 | 0 | subnet_addr = addr & subnet_entry.mask; |
1245 | |
|
1246 | 0 | char buffer_subnet[WS_INET_ADDRSTRLEN]; |
1247 | 0 | ip_addr_to_str_buf(&subnet_addr, buffer_subnet, WS_INET_ADDRSTRLEN); |
1248 | |
|
1249 | 0 | char buffer_cidr[WS_INET_CIDRADDRSTRLEN]; |
1250 | 0 | snprintf(buffer_cidr, WS_INET_CIDRADDRSTRLEN, "%s%s%u", buffer_subnet, "/", (unsigned)subnet_entry.mask_length); |
1251 | |
|
1252 | 0 | snprintf(tp->cidr_addr, WS_INET_CIDRADDRSTRLEN, "%s%s%u", buffer_subnet, "/", (unsigned)subnet_entry.mask_length); |
1253 | 0 | cidr_covered = true; |
1254 | 82 | } else { |
1255 | | /* XXX: This means we end up printing "1.2.3.4 (1.2.3.4)" in many cases */ |
1256 | 82 | ip_addr_to_str_buf(&addr, tp->name, MAXDNSNAMELEN); |
1257 | | |
1258 | | /* IP does not belong to any known subnet, just indicate this IP without "/.32" */ |
1259 | 82 | ip_addr_to_str_buf(&addr, tp->cidr_addr, MAXDNSNAMELEN); |
1260 | 82 | } |
1261 | 82 | return cidr_covered; |
1262 | 82 | } |
1263 | | |
1264 | | |
1265 | | /* Forward declaration — defined later with the IPv6 subnet functions. */ |
1266 | | static subnet_entry_v6_t subnet6_lookup(const ws_in6_addr *addr); |
1267 | | |
1268 | | /* Fill in an IP6 structure with info from subnetIpv6 file or the string form |
1269 | | * of the address. |
1270 | | */ |
1271 | | static void |
1272 | | fill_dummy_ip6(hashipv6_t* volatile tp) |
1273 | 1 | { |
1274 | 1 | ws_in6_addr addr; |
1275 | 1 | memcpy(addr.bytes, tp->addr, 16); |
1276 | | |
1277 | | /* Overwrite if we get async DNS reply */ |
1278 | 1 | subnet_entry_v6_t subnet_entry = subnet6_lookup(&addr); |
1279 | 1 | if (subnet_entry.mask_length != 0) { |
1280 | 0 | ws_in6_addr host_addr; |
1281 | 0 | for (int i = 0; i < 16; i++) |
1282 | 0 | host_addr.bytes[i] = addr.bytes[i] & ~subnet_entry.mask[i]; |
1283 | | |
1284 | | /* Build host-portion 16-bit groups directly from bytes — avoids |
1285 | | * ambiguity from IPv6 '::' zero-compression in string scanning. */ |
1286 | 0 | size_t first_host_group = subnet_entry.mask_length / 16; |
1287 | 0 | wmem_strbuf_t *host_strbuf = wmem_strbuf_new_sized(addr_resolv_scope, |
1288 | 0 | WS_INET6_ADDRSTRLEN); |
1289 | 0 | for (size_t g = first_host_group; g < 8; g++) { |
1290 | 0 | if (g > first_host_group) |
1291 | 0 | wmem_strbuf_append_c(host_strbuf, ':'); |
1292 | 0 | uint16_t grp = ((uint16_t)host_addr.bytes[g * 2] << 8) |
1293 | 0 | | host_addr.bytes[g * 2 + 1]; |
1294 | 0 | wmem_strbuf_append_printf(host_strbuf, "%x", (unsigned)grp); |
1295 | 0 | } |
1296 | | |
1297 | | /* Assemble name: "subnetName:host_portion" */ |
1298 | 0 | wmem_strbuf_t *name_strbuf = wmem_strbuf_new_sized(addr_resolv_scope, |
1299 | 0 | MAXDNSNAMELEN); |
1300 | 0 | wmem_strbuf_append(name_strbuf, subnet_entry.name); |
1301 | 0 | if (wmem_strbuf_get_len(host_strbuf) > 0) { |
1302 | 0 | wmem_strbuf_append_c(name_strbuf, ':'); |
1303 | 0 | wmem_strbuf_append(name_strbuf, wmem_strbuf_get_str(host_strbuf)); |
1304 | 0 | } |
1305 | 0 | g_strlcpy(tp->name, wmem_strbuf_get_str(name_strbuf), MAXDNSNAMELEN); |
1306 | 0 | wmem_strbuf_destroy(name_strbuf); |
1307 | 0 | wmem_strbuf_destroy(host_strbuf); |
1308 | | |
1309 | | /* Build CIDR notation for cidr_addr */ |
1310 | 0 | ws_in6_addr net_addr; |
1311 | 0 | for (int i = 0; i < 16; i++) |
1312 | 0 | net_addr.bytes[i] = addr.bytes[i] & subnet_entry.mask[i]; |
1313 | 0 | char net_buf[WS_INET6_ADDRSTRLEN]; |
1314 | 0 | ip6_to_str_buf(&net_addr, net_buf, sizeof(net_buf)); |
1315 | 0 | wmem_strbuf_t *cidr_strbuf = wmem_strbuf_new_sized(addr_resolv_scope, |
1316 | 0 | WS_INET6_CIDRADDRSTRLEN); |
1317 | 0 | wmem_strbuf_append_printf(cidr_strbuf, "%s/%zu", net_buf, |
1318 | 0 | subnet_entry.mask_length); |
1319 | 0 | g_strlcpy(tp->cidr_addr, wmem_strbuf_get_str(cidr_strbuf), |
1320 | 0 | WS_INET6_CIDRADDRSTRLEN); |
1321 | 0 | wmem_strbuf_destroy(cidr_strbuf); |
1322 | 1 | } else { |
1323 | 1 | (void)g_strlcpy(tp->name, tp->ip6, MAXDNSNAMELEN); |
1324 | 1 | (void)g_strlcpy(tp->cidr_addr, tp->ip6, WS_INET6_CIDRADDRSTRLEN); |
1325 | 1 | } |
1326 | 1 | } |
1327 | | |
1328 | | static void |
1329 | 0 | c_ares_ghba_cb(void *arg, int status, int timeouts _U_, struct hostent *he) { |
1330 | 0 | async_dns_queue_msg_t *caqm = (async_dns_queue_msg_t *)arg; |
1331 | 0 | char **p; |
1332 | |
|
1333 | 0 | if (!caqm) return; |
1334 | | /* XXX, what to do if async_dns_in_flight == 0? */ |
1335 | 0 | async_dns_in_flight--; |
1336 | |
|
1337 | 0 | if (status == ARES_SUCCESS) { |
1338 | 0 | for (p = he->h_addr_list; *p != NULL; p++) { |
1339 | 0 | switch(caqm->family) { |
1340 | 0 | case AF_INET: |
1341 | 0 | add_ipv4_name(caqm->addr.ip4, he->h_name, false); |
1342 | 0 | break; |
1343 | 0 | case AF_INET6: |
1344 | 0 | add_ipv6_name(&caqm->addr.ip6, he->h_name, false); |
1345 | 0 | break; |
1346 | 0 | default: |
1347 | | /* Throw an exception? */ |
1348 | 0 | break; |
1349 | 0 | } |
1350 | 0 | } |
1351 | 0 | } |
1352 | 0 | wmem_free(addr_resolv_scope, caqm); |
1353 | 0 | } |
1354 | | |
1355 | | /* --------------- */ |
1356 | | hashipv4_t * |
1357 | | new_ipv4(const unsigned addr) |
1358 | 143 | { |
1359 | 143 | hashipv4_t *tp = wmem_new(addr_resolv_scope, hashipv4_t); |
1360 | 143 | tp->addr = addr; |
1361 | 143 | tp->flags = 0; |
1362 | 143 | tp->name[0] = '\0'; |
1363 | 143 | ip_addr_to_str_buf(&addr, tp->ip, sizeof(tp->ip)); |
1364 | 143 | return tp; |
1365 | 143 | } |
1366 | | |
1367 | | static hashipv4_t * |
1368 | | host_lookup(const unsigned addr) |
1369 | 135 | { |
1370 | 135 | hashipv4_t * volatile tp; |
1371 | | |
1372 | 135 | tp = (hashipv4_t *)wmem_map_lookup(ipv4_hash_table, GUINT_TO_POINTER(addr)); |
1373 | 135 | if (tp == NULL) { |
1374 | | /* |
1375 | | * We don't already have an entry for this host name; create one, |
1376 | | * and then try to resolve it. |
1377 | | */ |
1378 | 82 | tp = new_ipv4(addr); |
1379 | 82 | fill_dummy_ip4(addr, tp); |
1380 | 82 | wmem_map_insert(ipv4_hash_table, GUINT_TO_POINTER(addr), tp); |
1381 | 82 | } else if (tp->flags & TRIED_OR_RESOLVED_MASK) { |
1382 | 0 | return tp; |
1383 | 0 | } |
1384 | | |
1385 | | /* |
1386 | | * This hasn't been resolved yet, and we haven't tried to |
1387 | | * resolve it already. |
1388 | | */ |
1389 | | |
1390 | 135 | if (!gbl_resolv_flags.network_name) |
1391 | 135 | return tp; |
1392 | | |
1393 | 0 | if (gbl_resolv_flags.use_external_net_name_resolver) { |
1394 | 0 | tp->flags |= TRIED_RESOLVE_ADDRESS; |
1395 | |
|
1396 | 0 | if (async_dns_initialized) { |
1397 | | /* c-ares is initialized, so we can use it */ |
1398 | 0 | if (resolve_synchronously || name_resolve_concurrency == 0) { |
1399 | | /* |
1400 | | * Either all names are to be resolved synchronously or |
1401 | | * the concurrency level is 0; do the resolution |
1402 | | * synchronously. |
1403 | | */ |
1404 | 0 | sync_lookup_ip4(addr); |
1405 | 0 | } else { |
1406 | | /* |
1407 | | * Names are to be resolved asynchronously, and we |
1408 | | * allow at least one asynchronous request in flight; |
1409 | | * post an asynchronous request. |
1410 | | */ |
1411 | 0 | async_dns_queue_msg_t *caqm; |
1412 | |
|
1413 | 0 | caqm = wmem_new(addr_resolv_scope, async_dns_queue_msg_t); |
1414 | 0 | caqm->family = AF_INET; |
1415 | 0 | caqm->addr.ip4 = addr; |
1416 | 0 | wmem_list_append(async_dns_queue_head, (void *) caqm); |
1417 | 0 | } |
1418 | 0 | } |
1419 | 0 | } |
1420 | |
|
1421 | 0 | return tp; |
1422 | | |
1423 | 135 | } /* host_lookup */ |
1424 | | |
1425 | | /* --------------- */ |
1426 | | static hashipv6_t * |
1427 | | new_ipv6(const ws_in6_addr *addr) |
1428 | 194 | { |
1429 | 194 | hashipv6_t *tp = wmem_new(addr_resolv_scope, hashipv6_t); |
1430 | 194 | memcpy(tp->addr, addr->bytes, sizeof tp->addr); |
1431 | 194 | tp->flags = 0; |
1432 | 194 | tp->name[0] = '\0'; |
1433 | 194 | ip6_to_str_buf(addr, tp->ip6, sizeof(tp->ip6)); |
1434 | 194 | return tp; |
1435 | 194 | } |
1436 | | |
1437 | | /* ------------------------------------ */ |
1438 | | static hashipv6_t * |
1439 | | host_lookup6(const ws_in6_addr *addr) |
1440 | 1 | { |
1441 | 1 | hashipv6_t * volatile tp; |
1442 | | |
1443 | 1 | tp = (hashipv6_t *)wmem_map_lookup(ipv6_hash_table, addr); |
1444 | 1 | if (tp == NULL) { |
1445 | | /* |
1446 | | * We don't already have an entry for this host name; create one, |
1447 | | * and then try to resolve it. |
1448 | | */ |
1449 | 1 | ws_in6_addr *addr_key; |
1450 | | |
1451 | 1 | addr_key = wmem_new(addr_resolv_scope, ws_in6_addr); |
1452 | 1 | tp = new_ipv6(addr); |
1453 | 1 | memcpy(addr_key, addr, 16); |
1454 | 1 | fill_dummy_ip6(tp); |
1455 | 1 | wmem_map_insert(ipv6_hash_table, addr_key, tp); |
1456 | 1 | } else if (tp->flags & TRIED_OR_RESOLVED_MASK) { |
1457 | 0 | return tp; |
1458 | 0 | } |
1459 | | |
1460 | | /* |
1461 | | * This hasn't been resolved yet, and we haven't tried to |
1462 | | * resolve it already. |
1463 | | */ |
1464 | | |
1465 | 1 | if (!gbl_resolv_flags.network_name) |
1466 | 1 | return tp; |
1467 | | |
1468 | 0 | if (gbl_resolv_flags.use_external_net_name_resolver) { |
1469 | 0 | tp->flags |= TRIED_RESOLVE_ADDRESS; |
1470 | |
|
1471 | 0 | if (async_dns_initialized) { |
1472 | | /* c-ares is initialized, so we can use it */ |
1473 | 0 | if (resolve_synchronously || name_resolve_concurrency == 0) { |
1474 | | /* |
1475 | | * Either all names are to be resolved synchronously or |
1476 | | * the concurrency level is 0; do the resolution |
1477 | | * synchronously. |
1478 | | */ |
1479 | 0 | sync_lookup_ip6(addr); |
1480 | 0 | } else { |
1481 | | /* |
1482 | | * Names are to be resolved asynchronously, and we |
1483 | | * allow at least one asynchronous request in flight; |
1484 | | * post an asynchronous request. |
1485 | | */ |
1486 | 0 | async_dns_queue_msg_t *caqm; |
1487 | |
|
1488 | 0 | caqm = wmem_new(addr_resolv_scope, async_dns_queue_msg_t); |
1489 | 0 | caqm->family = AF_INET6; |
1490 | 0 | memcpy(&caqm->addr.ip6, addr, sizeof(caqm->addr.ip6)); |
1491 | 0 | wmem_list_append(async_dns_queue_head, (void *) caqm); |
1492 | 0 | } |
1493 | 0 | } |
1494 | 0 | } |
1495 | |
|
1496 | 0 | return tp; |
1497 | | |
1498 | 1 | } /* host_lookup6 */ |
1499 | | |
1500 | | /* |
1501 | | * Ethernet / manufacturer resolution |
1502 | | * |
1503 | | * The following functions implement ethernet address resolution and |
1504 | | * ethers files parsing (see ethers(4)). |
1505 | | * |
1506 | | * The manuf file has the same format as ethers(4) except that names are |
1507 | | * truncated to MAXMANUFLEN-1 (8) characters and that an address contains |
1508 | | * only 3 bytes (instead of 6). |
1509 | | * |
1510 | | * Notes: |
1511 | | * |
1512 | | * I decide to not use the existing functions (see ethers(3) on some |
1513 | | * operating systems) for the following reasons: |
1514 | | * - performance gains (use of hash tables and some other enhancements), |
1515 | | * - use of two ethers files (system-wide and per user), |
1516 | | * - avoid the use of NIS maps, |
1517 | | * - lack of these functions on some systems. |
1518 | | * |
1519 | | * So the following functions do _not_ behave as the standard ones. |
1520 | | * |
1521 | | * -- Laurent. |
1522 | | */ |
1523 | | |
1524 | | /* |
1525 | | * Converts Ethernet addresses of the form aa:bb:cc or aa:bb:cc:dd:ee:ff/28. |
1526 | | * '-' is also supported as a separator. The |
1527 | | * octets must be exactly two hexadecimal characters and the mask must be either |
1528 | | * 28 or 36. Pre-condition: cp MUST be at least 21 bytes. |
1529 | | */ |
1530 | | static bool |
1531 | | parse_ether_address_fast(const unsigned char *cp, ether_t *eth, unsigned int *mask, |
1532 | | const bool accept_mask) |
1533 | 0 | { |
1534 | | /* XXX copied from strutil.c */ |
1535 | | /* a map from ASCII hex chars to their value */ |
1536 | 0 | static const int8_t str_to_nibble[256] = { |
1537 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1538 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1539 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1540 | 0 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, |
1541 | 0 | -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1542 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1543 | 0 | -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1544 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1545 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1546 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1547 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1548 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1549 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1550 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1551 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
1552 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
1553 | 0 | }; |
1554 | 0 | const uint8_t *str_to_nibble_usg = (const uint8_t *)str_to_nibble; |
1555 | |
|
1556 | 0 | unsigned char sep = cp[2]; |
1557 | 0 | if ((sep != ':' && sep != '-') || cp[5] != sep) { |
1558 | | /* Unexpected separators. */ |
1559 | 0 | return false; |
1560 | 0 | } |
1561 | | |
1562 | | /* N.B. store octet values in an int to detect invalid (-1) entries */ |
1563 | 0 | int num0 = (str_to_nibble_usg[cp[0]] << 4) | (int8_t)str_to_nibble_usg[cp[1]]; |
1564 | 0 | int num1 = (str_to_nibble_usg[cp[3]] << 4) | (int8_t)str_to_nibble_usg[cp[4]]; |
1565 | 0 | int num2 = (str_to_nibble_usg[cp[6]] << 4) | (int8_t)str_to_nibble_usg[cp[7]]; |
1566 | |
|
1567 | 0 | if ((num0 | num1 | num2) & 0x100) { |
1568 | | /* Not hexadecimal numbers. */ |
1569 | 0 | return false; |
1570 | 0 | } |
1571 | | |
1572 | 0 | eth->addr[0] = (uint8_t)num0; |
1573 | 0 | eth->addr[1] = (uint8_t)num1; |
1574 | 0 | eth->addr[2] = (uint8_t)num2; |
1575 | |
|
1576 | 0 | if (cp[8] == '\0' && accept_mask) { |
1577 | | /* Indicate that this is a manufacturer ID (0 is not allowed as a mask). */ |
1578 | 0 | *mask = 0; |
1579 | 0 | return true; |
1580 | 0 | } else if (cp[8] != sep || !accept_mask) { |
1581 | | /* Format not handled by this fast path. */ |
1582 | 0 | return false; |
1583 | 0 | } |
1584 | | |
1585 | | /* N.B. store octet values in an int to detect invalid (-1) entries */ |
1586 | 0 | int num3 = (str_to_nibble_usg[cp[9]] << 4) | (int8_t)str_to_nibble_usg[cp[10]]; |
1587 | 0 | int num4 = (str_to_nibble_usg[cp[12]] << 4) | (int8_t)str_to_nibble_usg[cp[13]]; |
1588 | 0 | int num5 = (str_to_nibble_usg[cp[15]] << 4) | (int8_t)str_to_nibble_usg[cp[16]]; |
1589 | |
|
1590 | 0 | if (((num3 | num4 | num5) & 0x100) || cp[11] != sep || cp[14] != sep) { |
1591 | | /* Not hexadecimal numbers or invalid separators. */ |
1592 | 0 | return false; |
1593 | 0 | } |
1594 | | |
1595 | 0 | eth->addr[3] = (uint8_t)num3; |
1596 | 0 | eth->addr[4] = (uint8_t)num4; |
1597 | 0 | eth->addr[5] = (uint8_t)num5; |
1598 | 0 | if (cp[17] == '\0') { |
1599 | | /* We got 6 bytes, so this is a MAC address (48 is not allowed as a mask). */ |
1600 | 0 | *mask = 48; |
1601 | 0 | return true; |
1602 | 0 | } else if (cp[17] != '/' || cp[20] != '\0') { |
1603 | | /* Format not handled by this fast path. */ |
1604 | 0 | return false; |
1605 | 0 | } |
1606 | | |
1607 | 0 | int m1 = cp[18]; |
1608 | 0 | int m2 = cp[19]; |
1609 | 0 | if (m1 == '3' && m2 == '6') { /* Mask /36 */ |
1610 | 0 | eth->addr[4] &= 0xf0; |
1611 | 0 | eth->addr[5] = 0; |
1612 | 0 | *mask = 36; |
1613 | 0 | return true; |
1614 | 0 | } |
1615 | 0 | if (m1 == '2' && m2 == '8') { /* Mask /28 */ |
1616 | 0 | eth->addr[3] &= 0xf0; |
1617 | 0 | eth->addr[4] = 0; |
1618 | 0 | eth->addr[5] = 0; |
1619 | 0 | *mask = 28; |
1620 | 0 | return true; |
1621 | 0 | } |
1622 | | /* Unsupported mask */ |
1623 | 0 | return false; |
1624 | 0 | } |
1625 | | |
1626 | | /* |
1627 | | * If "accept_mask" is false, cp must point to an address that consists |
1628 | | * of exactly 6 (EUI-48) or 8 (EUI-64) bytes. |
1629 | | * If "accept_mask" is true, parse an up-to-6-byte sequence with an optional |
1630 | | * mask. |
1631 | | */ |
1632 | | static bool |
1633 | | parse_ether_address(const char *cp, ether_t *eth, unsigned int *mask, |
1634 | | const bool accept_mask) |
1635 | 0 | { |
1636 | 0 | int i; |
1637 | 0 | unsigned long num; |
1638 | 0 | char *p; |
1639 | 0 | char sep = '\0'; |
1640 | |
|
1641 | 0 | for (i = 0; i < EUI64_ADDR_LEN; i++) { |
1642 | | /* Get a hex number, 1 or 2 digits, no sign characters allowed. */ |
1643 | 0 | if (!g_ascii_isxdigit(*cp)) |
1644 | 0 | return false; |
1645 | 0 | num = strtoul(cp, &p, 16); |
1646 | 0 | if (p == cp) |
1647 | 0 | return false; /* failed */ |
1648 | 0 | if (num > 0xFF) |
1649 | 0 | return false; /* not a valid octet */ |
1650 | 0 | eth->addr[i] = (uint8_t) num; |
1651 | 0 | cp = p; /* skip past the number */ |
1652 | | |
1653 | | /* OK, what character terminated the octet? */ |
1654 | 0 | if (*cp == '/') { |
1655 | | /* "/" - this has a mask. */ |
1656 | 0 | if (!accept_mask) { |
1657 | | /* Entries with masks are not allowed in this file. */ |
1658 | 0 | return false; |
1659 | 0 | } |
1660 | 0 | cp++; /* skip past the '/' to get to the mask */ |
1661 | 0 | if (!g_ascii_isdigit(*cp)) |
1662 | 0 | return false; /* no sign allowed */ |
1663 | 0 | num = strtoul(cp, &p, 10); |
1664 | 0 | if (p == cp) |
1665 | 0 | return false; /* failed */ |
1666 | 0 | cp = p; /* skip past the number */ |
1667 | 0 | if (*cp != '\0' && !g_ascii_isspace(*cp)) |
1668 | 0 | return false; /* bogus terminator */ |
1669 | 0 | if (num == 0 || num >= 48) |
1670 | 0 | return false; /* bogus mask */ |
1671 | | /* Mask out the bits not covered by the mask */ |
1672 | 0 | *mask = (int)num; |
1673 | 0 | for (i = 0; num >= 8; i++, num -= 8) |
1674 | 0 | ; /* skip octets entirely covered by the mask */ |
1675 | | /* Mask out the first masked octet */ |
1676 | 0 | eth->addr[i] &= (0xFF << (8 - num)); |
1677 | 0 | i++; |
1678 | | /* Mask out completely-masked-out octets */ |
1679 | 0 | for (; i < 6; i++) |
1680 | 0 | eth->addr[i] = 0; |
1681 | 0 | return true; |
1682 | 0 | } |
1683 | 0 | if (*cp == '\0') { |
1684 | | /* We're at the end of the address, and there's no mask. */ |
1685 | 0 | if (i == 2) { |
1686 | | /* We got 3 bytes, so this is a manufacturer ID. */ |
1687 | 0 | if (!accept_mask) { |
1688 | | /* Manufacturer IDs are not allowed in this file */ |
1689 | 0 | return false; |
1690 | 0 | } |
1691 | | /* Indicate that this is a manufacturer ID (0 is not allowed |
1692 | | as a mask). */ |
1693 | 0 | *mask = 0; |
1694 | 0 | return true; |
1695 | 0 | } |
1696 | | |
1697 | 0 | if (i == 5) { |
1698 | | /* We got 6 bytes, so this is a MAC address (48 is not allowed as a mask). */ |
1699 | 0 | if (mask) { |
1700 | 0 | *mask = 48; |
1701 | 0 | } |
1702 | 0 | return true; |
1703 | 0 | } |
1704 | | |
1705 | 0 | if (i == 7) { |
1706 | | /* We got 8 bytes, so this is a EUI-64 address (64 is not allowed as a mask). */ |
1707 | 0 | if (mask) { |
1708 | 0 | *mask = 64; |
1709 | 0 | } |
1710 | 0 | return true; |
1711 | 0 | } |
1712 | | |
1713 | | /* We didn't get 3 or 6 or 8 bytes, and there's no mask; this is |
1714 | | illegal. */ |
1715 | 0 | return false; |
1716 | 0 | } else { |
1717 | 0 | if (sep == '\0') { |
1718 | | /* We don't know the separator used in this number; it can either |
1719 | | be ':', '-', or '.'. */ |
1720 | 0 | if (*cp != ':' && *cp != '-' && *cp != '.') |
1721 | 0 | return false; |
1722 | 0 | sep = *cp; /* subsequent separators must be the same */ |
1723 | 0 | } else { |
1724 | | /* It has to be the same as the first separator */ |
1725 | 0 | if (*cp != sep) |
1726 | 0 | return false; |
1727 | 0 | } |
1728 | 0 | } |
1729 | 0 | cp++; |
1730 | 0 | } |
1731 | | |
1732 | 0 | return true; |
1733 | 0 | } |
1734 | | |
1735 | | static int |
1736 | | parse_ether_line(char *line, ether_t *eth, unsigned int *mask, |
1737 | | const bool accept_mask) |
1738 | 0 | { |
1739 | | /* |
1740 | | * See the ethers(4) or ethers(5) man page for ethers file format |
1741 | | * (not available on all systems). |
1742 | | * We allow both ethernet address separators (':' and '-'), |
1743 | | * as well as Wireshark's '.' separator. |
1744 | | */ |
1745 | |
|
1746 | 0 | char *cp; |
1747 | |
|
1748 | 0 | line = g_strstrip(line); |
1749 | 0 | if (line[0] == '\0' || line[0] == '#') |
1750 | 0 | return -1; |
1751 | | |
1752 | 0 | if ((cp = strchr(line, '#'))) { |
1753 | 0 | *cp = '\0'; |
1754 | 0 | g_strchomp(line); |
1755 | 0 | } |
1756 | |
|
1757 | 0 | if ((cp = strtok(line, " \t")) == NULL) |
1758 | 0 | return -1; |
1759 | | |
1760 | | /* First try to match the common format for the large ethers file. */ |
1761 | 0 | if (!parse_ether_address_fast((const uint8_t*)cp, eth, mask, accept_mask)) { |
1762 | | /* Fallback for the well-known addresses (wka) file. */ |
1763 | 0 | if (!parse_ether_address(cp, eth, mask, accept_mask)) |
1764 | 0 | return -1; |
1765 | 0 | } |
1766 | | |
1767 | 0 | if ((cp = strtok(NULL, " \t")) == NULL) |
1768 | 0 | return -1; |
1769 | | |
1770 | 0 | (void) g_strlcpy(eth->name, cp, MAXNAMELEN); |
1771 | |
|
1772 | 0 | if ((cp = strtok(NULL, "\t")) != NULL) |
1773 | 0 | { |
1774 | 0 | (void) g_strlcpy(eth->longname, cp, MAXNAMELEN); |
1775 | 0 | } else { |
1776 | | /* Make the long name the short name */ |
1777 | 0 | (void) g_strlcpy(eth->longname, eth->name, MAXNAMELEN); |
1778 | 0 | } |
1779 | |
|
1780 | 0 | return 0; |
1781 | |
|
1782 | 0 | } /* parse_ether_line */ |
1783 | | |
1784 | | static FILE *eth_p; |
1785 | | |
1786 | | static void |
1787 | | set_ethent(char *path) |
1788 | 56 | { |
1789 | 56 | if (eth_p) |
1790 | 0 | rewind(eth_p); |
1791 | 56 | else |
1792 | 56 | eth_p = ws_fopen(path, "r"); |
1793 | 56 | } |
1794 | | |
1795 | | static void |
1796 | | end_ethent(void) |
1797 | 56 | { |
1798 | 56 | if (eth_p) { |
1799 | 0 | fclose(eth_p); |
1800 | 0 | eth_p = NULL; |
1801 | 0 | } |
1802 | 56 | } |
1803 | | |
1804 | | static ether_t * |
1805 | | get_ethent(unsigned int *mask, const bool accept_mask) |
1806 | 56 | { |
1807 | | |
1808 | 56 | static ether_t eth; |
1809 | 56 | char buf[MAX_LINELEN]; |
1810 | | |
1811 | 56 | if (eth_p == NULL) |
1812 | 56 | return NULL; |
1813 | | |
1814 | 0 | while (fgetline(buf, sizeof(buf), eth_p) >= 0) { |
1815 | 0 | if (parse_ether_line(buf, ð, mask, accept_mask) == 0) { |
1816 | 0 | return ð |
1817 | 0 | } |
1818 | 0 | } |
1819 | | |
1820 | 0 | return NULL; |
1821 | |
|
1822 | 0 | } /* get_ethent */ |
1823 | | |
1824 | | static hashmanuf_t * |
1825 | | manuf_hash_new_entry(const uint8_t *addr, const char* name, const char* longname) |
1826 | 8.26k | { |
1827 | 8.26k | unsigned manuf_key; |
1828 | 8.26k | hashmanuf_t *manuf_value; |
1829 | 8.26k | char *endp; |
1830 | 8.26k | size_t attempted_size; |
1831 | | |
1832 | | /* manuf needs only the 3 most significant octets of the ethernet address */ |
1833 | 8.26k | manuf_key = (addr[0] << 16) + (addr[1] << 8) + addr[2]; |
1834 | 8.26k | manuf_value = wmem_new(addr_resolv_scope, hashmanuf_t); |
1835 | | |
1836 | 8.26k | memcpy(manuf_value->addr, addr, 3); |
1837 | 8.26k | if (name != NULL) { |
1838 | 1.03k | (void) g_strlcpy(manuf_value->resolved_name, name, MAXNAMELEN); |
1839 | 1.03k | manuf_value->flags = NAME_RESOLVED; |
1840 | 1.03k | if (longname != NULL) { |
1841 | 1.03k | attempted_size = g_strlcpy(manuf_value->resolved_longname, longname, MAXNAMELEN); |
1842 | 1.03k | if (attempted_size >= MAXNAMELEN) { |
1843 | 1 | ws_utf8_truncate(manuf_value->resolved_longname, MAXNAMELEN - 1); |
1844 | 1 | } |
1845 | 1.03k | } |
1846 | 0 | else { |
1847 | 0 | (void) g_strlcpy(manuf_value->resolved_longname, name, MAXNAMELEN); |
1848 | 0 | } |
1849 | 1.03k | } |
1850 | 7.23k | else { |
1851 | 7.23k | manuf_value->flags = 0; |
1852 | 7.23k | manuf_value->resolved_name[0] = '\0'; |
1853 | 7.23k | manuf_value->resolved_longname[0] = '\0'; |
1854 | 7.23k | } |
1855 | | /* Values returned by bytes_to_hexstr_punct() are *not* null-terminated */ |
1856 | 8.26k | endp = bytes_to_hexstr_punct(manuf_value->hexaddr, addr, sizeof(manuf_value->addr), ':'); |
1857 | 8.26k | *endp = '\0'; |
1858 | | |
1859 | 8.26k | wmem_map_insert(manuf_hashtable, GUINT_TO_POINTER(manuf_key), manuf_value); |
1860 | 8.26k | return manuf_value; |
1861 | 8.26k | } |
1862 | | |
1863 | | static hashwka_t* |
1864 | | wka_hash_new_entry(const uint8_t *addr, char* name) |
1865 | 0 | { |
1866 | 0 | uint8_t *wka_key; |
1867 | 0 | hashwka_t *wka_value; |
1868 | |
|
1869 | 0 | wka_key = (uint8_t *)wmem_alloc(addr_resolv_scope, 6); |
1870 | 0 | memcpy(wka_key, addr, 6); |
1871 | |
|
1872 | 0 | wka_value = (hashwka_t*)wmem_new(addr_resolv_scope, hashwka_t); |
1873 | 0 | wka_value->flags = NAME_RESOLVED; |
1874 | 0 | wka_value->name = wmem_strdup(addr_resolv_scope, name); |
1875 | |
|
1876 | 0 | wmem_map_insert(wka_hashtable, wka_key, wka_value); |
1877 | 0 | return wka_value; |
1878 | 0 | } |
1879 | | |
1880 | | static void |
1881 | | add_manuf_name(const uint8_t *addr, unsigned int mask, char *name, char *longname) |
1882 | 0 | { |
1883 | 0 | switch (mask) |
1884 | 0 | { |
1885 | 0 | case 0: |
1886 | 0 | { |
1887 | | /* This is a manufacturer ID; add it to the manufacturer ID hash table */ |
1888 | 0 | hashmanuf_t *entry = manuf_hash_new_entry(addr, name, longname); |
1889 | 0 | entry->flags |= STATIC_HOSTNAME; |
1890 | 0 | break; |
1891 | 0 | } |
1892 | 0 | case 48: |
1893 | 0 | { |
1894 | | /* This is a well-known MAC address; add it to the Ethernet hash table */ |
1895 | 0 | add_eth_name(addr, name, true); |
1896 | 0 | break; |
1897 | 0 | } |
1898 | 0 | default: |
1899 | 0 | { |
1900 | | /* This is a range of well-known addresses; add it to the well-known-address table */ |
1901 | 0 | hashwka_t *entry = wka_hash_new_entry(addr, name); |
1902 | 0 | entry->flags |= STATIC_HOSTNAME; |
1903 | 0 | break; |
1904 | 0 | } |
1905 | 0 | } |
1906 | 0 | } /* add_manuf_name */ |
1907 | | |
1908 | | /* XXX: manuf_name_lookup returns a hashmanuf_t*, which cannot hold a 28 or |
1909 | | * 36 bit MA-M or MA-S. So it returns those as unresolved. For EUI-48 and |
1910 | | * EUI-64, MA-M and MA-S should be checked for separately in the global |
1911 | | * tables. |
1912 | | * |
1913 | | * XXX - size_t is used only in a ws_return_val_if() that checks |
1914 | | * whether the argument has at least 3 bytes; that's done only if |
1915 | | * assertions are enabled, so it's used only if assertions are |
1916 | | * enabled. This means that, if assertions aren't enabled, a |
1917 | | * warning that the argument is unused will be issued by at least |
1918 | | * some compilers, so we mark it as unused. Should we do that |
1919 | | * check unconditionally, and just emit a warning if assertions |
1920 | | * are enabled? |
1921 | | */ |
1922 | | static hashmanuf_t * |
1923 | | manuf_name_lookup(const uint8_t *addr, size_t size _U_) |
1924 | 67.8k | { |
1925 | 67.8k | uint32_t manuf_key; |
1926 | 67.8k | uint8_t oct; |
1927 | 67.8k | hashmanuf_t *manuf_value; |
1928 | | |
1929 | 67.8k | ws_return_val_if(size < 3, NULL); |
1930 | | |
1931 | | /* manuf needs only the 3 most significant octets of the ethernet address */ |
1932 | 67.8k | manuf_key = addr[0]; |
1933 | 67.8k | manuf_key = manuf_key<<8; |
1934 | 67.8k | oct = addr[1]; |
1935 | 67.8k | manuf_key = manuf_key | oct; |
1936 | 67.8k | manuf_key = manuf_key<<8; |
1937 | 67.8k | oct = addr[2]; |
1938 | 67.8k | manuf_key = manuf_key | oct; |
1939 | | |
1940 | | |
1941 | | /* first try to find a "perfect match" */ |
1942 | 67.8k | manuf_value = (hashmanuf_t*)wmem_map_lookup(manuf_hashtable, GUINT_TO_POINTER(manuf_key)); |
1943 | 67.8k | if (manuf_value != NULL) { |
1944 | 58.0k | manuf_value->flags |= TRIED_RESOLVE_ADDRESS; |
1945 | 58.0k | return manuf_value; |
1946 | 58.0k | } |
1947 | | |
1948 | | /* Mask out the broadcast/multicast flag but not the locally |
1949 | | * administered flag as locally administered means: not assigned |
1950 | | * by the IEEE but the local administrator instead. |
1951 | | * 0x01 multicast / broadcast bit |
1952 | | * 0x02 locally administered bit */ |
1953 | 9.72k | if ((manuf_key & 0x00010000) != 0) { |
1954 | 4.96k | manuf_key &= 0x00FEFFFF; |
1955 | 4.96k | manuf_value = (hashmanuf_t*)wmem_map_lookup(manuf_hashtable, GUINT_TO_POINTER(manuf_key)); |
1956 | 4.96k | if (manuf_value != NULL) { |
1957 | 1.46k | manuf_value->flags |= TRIED_RESOLVE_ADDRESS; |
1958 | 1.46k | return manuf_value; |
1959 | 1.46k | } |
1960 | 4.96k | } |
1961 | | |
1962 | | /* Try the global manuf tables. */ |
1963 | 8.26k | const char *short_name, *long_name; |
1964 | | /* We can't insert a 28 or 36 bit entry into the used hash table. */ |
1965 | 8.26k | short_name = ws_manuf_lookup_oui24(addr, &long_name); |
1966 | 8.26k | if (short_name != NULL) { |
1967 | | /* Found it */ |
1968 | 1.03k | manuf_value = manuf_hash_new_entry(addr, short_name, long_name); |
1969 | 7.23k | } else { |
1970 | | /* Add the address as a hex string */ |
1971 | 7.23k | manuf_value = manuf_hash_new_entry(addr, NULL, NULL); |
1972 | 7.23k | } |
1973 | | |
1974 | 8.26k | manuf_value->flags |= TRIED_RESOLVE_ADDRESS; |
1975 | 8.26k | return manuf_value; |
1976 | | |
1977 | 9.72k | } /* manuf_name_lookup */ |
1978 | | |
1979 | | static char * |
1980 | | wka_name_lookup(const uint8_t *addr, const unsigned int mask) |
1981 | 477k | { |
1982 | 477k | uint8_t masked_addr[6]; |
1983 | 477k | unsigned num; |
1984 | 477k | int i; |
1985 | 477k | hashwka_t *value; |
1986 | | |
1987 | 477k | if (wka_hashtable == NULL) { |
1988 | 0 | return NULL; |
1989 | 0 | } |
1990 | | /* Get the part of the address covered by the mask. */ |
1991 | 1.78M | for (i = 0, num = mask; num >= 8; i++, num -= 8) |
1992 | 1.30M | masked_addr[i] = addr[i]; /* copy octets entirely covered by the mask */ |
1993 | | /* Mask out the first masked octet */ |
1994 | 477k | masked_addr[i] = addr[i] & (0xFF << (8 - num)); |
1995 | 477k | i++; |
1996 | | /* Zero out completely-masked-out octets */ |
1997 | 1.56M | for (; i < 6; i++) |
1998 | 1.08M | masked_addr[i] = 0; |
1999 | | |
2000 | 477k | value = (hashwka_t*)wmem_map_lookup(wka_hashtable, masked_addr); |
2001 | | |
2002 | 477k | if (value) { |
2003 | 0 | value->flags |= TRIED_RESOLVE_ADDRESS; |
2004 | 0 | return value->name; |
2005 | 0 | } |
2006 | | |
2007 | 477k | return NULL; |
2008 | | |
2009 | 477k | } /* wka_name_lookup */ |
2010 | | |
2011 | | unsigned get_hash_ether_status(hashether_t* ether) |
2012 | 0 | { |
2013 | 0 | return ether->flags; |
2014 | 0 | } |
2015 | | |
2016 | | bool get_hash_ether_used(hashether_t* ether) |
2017 | 0 | { |
2018 | 0 | return ((ether->flags & TRIED_OR_RESOLVED_MASK) == TRIED_OR_RESOLVED_MASK); |
2019 | 0 | } |
2020 | | |
2021 | | char* get_hash_ether_hexaddr(hashether_t* ether) |
2022 | 0 | { |
2023 | 0 | return ether->hexaddr; |
2024 | 0 | } |
2025 | | |
2026 | | char* get_hash_ether_resolved_name(hashether_t* ether) |
2027 | 0 | { |
2028 | 0 | return ether->resolved_name; |
2029 | 0 | } |
2030 | | |
2031 | | bool get_hash_wka_used(hashwka_t* wka) |
2032 | 0 | { |
2033 | 0 | return ((wka->flags & TRIED_OR_RESOLVED_MASK) == TRIED_OR_RESOLVED_MASK); |
2034 | 0 | } |
2035 | | |
2036 | | char* get_hash_wka_resolved_name(hashwka_t* wka) |
2037 | 0 | { |
2038 | 0 | return wka->name; |
2039 | 0 | } |
2040 | | |
2041 | | static unsigned |
2042 | | eth_addr_hash(const void *key) |
2043 | 84.7k | { |
2044 | 84.7k | return wmem_strong_hash((const uint8_t *)key, 6); |
2045 | 84.7k | } |
2046 | | |
2047 | | static gboolean |
2048 | | eth_addr_cmp(const void *a, const void *b) |
2049 | 62.0k | { |
2050 | 62.0k | return (memcmp(a, b, 6) == 0); |
2051 | 62.0k | } |
2052 | | |
2053 | | static unsigned |
2054 | | eui64_addr_hash(const void *key) |
2055 | 4.14k | { |
2056 | 4.14k | return wmem_strong_hash((const uint8_t *)key, EUI64_ADDR_LEN); |
2057 | 4.14k | } |
2058 | | |
2059 | | static gboolean |
2060 | | eui64_addr_cmp(const void *a, const void *b) |
2061 | 2.42k | { |
2062 | 2.42k | return (memcmp(a, b, EUI64_ADDR_LEN) == 0); |
2063 | 2.42k | } |
2064 | | |
2065 | | static void |
2066 | | initialize_ethers(const char* app_env_var_prefix) |
2067 | 28 | { |
2068 | 28 | ether_t *eth; |
2069 | 28 | unsigned mask = 0; |
2070 | | |
2071 | | /* hash table initialization */ |
2072 | 28 | ws_assert(wka_hashtable == NULL); |
2073 | 28 | wka_hashtable = wmem_map_new(addr_resolv_scope, eth_addr_hash, eth_addr_cmp); |
2074 | 28 | ws_assert(manuf_hashtable == NULL); |
2075 | 28 | manuf_hashtable = wmem_map_new(addr_resolv_scope, g_direct_hash, g_direct_equal); |
2076 | 28 | ws_assert(eth_hashtable == NULL); |
2077 | 28 | eth_hashtable = wmem_map_new(addr_resolv_scope, eth_addr_hash, eth_addr_cmp); |
2078 | 28 | ws_assert(eui64_hashtable == NULL); |
2079 | 28 | eui64_hashtable = wmem_map_new(addr_resolv_scope, eui64_addr_hash, eui64_addr_cmp); |
2080 | | |
2081 | | /* Compute the pathname of the ethers file. */ |
2082 | 28 | if (g_ethers_path == NULL) { |
2083 | 28 | g_ethers_path = g_build_filename(get_systemfile_dir(app_env_var_prefix), ENAME_ETHERS, NULL); |
2084 | 28 | } |
2085 | | |
2086 | | /* Compute the pathname of the personal ethers file. */ |
2087 | 28 | if (g_pethers_path == NULL) { |
2088 | | /* Check profile directory before personal configuration */ |
2089 | 28 | g_pethers_path = get_persconffile_path(ENAME_ETHERS, true, app_env_var_prefix); |
2090 | 28 | if (!file_exists(g_pethers_path)) { |
2091 | 28 | g_free(g_pethers_path); |
2092 | 28 | g_pethers_path = get_persconffile_path(ENAME_ETHERS, false, app_env_var_prefix); |
2093 | 28 | } |
2094 | 28 | } |
2095 | | |
2096 | | /* Compute the pathname of the global manuf file */ |
2097 | 28 | if (g_manuf_path == NULL) |
2098 | 28 | g_manuf_path = get_datafile_path(ENAME_MANUF, app_env_var_prefix); |
2099 | | /* Read it and initialize the hash table */ |
2100 | 28 | if (file_exists(g_manuf_path)) { |
2101 | 0 | set_ethent(g_manuf_path); |
2102 | 0 | while ((eth = get_ethent(&mask, true))) { |
2103 | 0 | add_manuf_name(eth->addr, mask, eth->name, eth->longname); |
2104 | 0 | } |
2105 | 0 | end_ethent(); |
2106 | 0 | } |
2107 | | |
2108 | | /* Compute the pathname of the personal manuf file */ |
2109 | 28 | if (g_pmanuf_path == NULL) { |
2110 | | /* Check profile directory before personal configuration */ |
2111 | 28 | g_pmanuf_path = get_persconffile_path(ENAME_MANUF, true, app_env_var_prefix); |
2112 | 28 | if (!file_exists(g_pmanuf_path)) { |
2113 | 28 | g_free(g_pmanuf_path); |
2114 | 28 | g_pmanuf_path = get_persconffile_path(ENAME_MANUF, false, app_env_var_prefix); |
2115 | 28 | } |
2116 | 28 | } |
2117 | | /* Read it and initialize the hash table */ |
2118 | 28 | if (file_exists(g_pmanuf_path)) { |
2119 | 0 | set_ethent(g_pmanuf_path); |
2120 | 0 | while ((eth = get_ethent(&mask, true))) { |
2121 | 0 | add_manuf_name(eth->addr, mask, eth->name, eth->longname); |
2122 | 0 | } |
2123 | 0 | end_ethent(); |
2124 | 0 | } |
2125 | | |
2126 | | /* Compute the pathname of the wka file */ |
2127 | 28 | if (g_wka_path == NULL) |
2128 | 28 | g_wka_path = get_datafile_path(ENAME_WKA, app_env_var_prefix); |
2129 | | |
2130 | | /* Read it and initialize the hash table */ |
2131 | 28 | set_ethent(g_wka_path); |
2132 | 28 | while ((eth = get_ethent(&mask, true))) { |
2133 | 0 | add_manuf_name(eth->addr, mask, eth->name, eth->longname); |
2134 | 0 | } |
2135 | 28 | end_ethent(); |
2136 | | |
2137 | | /* Look at the ethers files last. These are set as static names, |
2138 | | * so they override earlier entries, and the ones we read last |
2139 | | * take precedence. Order of precedence is personal ethers file, |
2140 | | * global ethers file, wka file, personal manuf file, global manuf |
2141 | | * file, and then non-static sources like ARP Eth -> IP hostname |
2142 | | * discovery (if enabled), NRB entries (if wiretap adds support for |
2143 | | * EUI-48 in NRBs), etc. |
2144 | | * XXX: What _is_ the proper order of precedence, and should it |
2145 | | * be configurable? (cf. #18075) */ |
2146 | 28 | set_ethent(g_ethers_path); |
2147 | 28 | while ((eth = get_ethent(&mask, false))) { |
2148 | 0 | if (mask == 48) { |
2149 | 0 | add_eth_name(eth->addr, eth->name, true); |
2150 | 0 | } else if (mask == 64) { |
2151 | 0 | add_eui64_name(eth->addr, eth->name, true); |
2152 | 0 | } |
2153 | 0 | } |
2154 | 28 | end_ethent(); |
2155 | | |
2156 | 28 | if (file_exists(g_pethers_path)) { |
2157 | 0 | set_ethent(g_pethers_path); |
2158 | 0 | while ((eth = get_ethent(&mask, false))) { |
2159 | 0 | if (mask == 48) { |
2160 | 0 | add_eth_name(eth->addr, eth->name, true); |
2161 | 0 | } else if (mask == 64) { |
2162 | 0 | add_eui64_name(eth->addr, eth->name, true); |
2163 | 0 | } |
2164 | 0 | } |
2165 | 0 | end_ethent(); |
2166 | 0 | } |
2167 | | |
2168 | 28 | } /* initialize_ethers */ |
2169 | | |
2170 | | static void |
2171 | | ethers_cleanup(void) |
2172 | 14 | { |
2173 | 14 | wka_hashtable = NULL; |
2174 | 14 | manuf_hashtable = NULL; |
2175 | 14 | eth_hashtable = NULL; |
2176 | 14 | eui64_hashtable = NULL; |
2177 | 14 | g_free(g_ethers_path); |
2178 | 14 | g_ethers_path = NULL; |
2179 | 14 | g_free(g_pethers_path); |
2180 | 14 | g_pethers_path = NULL; |
2181 | 14 | g_free(g_manuf_path); |
2182 | 14 | g_manuf_path = NULL; |
2183 | 14 | g_free(g_pmanuf_path); |
2184 | 14 | g_pmanuf_path = NULL; |
2185 | 14 | g_free(g_wka_path); |
2186 | 14 | g_wka_path = NULL; |
2187 | 14 | } |
2188 | | |
2189 | | static void |
2190 | | eth_resolved_name_fill(hashether_t *tp, const char *name, unsigned mask, const uint8_t *addr) |
2191 | 142 | { |
2192 | 142 | switch (mask) { |
2193 | 0 | case 24: |
2194 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x:%02x:%02x", |
2195 | 0 | name, addr[3], addr[4], addr[5]); |
2196 | 0 | break; |
2197 | 131 | case 28: |
2198 | 131 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%01x:%02x:%02x", |
2199 | 131 | name, addr[3] & 0x0F, addr[4], addr[5]); |
2200 | 131 | break; |
2201 | 11 | case 36: |
2202 | 11 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%01x:%02x", |
2203 | 11 | name, addr[4] & 0x0F, addr[5]); |
2204 | 11 | break; |
2205 | 0 | default: // Future-proof generic algorithm |
2206 | 0 | { |
2207 | 0 | unsigned bytes = mask / 8; |
2208 | 0 | unsigned bitmask = mask % 8; |
2209 | |
|
2210 | 0 | int pos = snprintf(tp->resolved_name, MAXNAMELEN, "%s", name); |
2211 | 0 | if (pos >= MAXNAMELEN) return; |
2212 | | |
2213 | 0 | if (bytes < 6) { |
2214 | 0 | pos += snprintf(tp->resolved_name + pos, MAXNAMELEN - pos, |
2215 | 0 | bitmask >= 4 ? "_%01x" : "_%02x", |
2216 | 0 | addr[bytes] & (0xFF >> bitmask)); |
2217 | 0 | bytes++; |
2218 | 0 | } |
2219 | |
|
2220 | 0 | while (bytes < 6) { |
2221 | 0 | if (pos >= MAXNAMELEN) return; |
2222 | 0 | pos += snprintf(tp->resolved_name + pos, MAXNAMELEN - pos, ":%02x", |
2223 | 0 | addr[bytes]); |
2224 | 0 | bytes++; |
2225 | 0 | } |
2226 | 0 | } |
2227 | 142 | } |
2228 | 142 | } |
2229 | | |
2230 | | /* Resolve ethernet address */ |
2231 | | static hashether_t * |
2232 | 11.3k | eth_addr_resolve(hashether_t *tp) { |
2233 | 11.3k | hashmanuf_t *manuf_value; |
2234 | 11.3k | const uint8_t *addr = tp->addr; |
2235 | 11.3k | size_t addr_size = sizeof(tp->addr); |
2236 | | |
2237 | 11.3k | if (!(tp->flags & NAME_RESOLVED)) { |
2238 | 11.3k | unsigned mask; |
2239 | 11.3k | char *name; |
2240 | 11.3k | address ether_addr; |
2241 | | |
2242 | | /* Unknown name. Try looking for it in the well-known-address |
2243 | | tables for well-known address ranges smaller than 2^24. */ |
2244 | 11.3k | mask = 7; |
2245 | 90.6k | do { |
2246 | | /* Only the topmost 5 bytes participate fully */ |
2247 | 90.6k | if ((name = wka_name_lookup(addr, mask+40)) != NULL) { |
2248 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x", |
2249 | 0 | name, addr[5] & (0xFF >> mask)); |
2250 | 0 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2251 | 0 | return tp; |
2252 | 0 | } |
2253 | 90.6k | } while (mask--); |
2254 | | |
2255 | 11.3k | mask = 7; |
2256 | 90.6k | do { |
2257 | | /* Only the topmost 4 bytes participate fully */ |
2258 | 90.6k | if ((name = wka_name_lookup(addr, mask+32)) != NULL) { |
2259 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x:%02x", |
2260 | 0 | name, addr[4] & (0xFF >> mask), addr[5]); |
2261 | 0 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2262 | 0 | return tp; |
2263 | 0 | } |
2264 | 90.6k | } while (mask--); |
2265 | | |
2266 | 11.3k | mask = 7; |
2267 | 90.6k | do { |
2268 | | /* Only the topmost 3 bytes participate fully */ |
2269 | 90.6k | if ((name = wka_name_lookup(addr, mask+24)) != NULL) { |
2270 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x:%02x:%02x", |
2271 | 0 | name, addr[3] & (0xFF >> mask), addr[4], addr[5]); |
2272 | 0 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2273 | 0 | return tp; |
2274 | 0 | } |
2275 | 90.6k | } while (mask--); |
2276 | | |
2277 | | /* Now try looking in the manufacturer table. */ |
2278 | 11.3k | manuf_value = manuf_name_lookup(addr, addr_size); |
2279 | 11.3k | if ((manuf_value != NULL) && ((manuf_value->flags & NAME_RESOLVED) == NAME_RESOLVED)) { |
2280 | 2.38k | snprintf(tp->resolved_name, MAXNAMELEN, "%.*s_%02x:%02x:%02x", |
2281 | 2.38k | MAXNAMELEN - 10, manuf_value->resolved_name, addr[3], addr[4], addr[5]); |
2282 | 2.38k | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2283 | 2.38k | return tp; |
2284 | 2.38k | } |
2285 | | |
2286 | | /* Now try looking for it in the well-known-address |
2287 | | tables for well-known address ranges larger than 2^24. */ |
2288 | 8.95k | mask = 7; |
2289 | 71.6k | do { |
2290 | | /* Only the topmost 2 bytes participate fully */ |
2291 | 71.6k | if ((name = wka_name_lookup(addr, mask+16)) != NULL) { |
2292 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x:%02x:%02x:%02x", |
2293 | 0 | name, addr[2] & (0xFF >> mask), addr[3], addr[4], |
2294 | 0 | addr[5]); |
2295 | 0 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2296 | 0 | return tp; |
2297 | 0 | } |
2298 | 71.6k | } while (mask--); |
2299 | | |
2300 | 8.95k | mask = 7; |
2301 | 71.6k | do { |
2302 | | /* Only the topmost byte participates fully */ |
2303 | 71.6k | if ((name = wka_name_lookup(addr, mask+8)) != NULL) { |
2304 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x:%02x:%02x:%02x:%02x", |
2305 | 0 | name, addr[1] & (0xFF >> mask), addr[2], addr[3], |
2306 | 0 | addr[4], addr[5]); |
2307 | 0 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2308 | 0 | return tp; |
2309 | 0 | } |
2310 | 71.6k | } while (mask--); |
2311 | | |
2312 | 8.95k | mask = 7; |
2313 | 62.6k | do { |
2314 | | /* Not even the topmost byte participates fully */ |
2315 | 62.6k | if ((name = wka_name_lookup(addr, mask)) != NULL) { |
2316 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x:%02x:%02x:%02x:%02x:%02x", |
2317 | 0 | name, addr[0] & (0xFF >> mask), addr[1], addr[2], |
2318 | 0 | addr[3], addr[4], addr[5]); |
2319 | 0 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2320 | 0 | return tp; |
2321 | 0 | } |
2322 | 62.6k | } while (--mask); /* Work down to the last bit */ |
2323 | | |
2324 | | /* Now try looking in the global manuf data for a MA-M or MA-S |
2325 | | * match. We do this last so that the other files override this |
2326 | | * result. |
2327 | | */ |
2328 | 8.95k | const char *short_name, *long_name; |
2329 | 8.95k | short_name = ws_manuf_lookup(addr, &long_name, &mask); |
2330 | 8.95k | if (short_name != NULL) { |
2331 | 142 | if (mask == 24) { |
2332 | | /* This shouldn't happen as it should be handled above, |
2333 | | * but it doesn't hurt. |
2334 | | */ |
2335 | 0 | manuf_hash_new_entry(addr, short_name, long_name); |
2336 | 0 | } |
2337 | 142 | eth_resolved_name_fill(tp, short_name, mask, addr); |
2338 | 142 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2339 | 142 | return tp; |
2340 | 142 | } |
2341 | | /* No match whatsoever. */ |
2342 | 8.80k | set_address(ðer_addr, AT_ETHER, 6, addr); |
2343 | 8.80k | address_to_str_buf(ðer_addr, tp->resolved_name, MAXNAMELEN); |
2344 | 8.80k | return tp; |
2345 | 8.95k | } |
2346 | 0 | return tp; |
2347 | 11.3k | } /* eth_addr_resolve */ |
2348 | | |
2349 | | static hashether_t * |
2350 | | eth_hash_new_entry(const uint8_t *addr, const bool resolve) |
2351 | 11.3k | { |
2352 | 11.3k | hashether_t *tp; |
2353 | 11.3k | char *endp; |
2354 | | |
2355 | 11.3k | tp = wmem_new(addr_resolv_scope, hashether_t); |
2356 | 11.3k | memcpy(tp->addr, addr, sizeof(tp->addr)); |
2357 | 11.3k | tp->flags = 0; |
2358 | | /* Values returned by bytes_to_hexstr_punct() are *not* null-terminated */ |
2359 | 11.3k | endp = bytes_to_hexstr_punct(tp->hexaddr, addr, sizeof(tp->addr), ':'); |
2360 | 11.3k | *endp = '\0'; |
2361 | 11.3k | tp->resolved_name[0] = '\0'; |
2362 | | |
2363 | 11.3k | if (resolve) |
2364 | 11.3k | eth_addr_resolve(tp); |
2365 | | |
2366 | 11.3k | wmem_map_insert(eth_hashtable, tp->addr, tp); |
2367 | | |
2368 | 11.3k | return tp; |
2369 | 11.3k | } /* eth_hash_new_entry */ |
2370 | | |
2371 | | static hashether_t * |
2372 | | add_eth_name(const uint8_t *addr, const char *name, bool static_entry) |
2373 | 0 | { |
2374 | 0 | hashether_t *tp; |
2375 | |
|
2376 | 0 | tp = (hashether_t *)wmem_map_lookup(eth_hashtable, addr); |
2377 | |
|
2378 | 0 | if (tp == NULL) { |
2379 | 0 | tp = eth_hash_new_entry(addr, false); |
2380 | 0 | } |
2381 | |
|
2382 | 0 | if (strcmp(tp->resolved_name, name) != 0 && (static_entry || !(tp->flags & STATIC_HOSTNAME))) { |
2383 | 0 | (void) g_strlcpy(tp->resolved_name, name, MAXNAMELEN); |
2384 | 0 | tp->flags |= NAME_RESOLVED; |
2385 | 0 | if (static_entry) { |
2386 | 0 | tp->flags |= STATIC_HOSTNAME; |
2387 | 0 | } |
2388 | 0 | new_resolved_objects = true; |
2389 | 0 | } |
2390 | |
|
2391 | 0 | return tp; |
2392 | 0 | } /* add_eth_name */ |
2393 | | |
2394 | | static hashether_t * |
2395 | | eth_name_lookup(const uint8_t *addr, const bool resolve) |
2396 | 73.4k | { |
2397 | 73.4k | hashether_t *tp; |
2398 | | |
2399 | 73.4k | tp = (hashether_t *)wmem_map_lookup(eth_hashtable, addr); |
2400 | | |
2401 | 73.4k | if (tp == NULL) { |
2402 | 11.3k | tp = eth_hash_new_entry(addr, resolve); |
2403 | 62.0k | } else { |
2404 | 62.0k | if (resolve && !(tp->flags & TRIED_OR_RESOLVED_MASK)) { |
2405 | 0 | eth_addr_resolve(tp); /* Found but needs to be resolved */ |
2406 | 0 | } |
2407 | 62.0k | } |
2408 | 73.4k | if (resolve) { |
2409 | 73.4k | tp->flags |= TRIED_RESOLVE_ADDRESS; |
2410 | 73.4k | } |
2411 | | |
2412 | 73.4k | return tp; |
2413 | | |
2414 | 73.4k | } /* eth_name_lookup */ |
2415 | | |
2416 | | static void |
2417 | | eui64_resolved_name_fill(hasheui64_t *tp, const char *name, unsigned mask, const uint8_t *addr) |
2418 | 1 | { |
2419 | 1 | switch (mask) { |
2420 | 0 | case 24: |
2421 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%02x:%02x:%02x:%02x:%02x", |
2422 | 0 | name, addr[3], addr[4], addr[5], addr[6], addr[7]); |
2423 | 0 | break; |
2424 | 1 | case 28: |
2425 | 1 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%01x:%02x:%02x:%02x:%02x", |
2426 | 1 | name, addr[3] & 0x0F, addr[4], addr[5], addr[6], addr[7]); |
2427 | 1 | break; |
2428 | 0 | case 36: |
2429 | 0 | snprintf(tp->resolved_name, MAXNAMELEN, "%s_%01x:%02x:%02x:%02x", |
2430 | 0 | name, addr[4] & 0x0F, addr[5], addr[6], addr[7]); |
2431 | 0 | break; |
2432 | 0 | default: // Future-proof generic algorithm |
2433 | 0 | { |
2434 | 0 | unsigned bytes = mask / 8; |
2435 | 0 | unsigned bitmask = mask % 8; |
2436 | |
|
2437 | 0 | int pos = snprintf(tp->resolved_name, MAXNAMELEN, "%s", name); |
2438 | 0 | if (pos >= MAXNAMELEN) return; |
2439 | | |
2440 | 0 | if (bytes < EUI64_ADDR_LEN) { |
2441 | 0 | pos += snprintf(tp->resolved_name + pos, MAXNAMELEN - pos, |
2442 | 0 | bitmask >= 4 ? "_%01x" : "_%02x", |
2443 | 0 | addr[bytes] & (0xFF >> bitmask)); |
2444 | 0 | bytes++; |
2445 | 0 | } |
2446 | |
|
2447 | 0 | while (bytes < EUI64_ADDR_LEN) { |
2448 | 0 | if (pos >= MAXNAMELEN) return; |
2449 | 0 | pos += snprintf(tp->resolved_name + pos, MAXNAMELEN - pos, ":%02x", |
2450 | 0 | addr[bytes]); |
2451 | 0 | bytes++; |
2452 | 0 | } |
2453 | 0 | } |
2454 | 1 | } |
2455 | 1 | } |
2456 | | |
2457 | | /* Resolve EUI-64 address */ |
2458 | | static hasheui64_t * |
2459 | | eui64_addr_resolve(hasheui64_t *tp) |
2460 | 862 | { |
2461 | 862 | hashmanuf_t *manuf_value; |
2462 | 862 | const uint8_t *addr = tp->addr; |
2463 | 862 | size_t addr_size = sizeof(tp->addr); |
2464 | | |
2465 | 862 | if (!(tp->flags & NAME_RESOLVED)) { |
2466 | 862 | unsigned mask; |
2467 | 862 | address eui64_addr; |
2468 | | /* manuf_name_lookup returns a hashmanuf_t* that covers an entire /24, |
2469 | | * so we can't properly use it for MA-M and MA-S. We do want to check |
2470 | | * it first so it also covers the user-defined tables. |
2471 | | */ |
2472 | 862 | manuf_value = manuf_name_lookup(addr, addr_size); |
2473 | 862 | if ((manuf_value != NULL) && ((manuf_value->flags & NAME_RESOLVED) == NAME_RESOLVED)) { |
2474 | 94 | snprintf(tp->resolved_name, MAXNAMELEN, "%.*s_%02x:%02x:%02x:%02x:%02x", |
2475 | 94 | MAXNAMELEN - 16, manuf_value->resolved_name, addr[3], addr[4], addr[5], addr[6], addr[7]); |
2476 | 94 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2477 | 94 | return tp; |
2478 | 94 | } |
2479 | | |
2480 | | /* Now try looking in the global manuf data for a MA-M or MA-S |
2481 | | * match. We do this last so that the other files override this |
2482 | | * result. |
2483 | | */ |
2484 | 768 | const char *short_name, *long_name; |
2485 | 768 | short_name = ws_manuf_lookup(addr, &long_name, &mask); |
2486 | 768 | if (short_name != NULL) { |
2487 | 1 | if (mask == 24) { |
2488 | | /* This shouldn't happen as it should be handled above, |
2489 | | * but it doesn't hurt. |
2490 | | */ |
2491 | 0 | manuf_hash_new_entry(addr, short_name, long_name); |
2492 | 0 | } |
2493 | 1 | eui64_resolved_name_fill(tp, short_name, mask, addr); |
2494 | 1 | tp->flags |= NAME_RESOLVED | NAME_RESOLVED_PREFIX; |
2495 | 1 | return tp; |
2496 | 1 | } |
2497 | | /* No match whatsoever. */ |
2498 | 767 | set_address(&eui64_addr, AT_EUI64, 8, addr); |
2499 | 767 | address_to_str_buf(&eui64_addr, tp->resolved_name, MAXNAMELEN); |
2500 | 767 | return tp; |
2501 | 768 | } |
2502 | | |
2503 | 0 | return tp; |
2504 | 862 | } /* eui64_addr_resolve */ |
2505 | | |
2506 | | static hasheui64_t * |
2507 | | eui64_hash_new_entry(const uint8_t *addr, const bool resolve) |
2508 | 862 | { |
2509 | 862 | hasheui64_t *tp; |
2510 | 862 | char *endp; |
2511 | | |
2512 | 862 | tp = wmem_new(addr_resolv_scope, hasheui64_t); |
2513 | 862 | memcpy(tp->addr, addr, sizeof(tp->addr)); |
2514 | 862 | tp->flags = 0; |
2515 | | /* Values returned by bytes_to_hexstr_punct() are *not* null-terminated */ |
2516 | 862 | endp = bytes_to_hexstr_punct(tp->hexaddr, addr, sizeof(tp->addr), ':'); |
2517 | 862 | *endp = '\0'; |
2518 | 862 | tp->resolved_name[0] = '\0'; |
2519 | | |
2520 | 862 | if (resolve) |
2521 | 862 | eui64_addr_resolve(tp); |
2522 | | |
2523 | 862 | wmem_map_insert(eui64_hashtable, tp->addr, tp); |
2524 | | |
2525 | 862 | return tp; |
2526 | 862 | } /* eui64_hash_new_entry */ |
2527 | | |
2528 | | static hasheui64_t * |
2529 | | add_eui64_name(const uint8_t *addr, const char *name, bool static_entry) |
2530 | 0 | { |
2531 | 0 | hasheui64_t *tp; |
2532 | |
|
2533 | 0 | tp = (hasheui64_t *)wmem_map_lookup(eui64_hashtable, addr); |
2534 | |
|
2535 | 0 | if (tp == NULL) { |
2536 | 0 | tp = eui64_hash_new_entry(addr, false); |
2537 | 0 | } |
2538 | |
|
2539 | 0 | if (strcmp(tp->resolved_name, name) != 0 && (static_entry || !(tp->flags & STATIC_HOSTNAME))) { |
2540 | 0 | (void) g_strlcpy(tp->resolved_name, name, MAXNAMELEN); |
2541 | 0 | tp->flags |= NAME_RESOLVED; |
2542 | 0 | if (static_entry) { |
2543 | 0 | tp->flags |= STATIC_HOSTNAME; |
2544 | 0 | } |
2545 | 0 | new_resolved_objects = true; |
2546 | 0 | } |
2547 | |
|
2548 | 0 | return tp; |
2549 | 0 | } /* add_eui64_name */ |
2550 | | |
2551 | | static hasheui64_t * |
2552 | | eui64_name_lookup(const uint8_t *addr, const bool resolve) |
2553 | 3.28k | { |
2554 | 3.28k | hasheui64_t *tp; |
2555 | | |
2556 | 3.28k | tp = (hasheui64_t *)wmem_map_lookup(eui64_hashtable, addr); |
2557 | | |
2558 | 3.28k | if (tp == NULL) { |
2559 | 862 | tp = eui64_hash_new_entry(addr, resolve); |
2560 | 2.42k | } else { |
2561 | 2.42k | if (resolve && !(tp->flags & TRIED_OR_RESOLVED_MASK)) { |
2562 | 0 | eui64_addr_resolve(tp); /* Found but needs to be resolved */ |
2563 | 0 | } |
2564 | 2.42k | } |
2565 | 3.28k | if (resolve) { |
2566 | 3.28k | tp->flags |= TRIED_RESOLVE_ADDRESS; |
2567 | 3.28k | } |
2568 | | |
2569 | 3.28k | return tp; |
2570 | | |
2571 | 3.28k | } /* eui64_name_lookup */ |
2572 | | |
2573 | | /* IPXNETS */ |
2574 | | static int |
2575 | | parse_ipxnets_line(char *line, ipxnet_t *ipxnet) |
2576 | 0 | { |
2577 | | /* |
2578 | | * We allow three address separators (':', '-', and '.'), |
2579 | | * as well as no separators |
2580 | | */ |
2581 | |
|
2582 | 0 | char *cp; |
2583 | 0 | uint32_t a, a0, a1, a2, a3; |
2584 | 0 | bool found_single_number = false; |
2585 | |
|
2586 | 0 | if ((cp = strchr(line, '#'))) |
2587 | 0 | *cp = '\0'; |
2588 | |
|
2589 | 0 | if ((cp = strtok(line, " \t\n")) == NULL) |
2590 | 0 | return -1; |
2591 | | |
2592 | | /* Either fill a0,a1,a2,a3 and found_single_number is false, |
2593 | | * fill a and found_single_number is true, |
2594 | | * or return -1 |
2595 | | */ |
2596 | 0 | if (sscanf(cp, "%x:%x:%x:%x", &a0, &a1, &a2, &a3) != 4) { |
2597 | 0 | if (sscanf(cp, "%x-%x-%x-%x", &a0, &a1, &a2, &a3) != 4) { |
2598 | 0 | if (sscanf(cp, "%x.%x.%x.%x", &a0, &a1, &a2, &a3) != 4) { |
2599 | 0 | if (sscanf(cp, "%x", &a) == 1) { |
2600 | 0 | found_single_number = true; |
2601 | 0 | } |
2602 | 0 | else { |
2603 | 0 | return -1; |
2604 | 0 | } |
2605 | 0 | } |
2606 | 0 | } |
2607 | 0 | } |
2608 | | |
2609 | 0 | if ((cp = strtok(NULL, " \t\n")) == NULL) |
2610 | 0 | return -1; |
2611 | | |
2612 | 0 | if (found_single_number) { |
2613 | 0 | ipxnet->addr = a; |
2614 | 0 | } |
2615 | 0 | else { |
2616 | 0 | ipxnet->addr = (a0 << 24) | (a1 << 16) | (a2 << 8) | a3; |
2617 | 0 | } |
2618 | |
|
2619 | 0 | (void) g_strlcpy(ipxnet->name, cp, MAXNAMELEN); |
2620 | |
|
2621 | 0 | return 0; |
2622 | |
|
2623 | 0 | } /* parse_ipxnets_line */ |
2624 | | |
2625 | | static FILE *ipxnet_p; |
2626 | | |
2627 | | static void |
2628 | | set_ipxnetent(char *path) |
2629 | 0 | { |
2630 | 0 | if (ipxnet_p) |
2631 | 0 | rewind(ipxnet_p); |
2632 | 0 | else |
2633 | 0 | ipxnet_p = ws_fopen(path, "r"); |
2634 | 0 | } |
2635 | | |
2636 | | static void |
2637 | | end_ipxnetent(void) |
2638 | 0 | { |
2639 | 0 | if (ipxnet_p) { |
2640 | 0 | fclose(ipxnet_p); |
2641 | 0 | ipxnet_p = NULL; |
2642 | 0 | } |
2643 | 0 | } |
2644 | | |
2645 | | static ipxnet_t * |
2646 | | get_ipxnetent(void) |
2647 | 0 | { |
2648 | |
|
2649 | 0 | static ipxnet_t ipxnet; |
2650 | 0 | char buf[MAX_LINELEN]; |
2651 | |
|
2652 | 0 | if (ipxnet_p == NULL) |
2653 | 0 | return NULL; |
2654 | | |
2655 | 0 | while (fgetline(buf, sizeof(buf), ipxnet_p) >= 0) { |
2656 | 0 | if (parse_ipxnets_line(buf, &ipxnet) == 0) { |
2657 | 0 | return &ipxnet; |
2658 | 0 | } |
2659 | 0 | } |
2660 | | |
2661 | 0 | return NULL; |
2662 | |
|
2663 | 0 | } /* get_ipxnetent */ |
2664 | | |
2665 | | static ipxnet_t * |
2666 | | get_ipxnetbyaddr(uint32_t addr) |
2667 | 0 | { |
2668 | 0 | ipxnet_t *ipxnet; |
2669 | |
|
2670 | 0 | set_ipxnetent(g_ipxnets_path); |
2671 | |
|
2672 | 0 | while (((ipxnet = get_ipxnetent()) != NULL) && (addr != ipxnet->addr) ) ; |
2673 | |
|
2674 | 0 | if (ipxnet == NULL) { |
2675 | 0 | end_ipxnetent(); |
2676 | |
|
2677 | 0 | set_ipxnetent(g_pipxnets_path); |
2678 | |
|
2679 | 0 | while (((ipxnet = get_ipxnetent()) != NULL) && (addr != ipxnet->addr) ) |
2680 | 0 | ; |
2681 | |
|
2682 | 0 | end_ipxnetent(); |
2683 | 0 | } |
2684 | |
|
2685 | 0 | return ipxnet; |
2686 | |
|
2687 | 0 | } /* get_ipxnetbyaddr */ |
2688 | | |
2689 | | static void |
2690 | | initialize_ipxnets(const char* app_env_var_prefix) |
2691 | 28 | { |
2692 | | /* Compute the pathname of the ipxnets file. |
2693 | | * |
2694 | | * XXX - is there a notion of an "ipxnets file" in any flavor of |
2695 | | * UNIX, or with any add-on Netware package for UNIX? If not, |
2696 | | * should the UNIX version of the ipxnets file be in the datafile |
2697 | | * directory as well? |
2698 | | */ |
2699 | 28 | if (g_ipxnets_path == NULL) { |
2700 | 28 | g_ipxnets_path = wmem_strdup_printf(addr_resolv_scope, "%s" G_DIR_SEPARATOR_S "%s", |
2701 | 28 | get_systemfile_dir(app_env_var_prefix), ENAME_IPXNETS); |
2702 | 28 | } |
2703 | | |
2704 | | /* Set g_pipxnets_path here, but don't actually do anything |
2705 | | * with it. It's used in get_ipxnetbyaddr(). |
2706 | | */ |
2707 | 28 | if (g_pipxnets_path == NULL) { |
2708 | | /* Check profile directory before personal configuration */ |
2709 | 28 | g_pipxnets_path = get_persconffile_path(ENAME_IPXNETS, true, app_env_var_prefix); |
2710 | 28 | if (!file_exists(g_pipxnets_path)) { |
2711 | 28 | g_free(g_pipxnets_path); |
2712 | 28 | g_pipxnets_path = get_persconffile_path(ENAME_IPXNETS, false, app_env_var_prefix); |
2713 | 28 | } |
2714 | 28 | } |
2715 | | |
2716 | 28 | } /* initialize_ipxnets */ |
2717 | | |
2718 | | static void |
2719 | | ipx_name_lookup_cleanup(void) |
2720 | 14 | { |
2721 | 14 | g_ipxnets_path = NULL; |
2722 | 14 | g_free(g_pipxnets_path); |
2723 | 14 | g_pipxnets_path = NULL; |
2724 | 14 | } |
2725 | | |
2726 | | static char * |
2727 | | ipxnet_name_lookup(wmem_allocator_t *allocator, const unsigned addr) |
2728 | 0 | { |
2729 | 0 | hashipxnet_t *tp; |
2730 | 0 | ipxnet_t *ipxnet; |
2731 | |
|
2732 | 0 | tp = (hashipxnet_t *)wmem_map_lookup(ipxnet_hash_table, GUINT_TO_POINTER(addr)); |
2733 | 0 | if (tp == NULL) { |
2734 | 0 | tp = wmem_new(addr_resolv_scope, hashipxnet_t); |
2735 | 0 | wmem_map_insert(ipxnet_hash_table, GUINT_TO_POINTER(addr), tp); |
2736 | 0 | } else { |
2737 | 0 | return wmem_strdup(allocator, tp->name); |
2738 | 0 | } |
2739 | | |
2740 | | /* fill in a new entry */ |
2741 | | |
2742 | 0 | tp->addr = addr; |
2743 | |
|
2744 | 0 | if ( (ipxnet = get_ipxnetbyaddr(addr)) == NULL) { |
2745 | | /* unknown name */ |
2746 | 0 | snprintf(tp->name, MAXNAMELEN, "%X", addr); |
2747 | |
|
2748 | 0 | } else { |
2749 | 0 | (void) g_strlcpy(tp->name, ipxnet->name, MAXNAMELEN); |
2750 | 0 | } |
2751 | |
|
2752 | 0 | return wmem_strdup(allocator, tp->name); |
2753 | |
|
2754 | 0 | } /* ipxnet_name_lookup */ |
2755 | | |
2756 | | /* VLANS */ |
2757 | | static int |
2758 | | parse_vlan_line(char *line, vlan_t *vlan) |
2759 | 0 | { |
2760 | 0 | char *cp; |
2761 | 0 | uint16_t id; |
2762 | |
|
2763 | 0 | if ((cp = strchr(line, '#'))) |
2764 | 0 | *cp = '\0'; |
2765 | |
|
2766 | 0 | if ((cp = strtok(line, " \t\n")) == NULL) |
2767 | 0 | return -1; |
2768 | | |
2769 | 0 | if (sscanf(cp, "%" SCNu16, &id) == 1) { |
2770 | 0 | vlan->id = id; |
2771 | 0 | } |
2772 | 0 | else { |
2773 | 0 | return -1; |
2774 | 0 | } |
2775 | | |
2776 | 0 | if ((cp = strtok(NULL, "\t\n")) == NULL) |
2777 | 0 | return -1; |
2778 | | |
2779 | 0 | (void) g_strlcpy(vlan->name, cp, MAXVLANNAMELEN); |
2780 | |
|
2781 | 0 | return 0; |
2782 | |
|
2783 | 0 | } /* parse_vlan_line */ |
2784 | | |
2785 | | static FILE *vlan_p; |
2786 | | |
2787 | | static void |
2788 | | set_vlanent(char *path) |
2789 | 0 | { |
2790 | 0 | if (vlan_p) |
2791 | 0 | rewind(vlan_p); |
2792 | 0 | else |
2793 | 0 | vlan_p = ws_fopen(path, "r"); |
2794 | 0 | } |
2795 | | |
2796 | | static void |
2797 | | end_vlanent(void) |
2798 | 14 | { |
2799 | 14 | if (vlan_p) { |
2800 | 0 | fclose(vlan_p); |
2801 | 0 | vlan_p = NULL; |
2802 | 0 | } |
2803 | 14 | } |
2804 | | |
2805 | | static vlan_t * |
2806 | | get_vlanent(void) |
2807 | 0 | { |
2808 | |
|
2809 | 0 | static vlan_t vlan; |
2810 | 0 | char buf[MAX_LINELEN]; |
2811 | |
|
2812 | 0 | if (vlan_p == NULL) |
2813 | 0 | return NULL; |
2814 | | |
2815 | 0 | while (fgetline(buf, sizeof(buf), vlan_p) >= 0) { |
2816 | 0 | if (parse_vlan_line(buf, &vlan) == 0) { |
2817 | 0 | return &vlan; |
2818 | 0 | } |
2819 | 0 | } |
2820 | | |
2821 | 0 | return NULL; |
2822 | |
|
2823 | 0 | } /* get_vlanent */ |
2824 | | |
2825 | | static vlan_t * |
2826 | | get_vlannamebyid(uint16_t id) |
2827 | 0 | { |
2828 | 0 | vlan_t *vlan; |
2829 | |
|
2830 | 0 | set_vlanent(g_pvlan_path); |
2831 | |
|
2832 | 0 | while (((vlan = get_vlanent()) != NULL) && (id != vlan->id) ) ; |
2833 | |
|
2834 | 0 | if (vlan == NULL) { |
2835 | 0 | end_vlanent(); |
2836 | |
|
2837 | 0 | } |
2838 | |
|
2839 | 0 | return vlan; |
2840 | |
|
2841 | 0 | } /* get_vlannamebyid */ |
2842 | | |
2843 | | static void |
2844 | | initialize_vlans(const char* app_env_var_prefix) |
2845 | 28 | { |
2846 | 28 | ws_assert(vlan_hash_table == NULL); |
2847 | 28 | vlan_hash_table = wmem_map_new(addr_resolv_scope, g_direct_hash, g_direct_equal); |
2848 | | |
2849 | | /* Set g_pvlan_path here, but don't actually do anything |
2850 | | * with it. It's used in get_vlannamebyid() |
2851 | | */ |
2852 | 28 | if (g_pvlan_path == NULL) { |
2853 | | /* Check profile directory before personal configuration */ |
2854 | 28 | g_pvlan_path = get_persconffile_path(ENAME_VLANS, true, app_env_var_prefix); |
2855 | 28 | if (!file_exists(g_pvlan_path)) { |
2856 | 28 | g_free(g_pvlan_path); |
2857 | 28 | g_pvlan_path = get_persconffile_path(ENAME_VLANS, false, app_env_var_prefix); |
2858 | 28 | } |
2859 | 28 | } |
2860 | 28 | } /* initialize_vlans */ |
2861 | | |
2862 | | static void |
2863 | | vlan_name_lookup_cleanup(void) |
2864 | 14 | { |
2865 | 14 | end_vlanent(); |
2866 | 14 | vlan_hash_table = NULL; |
2867 | 14 | g_free(g_pvlan_path); |
2868 | 14 | g_pvlan_path = NULL; |
2869 | 14 | } |
2870 | | |
2871 | | static const char * |
2872 | | vlan_name_lookup(const unsigned id) |
2873 | 0 | { |
2874 | 0 | hashvlan_t *tp; |
2875 | 0 | vlan_t *vlan; |
2876 | |
|
2877 | 0 | tp = (hashvlan_t *)wmem_map_lookup(vlan_hash_table, GUINT_TO_POINTER(id)); |
2878 | 0 | if (tp == NULL) { |
2879 | 0 | tp = wmem_new(addr_resolv_scope, hashvlan_t); |
2880 | 0 | wmem_map_insert(vlan_hash_table, GUINT_TO_POINTER(id), tp); |
2881 | 0 | } else { |
2882 | 0 | return tp->name; |
2883 | 0 | } |
2884 | | |
2885 | | /* fill in a new entry */ |
2886 | | |
2887 | 0 | tp->id = id; |
2888 | |
|
2889 | 0 | if ( (vlan = get_vlannamebyid(id)) == NULL) { |
2890 | | /* unknown name */ |
2891 | 0 | snprintf(tp->name, MAXVLANNAMELEN, "<%u>", id); |
2892 | |
|
2893 | 0 | } else { |
2894 | 0 | (void) g_strlcpy(tp->name, vlan->name, MAXVLANNAMELEN); |
2895 | 0 | } |
2896 | |
|
2897 | 0 | return tp->name; |
2898 | |
|
2899 | 0 | } /* vlan_name_lookup */ |
2900 | | /* VLAN END */ |
2901 | | |
2902 | | static bool |
2903 | | read_hosts_file (const char *hostspath, bool store_entries) |
2904 | 56 | { |
2905 | 56 | FILE *hf; |
2906 | 56 | char line[MAX_LINELEN]; |
2907 | 56 | char *cp; |
2908 | 56 | union { |
2909 | 56 | uint32_t ip4_addr; |
2910 | 56 | ws_in6_addr ip6_addr; |
2911 | 56 | } host_addr; |
2912 | 56 | bool is_ipv6, entry_found = false; |
2913 | | |
2914 | | /* |
2915 | | * See the hosts(4) or hosts(5) man page for hosts file format |
2916 | | * (not available on all systems). |
2917 | | */ |
2918 | 56 | if ((hf = ws_fopen(hostspath, "r")) == NULL) |
2919 | 56 | return false; |
2920 | | |
2921 | 0 | while (fgetline(line, sizeof(line), hf) >= 0) { |
2922 | 0 | if ((cp = strchr(line, '#'))) |
2923 | 0 | *cp = '\0'; |
2924 | |
|
2925 | 0 | if ((cp = strtok(line, " \t")) == NULL) |
2926 | 0 | continue; /* no tokens in the line */ |
2927 | | |
2928 | 0 | if (ws_inet_pton6(cp, &host_addr.ip6_addr)) { |
2929 | | /* Valid IPv6 */ |
2930 | 0 | is_ipv6 = true; |
2931 | 0 | } else if (ws_inet_pton4(cp, &host_addr.ip4_addr)) { |
2932 | | /* Valid IPv4 */ |
2933 | 0 | is_ipv6 = false; |
2934 | 0 | } else { |
2935 | 0 | continue; |
2936 | 0 | } |
2937 | | |
2938 | 0 | if ((cp = strtok(NULL, " \t")) == NULL) |
2939 | 0 | continue; /* no host name */ |
2940 | | |
2941 | 0 | entry_found = true; |
2942 | 0 | if (store_entries) { |
2943 | 0 | if (is_ipv6) { |
2944 | 0 | add_ipv6_name(&host_addr.ip6_addr, cp, true); |
2945 | 0 | } else { |
2946 | 0 | add_ipv4_name(host_addr.ip4_addr, cp, true); |
2947 | 0 | } |
2948 | 0 | } |
2949 | 0 | } |
2950 | |
|
2951 | 0 | fclose(hf); |
2952 | 0 | return entry_found ? true : false; |
2953 | 56 | } /* read_hosts_file */ |
2954 | | |
2955 | | bool |
2956 | | add_hosts_file (const char *hosts_file) |
2957 | 0 | { |
2958 | 0 | bool found = false; |
2959 | 0 | unsigned i; |
2960 | |
|
2961 | 0 | if (!hosts_file) |
2962 | 0 | return false; |
2963 | | |
2964 | 0 | if (!extra_hosts_files) |
2965 | 0 | extra_hosts_files = g_ptr_array_new(); |
2966 | |
|
2967 | 0 | for (i = 0; i < extra_hosts_files->len; i++) { |
2968 | 0 | if (strcmp(hosts_file, (const char *) g_ptr_array_index(extra_hosts_files, i)) == 0) |
2969 | 0 | found = true; |
2970 | 0 | } |
2971 | |
|
2972 | 0 | if (!found) { |
2973 | 0 | g_ptr_array_add(extra_hosts_files, wmem_strdup(wmem_epan_scope(), hosts_file)); |
2974 | 0 | return read_hosts_file (hosts_file, false); |
2975 | 0 | } |
2976 | 0 | return true; |
2977 | 0 | } |
2978 | | |
2979 | | bool |
2980 | | add_ip_name_from_string (const char *addr, const char *name) |
2981 | 0 | { |
2982 | 0 | union { |
2983 | 0 | uint32_t ip4_addr; |
2984 | 0 | ws_in6_addr ip6_addr; |
2985 | 0 | } host_addr; |
2986 | 0 | bool is_ipv6; |
2987 | 0 | resolved_name_t *resolved_entry; |
2988 | |
|
2989 | 0 | if (ws_inet_pton6(addr, &host_addr.ip6_addr)) { |
2990 | 0 | is_ipv6 = true; |
2991 | 0 | } else if (ws_inet_pton4(addr, &host_addr.ip4_addr)) { |
2992 | 0 | is_ipv6 = false; |
2993 | 0 | } else { |
2994 | 0 | return false; |
2995 | 0 | } |
2996 | | |
2997 | 0 | if (is_ipv6) { |
2998 | 0 | resolved_entry = (resolved_name_t*)wmem_map_lookup(manually_resolved_ipv6_list, &host_addr.ip6_addr); |
2999 | 0 | if (resolved_entry) |
3000 | 0 | { |
3001 | | // If we found a previous matching key (IP address), then just update the value (custom hostname); |
3002 | 0 | (void) g_strlcpy(resolved_entry->name, name, MAXDNSNAMELEN); |
3003 | 0 | } |
3004 | 0 | else |
3005 | 0 | { |
3006 | | // Add a new mapping entry, if this IP address isn't already in the list. |
3007 | 0 | ws_in6_addr* addr_key = wmem_new(wmem_epan_scope(), ws_in6_addr); |
3008 | 0 | memcpy(addr_key, &host_addr.ip6_addr, sizeof(ws_in6_addr)); |
3009 | |
|
3010 | 0 | resolved_entry = wmem_new(wmem_epan_scope(), resolved_name_t); |
3011 | 0 | (void) g_strlcpy(resolved_entry->name, name, MAXDNSNAMELEN); |
3012 | |
|
3013 | 0 | wmem_map_insert(manually_resolved_ipv6_list, addr_key, resolved_entry); |
3014 | 0 | } |
3015 | 0 | } else { |
3016 | 0 | resolved_entry = (resolved_name_t*)wmem_map_lookup(manually_resolved_ipv4_list, GUINT_TO_POINTER(host_addr.ip4_addr)); |
3017 | 0 | if (resolved_entry) |
3018 | 0 | { |
3019 | | // If we found a previous matching key (IP address), then just update the value (custom hostname); |
3020 | 0 | (void) g_strlcpy(resolved_entry->name, name, MAXDNSNAMELEN); |
3021 | 0 | } |
3022 | 0 | else |
3023 | 0 | { |
3024 | | // Add a new mapping entry, if this IP address isn't already in the list. |
3025 | 0 | resolved_entry = wmem_new(wmem_epan_scope(), resolved_name_t); |
3026 | 0 | (void) g_strlcpy(resolved_entry->name, name, MAXDNSNAMELEN); |
3027 | |
|
3028 | 0 | wmem_map_insert(manually_resolved_ipv4_list, GUINT_TO_POINTER(host_addr.ip4_addr), resolved_entry); |
3029 | 0 | } |
3030 | 0 | } |
3031 | |
|
3032 | 0 | return true; |
3033 | 0 | } /* add_ip_name_from_string */ |
3034 | | |
3035 | | extern resolved_name_t* get_edited_resolved_name(const char* addr) |
3036 | 0 | { |
3037 | 0 | uint32_t ip4_addr; |
3038 | 0 | ws_in6_addr ip6_addr; |
3039 | 0 | resolved_name_t* resolved_entry = NULL; |
3040 | |
|
3041 | 0 | if (ws_inet_pton6(addr, &ip6_addr)) { |
3042 | 0 | resolved_entry = (resolved_name_t*)wmem_map_lookup(manually_resolved_ipv6_list, &ip6_addr); |
3043 | 0 | } |
3044 | 0 | else if (ws_inet_pton4(addr, &ip4_addr)) { |
3045 | 0 | resolved_entry = (resolved_name_t*)wmem_map_lookup(manually_resolved_ipv4_list, GUINT_TO_POINTER(ip4_addr)); |
3046 | 0 | } |
3047 | |
|
3048 | 0 | return resolved_entry; |
3049 | 0 | } |
3050 | | |
3051 | | /* |
3052 | | * Add the resolved addresses that are in use to the list used to create the pcapng NRB |
3053 | | */ |
3054 | | static void |
3055 | | ipv4_hash_table_resolved_to_list(void *key _U_, void *value, void *user_data) |
3056 | 0 | { |
3057 | 0 | addrinfo_lists_t *lists = (addrinfo_lists_t *)user_data; |
3058 | 0 | hashipv4_t *ipv4_hash_table_entry = (hashipv4_t *)value; |
3059 | |
|
3060 | 0 | if ((ipv4_hash_table_entry->flags & USED_AND_RESOLVED_MASK) == USED_AND_RESOLVED_MASK) { |
3061 | 0 | lists->ipv4_addr_list = g_list_prepend(lists->ipv4_addr_list, ipv4_hash_table_entry); |
3062 | 0 | } |
3063 | 0 | } |
3064 | | |
3065 | | /* |
3066 | | * Add the resolved addresses that are in use to the list used to create the pcapng NRB |
3067 | | */ |
3068 | | static void |
3069 | | ipv6_hash_table_resolved_to_list(void *key _U_, void *value, void *user_data) |
3070 | 0 | { |
3071 | 0 | addrinfo_lists_t *lists = (addrinfo_lists_t *)user_data; |
3072 | 0 | hashipv6_t *ipv6_hash_table_entry = (hashipv6_t *)value; |
3073 | |
|
3074 | 0 | if ((ipv6_hash_table_entry->flags & USED_AND_RESOLVED_MASK) == USED_AND_RESOLVED_MASK) { |
3075 | 0 | lists->ipv6_addr_list = g_list_prepend(lists->ipv6_addr_list, ipv6_hash_table_entry); |
3076 | 0 | } |
3077 | 0 | } |
3078 | | |
3079 | | addrinfo_lists_t * |
3080 | | get_addrinfo_list(void) |
3081 | 0 | { |
3082 | 0 | if (ipv4_hash_table) { |
3083 | 0 | wmem_map_foreach(ipv4_hash_table, ipv4_hash_table_resolved_to_list, &addrinfo_lists); |
3084 | 0 | } |
3085 | |
|
3086 | 0 | if (ipv6_hash_table) { |
3087 | 0 | wmem_map_foreach(ipv6_hash_table, ipv6_hash_table_resolved_to_list, &addrinfo_lists); |
3088 | 0 | } |
3089 | |
|
3090 | 0 | return &addrinfo_lists; |
3091 | 0 | } |
3092 | | |
3093 | | /* Read in a list of subnet definition - name pairs. |
3094 | | * <line> = <comment> | <entry> | <whitespace> |
3095 | | * <comment> = <whitespace>#<any> |
3096 | | * <entry> = <subnet_definition> <whitespace> <subnet_name> [<comment>|<whitespace><any>] |
3097 | | * <subnet_definition> = <ipv4_address> / <subnet_mask_length> |
3098 | | * <ipv4_address> is a full address; it will be masked to get the subnet-ID. |
3099 | | * <subnet_mask_length> is a decimal 1-31 |
3100 | | * <subnet_name> is a string containing no whitespace. |
3101 | | * <whitespace> = (space | tab)+ |
3102 | | * Any malformed entries are ignored. |
3103 | | * Any trailing data after the subnet_name is ignored. |
3104 | | * |
3105 | | * XXX Support IPv6 |
3106 | | */ |
3107 | | static bool |
3108 | | read_subnets_file (const char *subnetspath) |
3109 | 84 | { |
3110 | 84 | FILE *hf; |
3111 | 84 | char line[MAX_LINELEN]; |
3112 | 84 | char *cp, *cp2; |
3113 | 84 | uint32_t host_addr; /* IPv4 ONLY */ |
3114 | 84 | uint8_t mask_length; |
3115 | | |
3116 | 84 | if ((hf = ws_fopen(subnetspath, "r")) == NULL) |
3117 | 84 | return false; |
3118 | | |
3119 | 0 | while (fgetline(line, sizeof(line), hf) >= 0) { |
3120 | 0 | if ((cp = strchr(line, '#'))) |
3121 | 0 | *cp = '\0'; |
3122 | |
|
3123 | 0 | if ((cp = strtok(line, " \t")) == NULL) |
3124 | 0 | continue; /* no tokens in the line */ |
3125 | | |
3126 | | |
3127 | | /* Expected format is <IP4 address>/<subnet length> */ |
3128 | 0 | cp2 = strchr(cp, '/'); |
3129 | 0 | if (NULL == cp2) { |
3130 | | /* No length */ |
3131 | 0 | continue; |
3132 | 0 | } |
3133 | 0 | *cp2 = '\0'; /* Cut token */ |
3134 | 0 | ++cp2 ; |
3135 | | |
3136 | | /* Check if this is a valid IPv4 address */ |
3137 | 0 | if (!str_to_ip(cp, &host_addr)) { |
3138 | 0 | continue; /* no */ |
3139 | 0 | } |
3140 | | |
3141 | 0 | if (!ws_strtou8(cp2, NULL, &mask_length) || mask_length == 0 || mask_length > 32) { |
3142 | 0 | continue; /* invalid mask length */ |
3143 | 0 | } |
3144 | | |
3145 | 0 | if ((cp = strtok(NULL, " \t")) == NULL) |
3146 | 0 | continue; /* no subnet name */ |
3147 | | |
3148 | 0 | subnet_entry_set(host_addr, mask_length, cp); |
3149 | 0 | } |
3150 | |
|
3151 | 0 | fclose(hf); |
3152 | 0 | return true; |
3153 | 84 | } /* read_subnets_file */ |
3154 | | |
3155 | | static subnet_entry_t |
3156 | | subnet_lookup(const uint32_t addr) |
3157 | 82 | { |
3158 | 82 | subnet_entry_t subnet_entry; |
3159 | 82 | uint32_t i; |
3160 | | |
3161 | | /* Search mask lengths linearly, longest first */ |
3162 | | |
3163 | 82 | i = SUBNETLENGTHSIZE; |
3164 | 82 | while(have_subnet_entry && i > 0) { |
3165 | 0 | uint32_t masked_addr; |
3166 | 0 | subnet_length_entry_t* length_entry; |
3167 | | |
3168 | | /* Note that we run from 31 (length 32) to 0 (length 1) */ |
3169 | 0 | --i; |
3170 | 0 | ws_assert(i < SUBNETLENGTHSIZE); |
3171 | | |
3172 | |
|
3173 | 0 | length_entry = &subnet_length_entries[i]; |
3174 | |
|
3175 | 0 | if (NULL != length_entry->subnet_addresses) { |
3176 | 0 | sub_net_hashipv4_t * tp; |
3177 | 0 | uint32_t hash_idx; |
3178 | |
|
3179 | 0 | masked_addr = addr & length_entry->mask; |
3180 | 0 | hash_idx = HASH_IPV4_ADDRESS(masked_addr); |
3181 | |
|
3182 | 0 | tp = length_entry->subnet_addresses[hash_idx]; |
3183 | 0 | while(tp != NULL && tp->addr != masked_addr) { |
3184 | 0 | tp = tp->next; |
3185 | 0 | } |
3186 | |
|
3187 | 0 | if (NULL != tp) { |
3188 | 0 | subnet_entry.mask = length_entry->mask; |
3189 | 0 | subnet_entry.mask_length = i + 1; /* Length is offset + 1 */ |
3190 | 0 | subnet_entry.name = tp->name; |
3191 | 0 | return subnet_entry; |
3192 | 0 | } |
3193 | 0 | } |
3194 | 0 | } |
3195 | | |
3196 | 82 | subnet_entry.mask = 0; |
3197 | 82 | subnet_entry.mask_length = 0; |
3198 | 82 | subnet_entry.name = NULL; |
3199 | | |
3200 | 82 | return subnet_entry; |
3201 | 82 | } |
3202 | | |
3203 | | /* Add a subnet-definition - name pair to the set. |
3204 | | * The definition is taken by masking the address passed in with the mask of the |
3205 | | * given length. |
3206 | | */ |
3207 | | static void |
3208 | | subnet_entry_set(uint32_t subnet_addr, const uint8_t mask_length, const char* name) |
3209 | 0 | { |
3210 | 0 | subnet_length_entry_t* entry; |
3211 | 0 | sub_net_hashipv4_t * tp; |
3212 | 0 | size_t hash_idx; |
3213 | |
|
3214 | 0 | ws_assert(mask_length > 0 && mask_length <= 32); |
3215 | |
|
3216 | 0 | entry = &subnet_length_entries[mask_length - 1]; |
3217 | |
|
3218 | 0 | subnet_addr &= entry->mask; |
3219 | |
|
3220 | 0 | hash_idx = HASH_IPV4_ADDRESS(subnet_addr); |
3221 | |
|
3222 | 0 | if (NULL == entry->subnet_addresses) { |
3223 | 0 | entry->subnet_addresses = (sub_net_hashipv4_t**)wmem_alloc0(addr_resolv_scope, sizeof(sub_net_hashipv4_t*) * HASHHOSTSIZE); |
3224 | 0 | } |
3225 | |
|
3226 | 0 | if (NULL != (tp = entry->subnet_addresses[hash_idx])) { |
3227 | 0 | sub_net_hashipv4_t * new_tp; |
3228 | |
|
3229 | 0 | while (tp->next) { |
3230 | 0 | if (tp->addr == subnet_addr) { |
3231 | 0 | return; /* XXX provide warning that an address was repeated? */ |
3232 | 0 | } else { |
3233 | 0 | tp = tp->next; |
3234 | 0 | } |
3235 | 0 | } |
3236 | | |
3237 | 0 | new_tp = wmem_new(addr_resolv_scope, sub_net_hashipv4_t); |
3238 | 0 | tp->next = new_tp; |
3239 | 0 | tp = new_tp; |
3240 | 0 | } else { |
3241 | 0 | tp = entry->subnet_addresses[hash_idx] = wmem_new(addr_resolv_scope, sub_net_hashipv4_t); |
3242 | 0 | } |
3243 | | |
3244 | 0 | tp->next = NULL; |
3245 | 0 | tp->addr = subnet_addr; |
3246 | 0 | (void) g_strlcpy(tp->name, name, MAXNAMELEN); /* This is longer than subnet names can actually be */ |
3247 | 0 | have_subnet_entry = true; |
3248 | 0 | } |
3249 | | |
3250 | | static void |
3251 | | subnet_name_lookup_init(const char* app_env_var_prefix) |
3252 | 28 | { |
3253 | 28 | char* subnetspath; |
3254 | 28 | uint32_t i; |
3255 | | |
3256 | 924 | for(i = 0; i < SUBNETLENGTHSIZE; ++i) { |
3257 | 896 | uint32_t length = i + 1; |
3258 | | |
3259 | 896 | subnet_length_entries[i].subnet_addresses = NULL; |
3260 | 896 | subnet_length_entries[i].mask_length = length; |
3261 | 896 | subnet_length_entries[i].mask = g_htonl(ws_ipv4_get_subnet_mask(length)); |
3262 | 896 | } |
3263 | | |
3264 | | /* Check profile directory before personal configuration */ |
3265 | 28 | subnetspath = get_persconffile_path(ENAME_SUBNETS, true, app_env_var_prefix); |
3266 | 28 | if (!read_subnets_file(subnetspath)) { |
3267 | 28 | if (errno != ENOENT) { |
3268 | 0 | report_open_failure(subnetspath, errno, false); |
3269 | 0 | } |
3270 | | |
3271 | 28 | g_free(subnetspath); |
3272 | 28 | subnetspath = get_persconffile_path(ENAME_SUBNETS, false, app_env_var_prefix); |
3273 | 28 | if (!read_subnets_file(subnetspath) && errno != ENOENT) { |
3274 | 0 | report_open_failure(subnetspath, errno, false); |
3275 | 0 | } |
3276 | 28 | } |
3277 | 28 | g_free(subnetspath); |
3278 | | |
3279 | | /* |
3280 | | * Load the global subnets file, if we have one. |
3281 | | */ |
3282 | 28 | subnetspath = get_datafile_path(ENAME_SUBNETS, app_env_var_prefix); |
3283 | 28 | if (!read_subnets_file(subnetspath) && errno != ENOENT) { |
3284 | 0 | report_open_failure(subnetspath, errno, false); |
3285 | 0 | } |
3286 | 28 | g_free(subnetspath); |
3287 | 28 | } |
3288 | | |
3289 | | /* IPv6 Subnet Name Resolution */ |
3290 | | |
3291 | | /* Compute a 16-byte subnet mask from a prefix length (1-128). */ |
3292 | | static void |
3293 | | ipv6_get_subnet_mask(uint32_t mask_length, uint8_t mask[16]) |
3294 | 3.58k | { |
3295 | 3.58k | uint32_t full_bytes = mask_length / 8; |
3296 | 3.58k | uint32_t remaining = mask_length % 8; |
3297 | 3.58k | memset(mask, 0, 16); |
3298 | 30.9k | for (uint32_t i = 0; i < full_bytes; i++) |
3299 | 27.3k | mask[i] = 0xff; |
3300 | 3.58k | if (remaining > 0 && full_bytes < 16) |
3301 | 3.13k | mask[full_bytes] = (uint8_t)(0xff << (8 - remaining)); |
3302 | 3.58k | } |
3303 | | |
3304 | | static void |
3305 | | subnet6_entry_set(const ws_in6_addr *subnet_addr, const uint32_t mask_length, |
3306 | | const char *name) |
3307 | 0 | { |
3308 | 0 | subnet_length_entry_v6_t *entry; |
3309 | 0 | sub_net_hashipv6_t *tp; |
3310 | 0 | uint8_t masked[16]; |
3311 | 0 | size_t hash_idx; |
3312 | |
|
3313 | 0 | ws_assert(mask_length > 0 && mask_length <= 128); |
3314 | 0 | entry = &subnet_length_entries_v6[mask_length - 1]; |
3315 | |
|
3316 | 0 | for (int i = 0; i < 16; i++) |
3317 | 0 | masked[i] = subnet_addr->bytes[i] & entry->mask[i]; |
3318 | |
|
3319 | 0 | hash_idx = ipv6_oat_hash(masked) & (HASHHOSTSIZE - 1); |
3320 | |
|
3321 | 0 | if (entry->subnet_addresses == NULL) |
3322 | 0 | entry->subnet_addresses = (sub_net_hashipv6_t **)wmem_alloc0( |
3323 | 0 | addr_resolv_scope, sizeof(sub_net_hashipv6_t *) * HASHHOSTSIZE); |
3324 | |
|
3325 | 0 | if ((tp = entry->subnet_addresses[hash_idx]) != NULL) { |
3326 | 0 | sub_net_hashipv6_t *new_tp; |
3327 | 0 | while (tp->next) { |
3328 | 0 | if (memcmp(tp->addr, masked, 16) == 0) |
3329 | 0 | return; /* duplicate */ |
3330 | 0 | tp = tp->next; |
3331 | 0 | } |
3332 | 0 | if (memcmp(tp->addr, masked, 16) == 0) |
3333 | 0 | return; /* duplicate at tail */ |
3334 | 0 | new_tp = wmem_new(addr_resolv_scope, sub_net_hashipv6_t); |
3335 | 0 | tp->next = new_tp; |
3336 | 0 | tp = new_tp; |
3337 | 0 | } else { |
3338 | 0 | tp = entry->subnet_addresses[hash_idx] = |
3339 | 0 | wmem_new(addr_resolv_scope, sub_net_hashipv6_t); |
3340 | 0 | } |
3341 | | |
3342 | 0 | tp->next = NULL; |
3343 | 0 | memcpy(tp->addr, masked, 16); |
3344 | 0 | (void)g_strlcpy(tp->name, name, MAXNAMELEN); |
3345 | 0 | have_subnet_entry_v6 = true; |
3346 | 0 | } |
3347 | | |
3348 | | static subnet_entry_v6_t |
3349 | | subnet6_lookup(const ws_in6_addr *addr) |
3350 | 1 | { |
3351 | 1 | subnet_entry_v6_t result; |
3352 | 1 | uint32_t i = SUBNETLENGTHSIZE_V6; |
3353 | | |
3354 | 1 | while (have_subnet_entry_v6 && i > 0) { |
3355 | 0 | subnet_length_entry_v6_t *length_entry; |
3356 | 0 | uint8_t masked[16]; |
3357 | 0 | size_t hash_idx; |
3358 | 0 | sub_net_hashipv6_t *tp; |
3359 | |
|
3360 | 0 | --i; |
3361 | 0 | length_entry = &subnet_length_entries_v6[i]; |
3362 | |
|
3363 | 0 | if (length_entry->subnet_addresses == NULL) |
3364 | 0 | continue; |
3365 | | |
3366 | 0 | for (int b = 0; b < 16; b++) |
3367 | 0 | masked[b] = addr->bytes[b] & length_entry->mask[b]; |
3368 | |
|
3369 | 0 | hash_idx = ipv6_oat_hash(masked) & (HASHHOSTSIZE - 1); |
3370 | 0 | tp = length_entry->subnet_addresses[hash_idx]; |
3371 | |
|
3372 | 0 | while (tp != NULL && memcmp(tp->addr, masked, 16) != 0) |
3373 | 0 | tp = tp->next; |
3374 | |
|
3375 | 0 | if (tp != NULL) { |
3376 | 0 | memcpy(result.mask, length_entry->mask, 16); |
3377 | 0 | result.mask_length = i + 1; |
3378 | 0 | result.name = tp->name; |
3379 | 0 | return result; |
3380 | 0 | } |
3381 | 0 | } |
3382 | | |
3383 | 1 | memset(result.mask, 0, 16); |
3384 | 1 | result.mask_length = 0; |
3385 | 1 | result.name = NULL; |
3386 | 1 | return result; |
3387 | 1 | } |
3388 | | |
3389 | | static bool |
3390 | | read_subnets_ipv6_file(const char *subnetspath) |
3391 | 84 | { |
3392 | 84 | FILE *hf; |
3393 | 84 | char line[MAX_LINELEN]; |
3394 | 84 | char *cp, *cp2; |
3395 | 84 | ws_in6_addr host_addr; |
3396 | 84 | uint32_t mask_length; |
3397 | | |
3398 | 84 | if ((hf = ws_fopen(subnetspath, "r")) == NULL) |
3399 | 84 | return false; |
3400 | | |
3401 | 0 | while (fgetline(line, sizeof(line), hf) >= 0) { |
3402 | 0 | if ((cp = strchr(line, '#'))) |
3403 | 0 | *cp = '\0'; |
3404 | |
|
3405 | 0 | if ((cp = strtok(line, " \t")) == NULL) |
3406 | 0 | continue; |
3407 | | |
3408 | | /* Expected format: <IPv6_address>/<prefix_length> */ |
3409 | 0 | cp2 = strchr(cp, '/'); |
3410 | 0 | if (cp2 == NULL) |
3411 | 0 | continue; |
3412 | 0 | *cp2 = '\0'; |
3413 | 0 | ++cp2; |
3414 | |
|
3415 | 0 | if (!ws_inet_pton6(cp, &host_addr)) |
3416 | 0 | continue; |
3417 | | |
3418 | 0 | if (!ws_strtou32(cp2, NULL, &mask_length) || mask_length == 0 || mask_length > 128) |
3419 | 0 | continue; |
3420 | | |
3421 | 0 | if ((cp = strtok(NULL, " \t")) == NULL) |
3422 | 0 | continue; |
3423 | | |
3424 | 0 | subnet6_entry_set(&host_addr, mask_length, cp); |
3425 | 0 | } |
3426 | |
|
3427 | 0 | fclose(hf); |
3428 | 0 | return true; |
3429 | 84 | } |
3430 | | |
3431 | | static void |
3432 | | subnet6_name_lookup_init(const char *app_env_var_prefix) |
3433 | 28 | { |
3434 | 28 | char *subnetspath; |
3435 | | |
3436 | 3.61k | for (uint32_t i = 0; i < SUBNETLENGTHSIZE_V6; ++i) { |
3437 | 3.58k | subnet_length_entries_v6[i].subnet_addresses = NULL; |
3438 | 3.58k | subnet_length_entries_v6[i].mask_length = i + 1; |
3439 | 3.58k | ipv6_get_subnet_mask((uint32_t)(i + 1), subnet_length_entries_v6[i].mask); |
3440 | 3.58k | } |
3441 | | |
3442 | | /* Check profile directory before personal configuration */ |
3443 | 28 | subnetspath = get_persconffile_path(ENAME_SUBNETS_V6, true, app_env_var_prefix); |
3444 | 28 | if (!read_subnets_ipv6_file(subnetspath)) { |
3445 | 28 | if (errno != ENOENT) |
3446 | 0 | report_open_failure(subnetspath, errno, false); |
3447 | 28 | g_free(subnetspath); |
3448 | 28 | subnetspath = get_persconffile_path(ENAME_SUBNETS_V6, false, app_env_var_prefix); |
3449 | 28 | if (!read_subnets_ipv6_file(subnetspath) && errno != ENOENT) |
3450 | 0 | report_open_failure(subnetspath, errno, false); |
3451 | 28 | } |
3452 | 28 | g_free(subnetspath); |
3453 | | |
3454 | | /* |
3455 | | * Load the global IPv6 subnets file, if we have one. |
3456 | | */ |
3457 | 28 | subnetspath = get_datafile_path(ENAME_SUBNETS_V6, app_env_var_prefix); |
3458 | 28 | if (!read_subnets_ipv6_file(subnetspath) && errno != ENOENT) |
3459 | 0 | report_open_failure(subnetspath, errno, false); |
3460 | 28 | g_free(subnetspath); |
3461 | 28 | } |
3462 | | |
3463 | | /* SS7 PC Name Resolution Portion */ |
3464 | | static hashss7pc_t * |
3465 | | new_ss7pc(const uint8_t ni, const uint32_t pc) |
3466 | 0 | { |
3467 | 0 | hashss7pc_t *tp = wmem_new(addr_resolv_scope, hashss7pc_t); |
3468 | 0 | tp->id = (ni<<24) + (pc&0xffffff); |
3469 | 0 | tp->pc_addr[0] = '\0'; |
3470 | 0 | tp->name[0] = '\0'; |
3471 | |
|
3472 | 0 | return tp; |
3473 | 0 | } |
3474 | | |
3475 | | static hashss7pc_t * |
3476 | | host_lookup_ss7pc(const uint8_t ni, const uint32_t pc) |
3477 | 0 | { |
3478 | 0 | hashss7pc_t * volatile tp; |
3479 | 0 | uint32_t id; |
3480 | |
|
3481 | 0 | id = (ni<<24) + (pc&0xffffff); |
3482 | |
|
3483 | 0 | tp = (hashss7pc_t *)wmem_map_lookup(ss7pc_hash_table, GUINT_TO_POINTER(id)); |
3484 | 0 | if (tp == NULL) { |
3485 | 0 | tp = new_ss7pc(ni, pc); |
3486 | 0 | wmem_map_insert(ss7pc_hash_table, GUINT_TO_POINTER(id), tp); |
3487 | 0 | } |
3488 | |
|
3489 | 0 | return tp; |
3490 | 0 | } |
3491 | | |
3492 | | void fill_unresolved_ss7pc(const char * pc_addr, const uint8_t ni, const uint32_t pc) |
3493 | 0 | { |
3494 | 0 | hashss7pc_t *tp = host_lookup_ss7pc(ni, pc); |
3495 | |
|
3496 | 0 | (void) g_strlcpy(tp->pc_addr, pc_addr, MAXNAMELEN); |
3497 | 0 | } |
3498 | | |
3499 | | const char * |
3500 | | get_hostname_ss7pc(const uint8_t ni, const uint32_t pc) |
3501 | 0 | { |
3502 | 0 | hashss7pc_t *tp = host_lookup_ss7pc(ni, pc); |
3503 | | |
3504 | | /* never resolved yet*/ |
3505 | 0 | if (tp->pc_addr[0] == '\0') |
3506 | 0 | return tp->pc_addr; |
3507 | | |
3508 | | /* Don't have name in file */ |
3509 | 0 | if (tp->name[0] == '\0') |
3510 | 0 | return tp->pc_addr; |
3511 | | |
3512 | 0 | if (!gbl_resolv_flags.ss7pc_name) |
3513 | 0 | return tp->pc_addr; |
3514 | | |
3515 | 0 | return tp->name; |
3516 | 0 | } |
3517 | | |
3518 | | static void |
3519 | | add_ss7pc_name(const uint8_t ni, uint32_t pc, const char *name) |
3520 | 0 | { |
3521 | 0 | hashss7pc_t *tp; |
3522 | 0 | uint32_t id; |
3523 | |
|
3524 | 0 | if (!name || name[0] == '\0') |
3525 | 0 | return; |
3526 | | |
3527 | 0 | id = (ni<<24) + (pc&0xffffff); |
3528 | 0 | tp = (hashss7pc_t *)wmem_map_lookup(ss7pc_hash_table, GUINT_TO_POINTER(id)); |
3529 | 0 | if (!tp) { |
3530 | 0 | tp = new_ss7pc(ni, pc); |
3531 | 0 | wmem_map_insert(ss7pc_hash_table, GUINT_TO_POINTER(id), tp); |
3532 | 0 | } |
3533 | |
|
3534 | 0 | if (g_ascii_strcasecmp(tp->name, name)) { |
3535 | 0 | (void) g_strlcpy(tp->name, name, MAXNAMELEN); |
3536 | 0 | } |
3537 | 0 | } |
3538 | | |
3539 | | static bool |
3540 | | read_ss7pcs_file(const char *ss7pcspath) |
3541 | 28 | { |
3542 | 28 | FILE *hf; |
3543 | 28 | char line[MAX_LINELEN]; |
3544 | 28 | char *cp; |
3545 | 28 | uint8_t ni; |
3546 | 28 | uint32_t pc; |
3547 | 28 | bool entry_found = false; |
3548 | | |
3549 | | /* |
3550 | | * File format is Network Indicator (decimal)<dash>Point Code (Decimal)<tab/space>Hostname |
3551 | | */ |
3552 | 28 | if ((hf = ws_fopen(ss7pcspath, "r")) == NULL) |
3553 | 28 | return false; |
3554 | | |
3555 | 0 | while (fgetline(line, sizeof(line), hf) >= 0) { |
3556 | 0 | if ((cp = strchr(line, '#'))) |
3557 | 0 | *cp = '\0'; |
3558 | |
|
3559 | 0 | if ((cp = strtok(line, "-")) == NULL) |
3560 | 0 | continue; /*no ni-pc separator*/ |
3561 | 0 | if (!ws_strtou8(cp, NULL, &ni)) |
3562 | 0 | continue; |
3563 | 0 | if (ni > 3) |
3564 | 0 | continue; |
3565 | | |
3566 | 0 | if ((cp = strtok(NULL, " \t")) == NULL) |
3567 | 0 | continue; /* no tokens for pc and name */ |
3568 | 0 | if (!ws_strtou32(cp, NULL, &pc)) |
3569 | 0 | continue; |
3570 | 0 | if (pc >> 24 > 0) |
3571 | 0 | continue; |
3572 | | |
3573 | 0 | if ((cp = strtok(NULL, " \t")) == NULL) |
3574 | 0 | continue; /* no host name */ |
3575 | | |
3576 | 0 | entry_found = true; |
3577 | 0 | add_ss7pc_name(ni, pc, cp); |
3578 | 0 | } |
3579 | |
|
3580 | 0 | fclose(hf); |
3581 | 0 | return entry_found ? true : false; |
3582 | 28 | } |
3583 | | |
3584 | | static void |
3585 | | ss7pc_name_lookup_init(const char* app_env_var_prefix) |
3586 | 28 | { |
3587 | 28 | char *ss7pcspath; |
3588 | | |
3589 | 28 | ws_assert(ss7pc_hash_table == NULL); |
3590 | | |
3591 | 28 | ss7pc_hash_table = wmem_map_new(addr_resolv_scope, g_direct_hash, g_direct_equal); |
3592 | | |
3593 | | /* |
3594 | | * Load the user's ss7pcs file |
3595 | | */ |
3596 | 28 | ss7pcspath = get_persconffile_path(ENAME_SS7PCS, true, app_env_var_prefix); |
3597 | 28 | if (!read_ss7pcs_file(ss7pcspath) && errno != ENOENT) { |
3598 | 0 | report_open_failure(ss7pcspath, errno, false); |
3599 | 0 | } |
3600 | 28 | g_free(ss7pcspath); |
3601 | 28 | } |
3602 | | |
3603 | | /* SS7PC Name Resolution End*/ |
3604 | | |
3605 | | /* TACS */ |
3606 | | static bool |
3607 | | read_tacs_file(const char *tacspath) |
3608 | 28 | { |
3609 | 28 | FILE *hf; |
3610 | 28 | char line[MAX_LINELEN]; |
3611 | 28 | char *cp; |
3612 | 28 | uint16_t id; |
3613 | | |
3614 | | /* |
3615 | | * File format is TAC(decimal)<tab/space>TACName (no spaces) |
3616 | | */ |
3617 | 28 | if ((hf = ws_fopen(tacspath, "r")) == NULL) |
3618 | 28 | return false; |
3619 | | |
3620 | 0 | while (fgetline(line, sizeof(line), hf) >= 0) { |
3621 | 0 | if ((cp = strchr(line, '#'))) |
3622 | 0 | *cp = '\0'; |
3623 | |
|
3624 | 0 | if ((cp = strtok(line, " \t")) == NULL) |
3625 | 0 | continue; |
3626 | | |
3627 | 0 | if (sscanf(cp, "%" SCNu16, &id) != 1) { |
3628 | 0 | continue; |
3629 | 0 | } |
3630 | | |
3631 | 0 | if ((cp = strtok(NULL, " \t\n")) == NULL) |
3632 | 0 | continue; /* no TAC name */ |
3633 | | |
3634 | 0 | if (!wmem_map_lookup(tac_hash_table, GUINT_TO_POINTER(id))) { |
3635 | 0 | char *buf = wmem_strdup(addr_resolv_scope, cp); |
3636 | 0 | wmem_map_insert(tac_hash_table, GUINT_TO_POINTER(id), (void *)buf); |
3637 | 0 | } |
3638 | 0 | } |
3639 | |
|
3640 | 0 | fclose(hf); |
3641 | 0 | return true; |
3642 | 28 | } |
3643 | | |
3644 | | static void |
3645 | | initialize_tacs(const char* app_env_var_prefix) |
3646 | 28 | { |
3647 | 28 | char *tacspath; |
3648 | 28 | ws_assert(tac_hash_table == NULL); |
3649 | 28 | tac_hash_table = wmem_map_new(addr_resolv_scope, g_direct_hash, g_direct_equal); |
3650 | | |
3651 | 28 | tacspath = get_persconffile_path(ENAME_TACS, true, app_env_var_prefix); |
3652 | 28 | if (!read_tacs_file(tacspath) && errno != ENOENT) { |
3653 | 0 | report_open_failure(tacspath, errno, false); |
3654 | 0 | } |
3655 | 28 | g_free(tacspath); |
3656 | 28 | } |
3657 | | |
3658 | | static void |
3659 | | tac_name_lookup_cleanup(void) |
3660 | 14 | { |
3661 | 14 | tac_hash_table = NULL; |
3662 | 14 | } |
3663 | | |
3664 | | const char * |
3665 | | tac_name_lookup(const unsigned id) |
3666 | 0 | { |
3667 | 0 | return (const char *)wmem_map_lookup(tac_hash_table, GUINT_TO_POINTER(id)); |
3668 | 0 | } |
3669 | | /* TAC END */ |
3670 | | |
3671 | | /* |
3672 | | * External Functions |
3673 | | */ |
3674 | | |
3675 | | void |
3676 | | addr_resolve_pref_init(module_t *nameres) |
3677 | 14 | { |
3678 | 14 | prefs_register_bool_preference(nameres, "mac_name", |
3679 | 14 | "Resolve MAC addresses", |
3680 | 14 | "Resolve Ethernet MAC addresses to host names from the preferences" |
3681 | 14 | " or system's Ethers file, or to a manufacturer based name.", |
3682 | 14 | &gbl_resolv_flags.mac_name); |
3683 | | |
3684 | 14 | prefs_register_bool_preference(nameres, "transport_name", |
3685 | 14 | "Resolve transport names", |
3686 | 14 | "Resolve TCP/UDP ports into service names", |
3687 | 14 | &gbl_resolv_flags.transport_name); |
3688 | | |
3689 | 14 | prefs_register_bool_preference(nameres, "network_name", |
3690 | 14 | "Resolve network (IP) addresses", |
3691 | 14 | "Resolve IPv4, IPv6, and IPX addresses into host names." |
3692 | 14 | " The next set of check boxes determines how name resolution should be performed." |
3693 | 14 | " If no other options are checked name resolution is made from Wireshark's host, subnets or subnetsipv6 file" |
3694 | 14 | " and capture file name resolution blocks.", |
3695 | 14 | &gbl_resolv_flags.network_name); |
3696 | | |
3697 | 14 | prefs_register_bool_preference(nameres, "dns_pkt_addr_resolution", |
3698 | 14 | "Use captured DNS packet data for name resolution", |
3699 | 14 | "Use address/name pairs found in captured DNS packets for name resolution.", |
3700 | 14 | &gbl_resolv_flags.dns_pkt_addr_resolution); |
3701 | | |
3702 | 14 | prefs_register_bool_preference(nameres, "handshake_sni_addr_resolution", |
3703 | 14 | "Use SNI information from captured handshake packets", |
3704 | 14 | "Use the Server Name Indication found in TLS handshakes for name resolution.", |
3705 | 14 | &gbl_resolv_flags.handshake_sni_addr_resolution); |
3706 | | |
3707 | 14 | prefs_register_bool_preference(nameres, "use_external_name_resolver", |
3708 | 14 | "Use your system's DNS settings for name resolution", |
3709 | 14 | "Use your system's configured name resolver" |
3710 | 14 | " (usually DNS) to resolve network names." |
3711 | 14 | " Only applies when network name resolution" |
3712 | 14 | " is enabled.", |
3713 | 14 | &gbl_resolv_flags.use_external_net_name_resolver); |
3714 | | |
3715 | 14 | prefs_register_bool_preference(nameres, "use_custom_dns_servers", |
3716 | 14 | "Use a custom list of DNS servers for name resolution", |
3717 | 14 | "Use a DNS Servers list to resolve network names if true. If false, default information is used", |
3718 | 14 | &use_custom_dns_server_list); |
3719 | | |
3720 | 14 | static uat_field_t dns_server_uats_flds[] = { |
3721 | 14 | UAT_FLD_CSTRING_OTHER(dnsserverlist_uats, ipaddr, "IP address", dnsserver_uat_fld_ip_chk_cb, "IPv4 or IPv6 address"), |
3722 | 14 | UAT_FLD_CSTRING_OTHER(dnsserverlist_uats, tcp_port, "TCP Port", dnsserver_uat_fld_port_chk_cb, "Port Number (TCP)"), |
3723 | 14 | UAT_FLD_CSTRING_OTHER(dnsserverlist_uats, udp_port, "UDP Port", dnsserver_uat_fld_port_chk_cb, "Port Number (UDP)"), |
3724 | 14 | UAT_END_FIELDS |
3725 | 14 | }; |
3726 | | |
3727 | 14 | dnsserver_uat = uat_new("DNS Servers", |
3728 | 14 | sizeof(struct dns_server_data), |
3729 | 14 | "addr_resolve_dns_servers", /* filename */ |
3730 | 14 | true, /* from_profile */ |
3731 | 14 | &dnsserverlist_uats, /* data_ptr */ |
3732 | 14 | &ndnsservers, /* numitems_ptr */ |
3733 | 14 | UAT_AFFECTS_DISSECTION, |
3734 | 14 | NULL, |
3735 | 14 | dns_server_copy_cb, |
3736 | 14 | NULL, |
3737 | 14 | dns_server_free_cb, |
3738 | 14 | c_ares_set_dns_servers, |
3739 | 14 | NULL, |
3740 | 14 | dns_server_uats_flds); |
3741 | 14 | static const char *dnsserver_uat_defaults[] = { NULL, "53", "53" }; |
3742 | 14 | uat_set_default_values(dnsserver_uat, dnsserver_uat_defaults); |
3743 | 14 | prefs_register_uat_preference(nameres, "dns_servers", |
3744 | 14 | "DNS Servers", |
3745 | 14 | "A table of IPv4 and IPv6 addresses of DNS servers to be used to resolve IP names and addresses", |
3746 | 14 | dnsserver_uat); |
3747 | | |
3748 | 14 | prefs_register_obsolete_preference(nameres, "concurrent_dns"); |
3749 | | |
3750 | 14 | prefs_register_uint_preference(nameres, "name_resolve_concurrency", |
3751 | 14 | "Maximum concurrent requests", |
3752 | 14 | "The maximum number of DNS requests that may" |
3753 | 14 | " be active at any time. A large value (many" |
3754 | 14 | " thousands) might overload the network or make" |
3755 | 14 | " your DNS server behave badly.", |
3756 | 14 | 10, |
3757 | 14 | &name_resolve_concurrency); |
3758 | | |
3759 | 14 | prefs_register_obsolete_preference(nameres, "hosts_file_handling"); |
3760 | | |
3761 | 14 | prefs_register_bool_preference(nameres, "vlan_name", |
3762 | 14 | "Resolve VLAN IDs", |
3763 | 14 | "Resolve VLAN IDs to network names from the preferences \"vlans\" file." |
3764 | 14 | " Format of the file is: \"ID<Tab>Name\"." |
3765 | 14 | " One line per VLAN, e.g.: 1 Management", |
3766 | 14 | &gbl_resolv_flags.vlan_name); |
3767 | | |
3768 | 14 | prefs_register_bool_preference(nameres, "ss7_pc_name", |
3769 | 14 | "Resolve SS7 PCs", |
3770 | 14 | "Resolve SS7 Point Codes to node names from the profiles \"ss7pcs\" file." |
3771 | 14 | " Format of the file is: \"Network_Indicator<Dash>PC_Decimal<Tab>Name\"." |
3772 | 14 | " One line per Point Code, e.g.: 2-1234 MyPointCode1", |
3773 | 14 | &gbl_resolv_flags.ss7pc_name); |
3774 | | |
3775 | 14 | prefs_register_bool_preference(nameres, "tac_name", |
3776 | 14 | "Resolve TAC", |
3777 | 14 | "Resolve TAC to area names from the preferences \"tac\" file." |
3778 | 14 | " Format of the file is: \"TAC(decimail)<Tab/space>Name\"." |
3779 | 14 | " One line per TAC, e.g.: 30123 City1", |
3780 | 14 | &gbl_resolv_flags.tac_name); |
3781 | | |
3782 | 14 | } |
3783 | | |
3784 | | void addr_resolve_pref_apply(void) |
3785 | 0 | { |
3786 | 0 | c_ares_set_dns_servers(); |
3787 | 0 | maxmind_db_pref_apply(); |
3788 | 0 | } |
3789 | | |
3790 | | void |
3791 | 0 | disable_name_resolution(void) { |
3792 | 0 | gbl_resolv_flags.mac_name = false; |
3793 | 0 | gbl_resolv_flags.network_name = false; |
3794 | 0 | gbl_resolv_flags.transport_name = false; |
3795 | 0 | gbl_resolv_flags.dns_pkt_addr_resolution = false; |
3796 | 0 | gbl_resolv_flags.handshake_sni_addr_resolution = false; |
3797 | 0 | gbl_resolv_flags.use_external_net_name_resolver = false; |
3798 | 0 | gbl_resolv_flags.vlan_name = false; |
3799 | 0 | gbl_resolv_flags.ss7pc_name = false; |
3800 | 0 | gbl_resolv_flags.maxmind_geoip = false; |
3801 | 0 | gbl_resolv_flags.tac_name = false; |
3802 | 0 | } |
3803 | | |
3804 | | bool |
3805 | 0 | host_name_lookup_process(void) { |
3806 | 0 | struct timeval tv = { 0, 0 }; |
3807 | 0 | int nfds; |
3808 | 0 | fd_set rfds, wfds; |
3809 | 0 | bool nro = new_resolved_objects; |
3810 | |
|
3811 | 0 | new_resolved_objects = false; |
3812 | 0 | nro |= maxmind_db_lookup_process(); |
3813 | |
|
3814 | 0 | if (!async_dns_initialized) |
3815 | | /* c-ares not initialized. Bail out and cancel timers. */ |
3816 | 0 | return nro; |
3817 | | |
3818 | 0 | process_async_dns_queue(); |
3819 | |
|
3820 | 0 | FD_ZERO(&rfds); |
3821 | 0 | FD_ZERO(&wfds); |
3822 | 0 | nfds = ares_fds(ghba_chan, &rfds, &wfds); |
3823 | 0 | if (nfds > 0) { |
3824 | 0 | if (select(nfds, &rfds, &wfds, NULL, &tv) == -1) { /* call to select() failed */ |
3825 | | /* If it's interrupted by a signal, no need to put out a message */ |
3826 | 0 | if (errno != EINTR) |
3827 | 0 | fprintf(stderr, "Warning: call to select() failed, error is %s\n", g_strerror(errno)); |
3828 | 0 | return nro; |
3829 | 0 | } |
3830 | 0 | ares_process(ghba_chan, &rfds, &wfds); |
3831 | 0 | } |
3832 | | |
3833 | | /* Any new entries? */ |
3834 | 0 | return nro; |
3835 | 0 | } |
3836 | | |
3837 | | static void |
3838 | 14 | _host_name_lookup_cleanup(void) { |
3839 | 14 | async_dns_queue_head = NULL; |
3840 | | |
3841 | 14 | if (async_dns_initialized) { |
3842 | 14 | ares_destroy(ghba_chan); |
3843 | 14 | ares_destroy(ghbn_chan); |
3844 | 14 | } |
3845 | 14 | #ifdef CARES_HAVE_ARES_LIBRARY_INIT |
3846 | 14 | ares_library_cleanup(); |
3847 | 14 | #endif |
3848 | 14 | async_dns_initialized = false; |
3849 | 14 | } |
3850 | | |
3851 | | const char * |
3852 | | get_hostname(const unsigned addr) |
3853 | 135 | { |
3854 | | /* XXX why do we call this if we're not resolving? To create hash entries? |
3855 | | * Why? So that we can return a const char*? |
3856 | | * |
3857 | | * Note the returned string is in addr_resolv_scope, which has a similar |
3858 | | * life to the global file scope (slightly larger, in that the resolved |
3859 | | * addresses need to be available during dissector registration, e.g. |
3860 | | * for RADIUS and enterprises), so if not copied it is possible to use |
3861 | | * it after freeing. |
3862 | | * |
3863 | | * Should this be deprecated in favor of get_hostname_wmem so that |
3864 | | * host name lookups don't increase persistent memory usage even when |
3865 | | * hostname lookups are disabled? (An alternative would be to return |
3866 | | * NULL when lookups are disabled, but callers don't expect that.) |
3867 | | */ |
3868 | 135 | hashipv4_t *tp = host_lookup(addr); |
3869 | | |
3870 | 135 | if (!gbl_resolv_flags.network_name) |
3871 | 135 | return tp->ip; |
3872 | | |
3873 | 0 | tp->flags |= RESOLVED_ADDRESS_USED; |
3874 | |
|
3875 | 0 | return tp->name; |
3876 | 135 | } |
3877 | | |
3878 | | char * |
3879 | | get_hostname_wmem(wmem_allocator_t *allocator, const unsigned addr) |
3880 | 67.3k | { |
3881 | 67.3k | if (!gbl_resolv_flags.network_name) |
3882 | 67.3k | return ip_addr_to_str(allocator, &addr); |
3883 | | |
3884 | 0 | hashipv4_t *tp = host_lookup(addr); |
3885 | |
|
3886 | 0 | tp->flags |= RESOLVED_ADDRESS_USED; |
3887 | |
|
3888 | 0 | return wmem_strdup(allocator, tp->name); |
3889 | 67.3k | } |
3890 | | /* -------------------------- */ |
3891 | | |
3892 | | const char * |
3893 | | get_hostname6(const ws_in6_addr *addr) |
3894 | 1 | { |
3895 | | /* XXX why do we call this if we're not resolving? To create hash entries? |
3896 | | * Why? The same comments as get_hostname above apply. |
3897 | | */ |
3898 | 1 | hashipv6_t *tp = host_lookup6(addr); |
3899 | | |
3900 | 1 | if (!gbl_resolv_flags.network_name) |
3901 | 1 | return tp->ip6; |
3902 | | |
3903 | 0 | tp->flags |= RESOLVED_ADDRESS_USED; |
3904 | |
|
3905 | 0 | return tp->name; |
3906 | 1 | } |
3907 | | |
3908 | | char * |
3909 | | get_hostname6_wmem(wmem_allocator_t *allocator, const ws_in6_addr *addr) |
3910 | 28.4k | { |
3911 | 28.4k | if (!gbl_resolv_flags.network_name) |
3912 | 28.4k | return ip6_to_str(allocator, addr); |
3913 | | |
3914 | 0 | hashipv6_t *tp = host_lookup6(addr); |
3915 | |
|
3916 | 0 | tp->flags |= RESOLVED_ADDRESS_USED; |
3917 | |
|
3918 | 0 | return wmem_strdup(allocator, tp->name); |
3919 | 28.4k | } |
3920 | | /* -------------------------- */ |
3921 | | void |
3922 | | add_ipv4_name(const unsigned addr, const char *name, bool static_entry) |
3923 | 422 | { |
3924 | 422 | hashipv4_t *tp; |
3925 | | |
3926 | | /* |
3927 | | * Don't add zero-length names; apparently, some resolvers will return |
3928 | | * them if they get them from DNS. |
3929 | | */ |
3930 | 422 | if (!name || name[0] == '\0') |
3931 | 63 | return; |
3932 | | |
3933 | 359 | tp = (hashipv4_t *)wmem_map_lookup(ipv4_hash_table, GUINT_TO_POINTER(addr)); |
3934 | 359 | if (!tp) { |
3935 | 61 | tp = new_ipv4(addr); |
3936 | 61 | wmem_map_insert(ipv4_hash_table, GUINT_TO_POINTER(addr), tp); |
3937 | 61 | } |
3938 | | |
3939 | 359 | if (g_ascii_strcasecmp(tp->name, name) && (static_entry || !(tp->flags & STATIC_HOSTNAME))) { |
3940 | 173 | (void) g_strlcpy(tp->name, name, MAXDNSNAMELEN); |
3941 | 173 | new_resolved_objects = true; |
3942 | 173 | if (static_entry) |
3943 | 0 | tp->flags |= STATIC_HOSTNAME; |
3944 | 173 | } |
3945 | 359 | tp->flags |= TRIED_RESOLVE_ADDRESS|NAME_RESOLVED; |
3946 | 359 | } /* add_ipv4_name */ |
3947 | | |
3948 | | /* -------------------------- */ |
3949 | | void |
3950 | | add_ipv6_name(const ws_in6_addr *addrp, const char *name, const bool static_entry) |
3951 | 496 | { |
3952 | 496 | hashipv6_t *tp; |
3953 | | |
3954 | | /* |
3955 | | * Don't add zero-length names; apparently, some resolvers will return |
3956 | | * them if they get them from DNS. |
3957 | | */ |
3958 | 496 | if (!name || name[0] == '\0') |
3959 | 70 | return; |
3960 | | |
3961 | 426 | tp = (hashipv6_t *)wmem_map_lookup(ipv6_hash_table, addrp); |
3962 | 426 | if (!tp) { |
3963 | 193 | ws_in6_addr *addr_key; |
3964 | | |
3965 | 193 | addr_key = wmem_new(addr_resolv_scope, ws_in6_addr); |
3966 | 193 | tp = new_ipv6(addrp); |
3967 | 193 | memcpy(addr_key, addrp, 16); |
3968 | 193 | wmem_map_insert(ipv6_hash_table, addr_key, tp); |
3969 | 193 | } |
3970 | | |
3971 | 426 | if (g_ascii_strcasecmp(tp->name, name) && (static_entry || !(tp->flags & STATIC_HOSTNAME))) { |
3972 | 206 | (void) g_strlcpy(tp->name, name, MAXDNSNAMELEN); |
3973 | 206 | new_resolved_objects = true; |
3974 | 206 | if (static_entry) |
3975 | 0 | tp->flags |= STATIC_HOSTNAME; |
3976 | 206 | } |
3977 | 426 | tp->flags |= TRIED_RESOLVE_ADDRESS|NAME_RESOLVED; |
3978 | 426 | } /* add_ipv6_name */ |
3979 | | |
3980 | | static void |
3981 | | add_manually_resolved_ipv4(void *key, void *value, void *user_data _U_) |
3982 | 0 | { |
3983 | 0 | resolved_name_t *resolved_ipv4_entry = (resolved_name_t*)value; |
3984 | 0 | add_ipv4_name(GPOINTER_TO_UINT(key), resolved_ipv4_entry->name, true); |
3985 | 0 | } |
3986 | | |
3987 | | static void |
3988 | | add_manually_resolved_ipv6(void *key, void *value, void *user_data _U_) |
3989 | 0 | { |
3990 | 0 | resolved_name_t *resolved_ipv6_entry = (resolved_name_t*)value; |
3991 | 0 | add_ipv6_name((ws_in6_addr*)key, resolved_ipv6_entry->name, true); |
3992 | 0 | } |
3993 | | |
3994 | | static void |
3995 | | add_manually_resolved(void) |
3996 | 28 | { |
3997 | 28 | if (manually_resolved_ipv4_list) { |
3998 | 28 | wmem_map_foreach(manually_resolved_ipv4_list, add_manually_resolved_ipv4, NULL); |
3999 | 28 | } |
4000 | | |
4001 | 28 | if (manually_resolved_ipv6_list) { |
4002 | 28 | wmem_map_foreach(manually_resolved_ipv6_list, add_manually_resolved_ipv6, NULL); |
4003 | 28 | } |
4004 | 28 | } |
4005 | | |
4006 | | static void |
4007 | | host_name_lookup_init(const char* app_env_var_prefix) |
4008 | 28 | { |
4009 | 28 | char *hostspath; |
4010 | 28 | unsigned i; |
4011 | | |
4012 | 28 | ws_assert(ipxnet_hash_table == NULL); |
4013 | 28 | ipxnet_hash_table = wmem_map_new(addr_resolv_scope, g_direct_hash, g_direct_equal); |
4014 | | |
4015 | 28 | ws_assert(ipv4_hash_table == NULL); |
4016 | 28 | ipv4_hash_table = wmem_map_new(addr_resolv_scope, g_direct_hash, g_direct_equal); |
4017 | | |
4018 | 28 | ws_assert(ipv6_hash_table == NULL); |
4019 | 28 | ipv6_hash_table = wmem_map_new(addr_resolv_scope, ipv6_oat_hash, ipv6_equal); |
4020 | | |
4021 | 28 | ws_assert(async_dns_queue_head == NULL); |
4022 | 28 | async_dns_queue_head = wmem_list_new(addr_resolv_scope); |
4023 | | |
4024 | | /* |
4025 | | * The manually resolved lists are the only address resolution maps |
4026 | | * that are not reset by addr_resolv_cleanup(), because they are |
4027 | | * the only ones that do not have entries from personal configuration |
4028 | | * files that can change when changing configurations. All their |
4029 | | * entries must also be in epan scope. |
4030 | | */ |
4031 | 28 | if (manually_resolved_ipv4_list == NULL) |
4032 | 14 | manually_resolved_ipv4_list = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal); |
4033 | | |
4034 | 28 | if (manually_resolved_ipv6_list == NULL) |
4035 | 14 | manually_resolved_ipv6_list = wmem_map_new(wmem_epan_scope(), ws_ipv6_hash, ipv6_equal); |
4036 | | |
4037 | | /* |
4038 | | * Load the global hosts file, if we have one. |
4039 | | */ |
4040 | 28 | hostspath = get_datafile_path(ENAME_HOSTS, app_env_var_prefix); |
4041 | 28 | if (!read_hosts_file(hostspath, true) && errno != ENOENT) { |
4042 | 0 | report_open_failure(hostspath, errno, false); |
4043 | 0 | } |
4044 | 28 | g_free(hostspath); |
4045 | | /* |
4046 | | * Load the user's hosts file no matter what, if they have one. |
4047 | | */ |
4048 | 28 | hostspath = get_persconffile_path(ENAME_HOSTS, true, app_env_var_prefix); |
4049 | 28 | if (!read_hosts_file(hostspath, true) && errno != ENOENT) { |
4050 | 0 | report_open_failure(hostspath, errno, false); |
4051 | 0 | } |
4052 | 28 | g_free(hostspath); |
4053 | 28 | #ifdef CARES_HAVE_ARES_LIBRARY_INIT |
4054 | 28 | if (ares_library_init(ARES_LIB_INIT_ALL) == ARES_SUCCESS) { |
4055 | 28 | #endif |
4056 | | /* XXX - Check which options we should set */ |
4057 | 28 | if (ares_init_options(&ghba_chan, NULL, 0) == ARES_SUCCESS && ares_init_options(&ghbn_chan, NULL, 0) == ARES_SUCCESS) { |
4058 | 28 | async_dns_initialized = true; |
4059 | 28 | c_ares_set_dns_servers(); |
4060 | 28 | } |
4061 | 28 | #ifdef CARES_HAVE_ARES_LIBRARY_INIT |
4062 | 28 | } |
4063 | 28 | #endif |
4064 | | |
4065 | 28 | if (extra_hosts_files) { |
4066 | 0 | for (i = 0; i < extra_hosts_files->len; i++) { |
4067 | 0 | read_hosts_file((const char *) g_ptr_array_index(extra_hosts_files, i), true); |
4068 | 0 | } |
4069 | 0 | } |
4070 | | |
4071 | 28 | subnet_name_lookup_init(app_env_var_prefix); |
4072 | 28 | subnet6_name_lookup_init(app_env_var_prefix); |
4073 | | |
4074 | 28 | add_manually_resolved(); |
4075 | | |
4076 | 28 | ss7pc_name_lookup_init(app_env_var_prefix); |
4077 | 28 | } |
4078 | | |
4079 | | static void |
4080 | | host_name_lookup_cleanup(void) |
4081 | 14 | { |
4082 | 14 | uint32_t i, j; |
4083 | 14 | sub_net_hashipv4_t *entry, *next_entry; |
4084 | | |
4085 | 14 | _host_name_lookup_cleanup(); |
4086 | | |
4087 | 14 | ipxnet_hash_table = NULL; |
4088 | 14 | ipv4_hash_table = NULL; |
4089 | 14 | ipv6_hash_table = NULL; |
4090 | 14 | ss7pc_hash_table = NULL; |
4091 | | |
4092 | 462 | for(i = 0; i < SUBNETLENGTHSIZE; ++i) { |
4093 | 448 | if (subnet_length_entries[i].subnet_addresses != NULL) { |
4094 | 0 | for (j = 0; j < HASHHOSTSIZE; j++) { |
4095 | 0 | for (entry = subnet_length_entries[i].subnet_addresses[j]; |
4096 | 0 | entry != NULL; entry = next_entry) { |
4097 | 0 | next_entry = entry->next; |
4098 | 0 | wmem_free(addr_resolv_scope, entry); |
4099 | 0 | } |
4100 | 0 | } |
4101 | 0 | wmem_free(addr_resolv_scope, subnet_length_entries[i].subnet_addresses); |
4102 | 0 | subnet_length_entries[i].subnet_addresses = NULL; |
4103 | 0 | } |
4104 | 448 | } |
4105 | | |
4106 | 14 | have_subnet_entry = false; |
4107 | | |
4108 | 1.80k | for(i = 0; i < SUBNETLENGTHSIZE_V6; ++i) { |
4109 | 1.79k | sub_net_hashipv6_t *entry6, *next_entry6; |
4110 | 1.79k | if (subnet_length_entries_v6[i].subnet_addresses != NULL) { |
4111 | 0 | for (j = 0; j < HASHHOSTSIZE; j++) { |
4112 | 0 | for (entry6 = subnet_length_entries_v6[i].subnet_addresses[j]; |
4113 | 0 | entry6 != NULL; entry6 = next_entry6) { |
4114 | 0 | next_entry6 = entry6->next; |
4115 | 0 | wmem_free(addr_resolv_scope, entry6); |
4116 | 0 | } |
4117 | 0 | } |
4118 | 0 | wmem_free(addr_resolv_scope, subnet_length_entries_v6[i].subnet_addresses); |
4119 | 0 | subnet_length_entries_v6[i].subnet_addresses = NULL; |
4120 | 0 | } |
4121 | 1.79k | } |
4122 | 14 | have_subnet_entry_v6 = false; |
4123 | | |
4124 | 14 | new_resolved_objects = false; |
4125 | 14 | } |
4126 | | |
4127 | | |
4128 | | void host_name_lookup_reset(const char* app_env_var_prefix) |
4129 | 14 | { |
4130 | 14 | addr_resolv_cleanup(); |
4131 | 14 | addr_resolv_init(app_env_var_prefix); |
4132 | 14 | } |
4133 | | |
4134 | | char * |
4135 | | udp_port_to_display(wmem_allocator_t *allocator, unsigned port) |
4136 | 26.5k | { |
4137 | | |
4138 | 26.5k | if (!gbl_resolv_flags.transport_name) { |
4139 | 26.5k | return wmem_utoa(allocator, port); |
4140 | 26.5k | } |
4141 | | |
4142 | 0 | return wmem_strdup(allocator, serv_name_lookup(PT_UDP, port)); |
4143 | | |
4144 | 26.5k | } /* udp_port_to_display */ |
4145 | | |
4146 | | char * |
4147 | | dccp_port_to_display(wmem_allocator_t *allocator, unsigned port) |
4148 | 0 | { |
4149 | |
|
4150 | 0 | if (!gbl_resolv_flags.transport_name) { |
4151 | 0 | return wmem_utoa(allocator, port); |
4152 | 0 | } |
4153 | | |
4154 | 0 | return wmem_strdup(allocator, serv_name_lookup(PT_DCCP, port)); |
4155 | |
|
4156 | 0 | } /* dccp_port_to_display */ |
4157 | | |
4158 | | char * |
4159 | | tcp_port_to_display(wmem_allocator_t *allocator, unsigned port) |
4160 | 31.3k | { |
4161 | | |
4162 | 31.3k | if (!gbl_resolv_flags.transport_name) { |
4163 | 31.3k | return wmem_utoa(allocator, port); |
4164 | 31.3k | } |
4165 | | |
4166 | 0 | return wmem_strdup(allocator, serv_name_lookup(PT_TCP, port)); |
4167 | | |
4168 | 31.3k | } /* tcp_port_to_display */ |
4169 | | |
4170 | | char * |
4171 | | sctp_port_to_display(wmem_allocator_t *allocator, unsigned port) |
4172 | 31.7k | { |
4173 | | |
4174 | 31.7k | if (!gbl_resolv_flags.transport_name) { |
4175 | 31.7k | return wmem_utoa(allocator, port); |
4176 | 31.7k | } |
4177 | | |
4178 | 0 | return wmem_strdup(allocator, serv_name_lookup(PT_SCTP, port)); |
4179 | | |
4180 | 31.7k | } /* sctp_port_to_display */ |
4181 | | |
4182 | | char * |
4183 | | port_with_resolution_to_str(wmem_allocator_t *scope, port_type proto, unsigned port) |
4184 | 166k | { |
4185 | 166k | const char *port_str; |
4186 | | |
4187 | 166k | if (!gbl_resolv_flags.transport_name || (proto == PT_NONE)) { |
4188 | | /* No name resolution support, just return port string */ |
4189 | 166k | return wmem_strdup_printf(scope, "%u", port); |
4190 | 166k | } |
4191 | 0 | port_str = serv_name_lookup(proto, port); |
4192 | 0 | ws_assert(port_str); |
4193 | 0 | return wmem_strdup_printf(scope, "%s (%u)", port_str, port); |
4194 | 166k | } |
4195 | | |
4196 | | int |
4197 | | port_with_resolution_to_str_buf(char *buf, unsigned long buf_size, port_type proto, unsigned port) |
4198 | 0 | { |
4199 | 0 | const char *port_str; |
4200 | |
|
4201 | 0 | if (!gbl_resolv_flags.transport_name || (proto == PT_NONE)) { |
4202 | | /* No name resolution support, just return port string */ |
4203 | 0 | return snprintf(buf, buf_size, "%u", port); |
4204 | 0 | } |
4205 | 0 | port_str = serv_name_lookup(proto, port); |
4206 | 0 | ws_assert(port_str); |
4207 | 0 | return snprintf(buf, buf_size, "%s (%u)", port_str, port); |
4208 | 0 | } |
4209 | | |
4210 | | const char * |
4211 | | get_ether_name(const uint8_t *addr) |
4212 | 72.5k | { |
4213 | 72.5k | hashether_t *tp; |
4214 | 72.5k | bool resolve = gbl_resolv_flags.mac_name; |
4215 | | |
4216 | 72.5k | tp = eth_name_lookup(addr, resolve); |
4217 | | |
4218 | 72.5k | return resolve ? tp->resolved_name : tp->hexaddr; |
4219 | | |
4220 | 72.5k | } /* get_ether_name */ |
4221 | | |
4222 | | const char * |
4223 | | tvb_get_ether_name(tvbuff_t *tvb, unsigned offset) |
4224 | 0 | { |
4225 | 0 | return get_ether_name(tvb_get_ptr(tvb, offset, 6)); |
4226 | 0 | } |
4227 | | |
4228 | | /* Look for a (non-dummy) ether name in the hash, and return it if found. |
4229 | | * If it's not found, simply return NULL. |
4230 | | */ |
4231 | | const char * |
4232 | | get_ether_name_if_known(const uint8_t *addr) |
4233 | 819 | { |
4234 | 819 | hashether_t *tp; |
4235 | | |
4236 | | /* Initialize ether structs if we're the first |
4237 | | * ether-related function called */ |
4238 | 819 | if (!gbl_resolv_flags.mac_name) |
4239 | 0 | return NULL; |
4240 | | |
4241 | | /* eth_name_lookup will create a (resolved) hash entry |
4242 | | * if it doesn't exist, so it never returns NULL */ |
4243 | 819 | tp = eth_name_lookup(addr, true); |
4244 | | |
4245 | 819 | if ((tp->flags & (NAME_RESOLVED | NAME_RESOLVED_PREFIX)) == NAME_RESOLVED) { |
4246 | | /* Name is from an exact match, not a prefix/OUI */ |
4247 | 0 | return tp->resolved_name; |
4248 | 0 | } |
4249 | 819 | else { |
4250 | | /* Name was created */ |
4251 | 819 | return NULL; |
4252 | 819 | } |
4253 | 819 | } |
4254 | | |
4255 | | void |
4256 | | add_ether_byip(const unsigned ip, const uint8_t *eth) |
4257 | 0 | { |
4258 | 0 | hashipv4_t *tp; |
4259 | | |
4260 | | /* first check that IP address can be resolved */ |
4261 | 0 | if (!gbl_resolv_flags.network_name) |
4262 | 0 | return; |
4263 | | |
4264 | 0 | tp = host_lookup(ip); |
4265 | | |
4266 | | /* |
4267 | | * Was this IP address resolved to a host name? |
4268 | | */ |
4269 | 0 | if (tp->flags & NAME_RESOLVED) { |
4270 | | /* |
4271 | | * Yes, so add an entry in the ethers hashtable resolving |
4272 | | * the MAC address to that name. |
4273 | | */ |
4274 | 0 | add_eth_name(eth, tp->name, false); |
4275 | 0 | } |
4276 | |
|
4277 | 0 | } /* add_ether_byip */ |
4278 | | |
4279 | | char * |
4280 | | get_ipxnet_name(wmem_allocator_t *allocator, const uint32_t addr) |
4281 | 0 | { |
4282 | |
|
4283 | 0 | if (!gbl_resolv_flags.network_name) { |
4284 | 0 | return ipxnet_to_str_punct(allocator, addr, '\0'); |
4285 | 0 | } |
4286 | | |
4287 | 0 | return ipxnet_name_lookup(allocator, addr); |
4288 | |
|
4289 | 0 | } /* get_ipxnet_name */ |
4290 | | |
4291 | | char * |
4292 | | get_vlan_name(wmem_allocator_t *allocator, const uint16_t id) |
4293 | 0 | { |
4294 | |
|
4295 | 0 | if (!gbl_resolv_flags.vlan_name) { |
4296 | 0 | return NULL; |
4297 | 0 | } |
4298 | | |
4299 | 0 | return wmem_strdup(allocator, vlan_name_lookup(id)); |
4300 | |
|
4301 | 0 | } /* get_vlan_name */ |
4302 | | |
4303 | | const char * |
4304 | | get_manuf_name(const uint8_t *addr, size_t size) |
4305 | 4 | { |
4306 | 4 | hashmanuf_t *manuf_value; |
4307 | | |
4308 | 4 | ws_return_val_if(size < 3, NULL); |
4309 | | |
4310 | 4 | manuf_value = manuf_name_lookup(addr, size); |
4311 | 4 | if (gbl_resolv_flags.mac_name && ((manuf_value->flags & NAME_RESOLVED) == NAME_RESOLVED)) |
4312 | 3 | return manuf_value->resolved_name; |
4313 | | |
4314 | 1 | return manuf_value->hexaddr; |
4315 | | |
4316 | 4 | } /* get_manuf_name */ |
4317 | | |
4318 | | const char * |
4319 | | tvb_get_manuf_name(tvbuff_t *tvb, unsigned offset) |
4320 | 4 | { |
4321 | 4 | uint8_t buf[3] = { 0 }; |
4322 | 4 | tvb_memcpy(tvb, buf, offset, 3); |
4323 | 4 | return get_manuf_name(buf, sizeof(buf)); |
4324 | 4 | } |
4325 | | |
4326 | | const char * |
4327 | | get_manuf_name_if_known(const uint8_t *addr, size_t size) |
4328 | 55.6k | { |
4329 | 55.6k | hashmanuf_t *manuf_value; |
4330 | | |
4331 | 55.6k | ws_return_val_if(size < 3, NULL); |
4332 | | |
4333 | 55.6k | manuf_value = manuf_name_lookup(addr, size); |
4334 | 55.6k | if (manuf_value != NULL && ((manuf_value->flags & NAME_RESOLVED) == NAME_RESOLVED)) { |
4335 | 17.3k | return manuf_value->resolved_longname; |
4336 | 17.3k | } |
4337 | | |
4338 | 38.2k | if (size >= 6) { |
4339 | | /* Try the global manuf tables. */ |
4340 | 38.1k | const char *short_name, *long_name; |
4341 | 38.1k | short_name = ws_manuf_lookup_str(addr, &long_name); |
4342 | 38.1k | if (short_name != NULL) { |
4343 | | /* Found it */ |
4344 | 601 | return long_name; |
4345 | 601 | } |
4346 | 38.1k | } |
4347 | | |
4348 | 37.6k | return NULL; |
4349 | | |
4350 | 38.2k | } /* get_manuf_name_if_known */ |
4351 | | |
4352 | | const char * |
4353 | | uint_get_manuf_name_if_known(const uint32_t manuf_key) |
4354 | 1.85k | { |
4355 | 1.85k | uint8_t addr[6] = { 0 }; |
4356 | 1.85k | addr[0] = (manuf_key >> 16) & 0xFF; |
4357 | 1.85k | addr[1] = (manuf_key >> 8) & 0xFF; |
4358 | 1.85k | addr[2] = manuf_key & 0xFF; |
4359 | | |
4360 | 1.85k | return get_manuf_name_if_known(addr, sizeof(addr)); |
4361 | 1.85k | } |
4362 | | |
4363 | | const char * |
4364 | | tvb_get_manuf_name_if_known(tvbuff_t *tvb, unsigned offset) |
4365 | 120 | { |
4366 | 120 | uint8_t buf[3] = { 0 }; |
4367 | 120 | tvb_memcpy(tvb, buf, offset, 3); |
4368 | 120 | return get_manuf_name_if_known(buf, sizeof(buf)); |
4369 | 120 | } |
4370 | | |
4371 | | bool get_hash_manuf_used(hashmanuf_t* manuf) |
4372 | 0 | { |
4373 | 0 | return ((manuf->flags & TRIED_OR_RESOLVED_MASK) == TRIED_OR_RESOLVED_MASK); |
4374 | 0 | } |
4375 | | |
4376 | | char* get_hash_manuf_resolved_name(hashmanuf_t* manuf) |
4377 | 0 | { |
4378 | 0 | return manuf->resolved_longname; |
4379 | 0 | } |
4380 | | |
4381 | | const char * |
4382 | | get_eui64_name(const uint8_t *addr) |
4383 | 3.28k | { |
4384 | 3.28k | hasheui64_t *tp; |
4385 | 3.28k | bool resolve = gbl_resolv_flags.mac_name; |
4386 | | |
4387 | 3.28k | tp = eui64_name_lookup(addr, resolve); |
4388 | | |
4389 | 3.28k | return resolve ? tp->resolved_name : tp->hexaddr; |
4390 | | |
4391 | 3.28k | } /* get_eui64_name */ |
4392 | | |
4393 | | char * |
4394 | | eui64_to_display(wmem_allocator_t *allocator, const uint64_t addr_eui64) |
4395 | 3.01k | { |
4396 | 3.01k | uint8_t addr[EUI64_ADDR_LEN]; |
4397 | | |
4398 | 3.01k | phtonu64(addr, addr_eui64); |
4399 | | |
4400 | 3.01k | const char *result = get_eui64_name(addr); |
4401 | | |
4402 | 3.01k | return wmem_strdup(allocator, result); |
4403 | 3.01k | } /* eui64_to_display */ |
4404 | | |
4405 | 0 | #define GHI_TIMEOUT (250 * 1000) |
4406 | | static void |
4407 | 0 | c_ares_ghi_cb(void *arg, int status, int timeouts _U_, struct hostent *hp) { |
4408 | | /* |
4409 | | * XXX - If we wanted to be really fancy we could cache results here and |
4410 | | * look them up in get_host_ipaddr* below. |
4411 | | * |
4412 | | * XXX - This only gets the first host address if there's more than one. |
4413 | | */ |
4414 | 0 | async_hostent_t *ahp = (async_hostent_t *)arg; |
4415 | 0 | if (status == ARES_SUCCESS && hp && ahp && hp->h_length == ahp->addr_size) { |
4416 | 0 | memcpy(ahp->addrp, hp->h_addr, hp->h_length); |
4417 | 0 | ahp->copied = hp->h_length; |
4418 | 0 | } |
4419 | 0 | } |
4420 | | |
4421 | | /* Translate a string, assumed either to be a dotted-quad IPv4 address or |
4422 | | * a host name, to a numeric IPv4 address. Return true if we succeed and |
4423 | | * set "*addrp" to that numeric IPv4 address; return false if we fail. */ |
4424 | | bool |
4425 | | get_host_ipaddr(const char *host, uint32_t *addrp) |
4426 | 0 | { |
4427 | 0 | struct timeval tv = { 0, GHI_TIMEOUT }, *tvp; |
4428 | 0 | int nfds; |
4429 | 0 | fd_set rfds, wfds; |
4430 | 0 | async_hostent_t ahe; |
4431 | | |
4432 | | /* |
4433 | | * XXX - are there places where this is used to translate something |
4434 | | * that's *only* supposed to be an IPv4 address, and where it |
4435 | | * *shouldn't* translate host names? |
4436 | | */ |
4437 | 0 | if (!ws_inet_pton4(host, addrp)) { |
4438 | | |
4439 | | /* It's not a valid dotted-quad IP address; is it a valid |
4440 | | * host name? |
4441 | | */ |
4442 | | |
4443 | | /* If we're not allowed to do name resolution, don't do name |
4444 | | * resolution... |
4445 | | * XXX - What if we're allowed to do name resolution, and the name |
4446 | | * is in a DNS packet we've dissected or in a Name Resolution Block, |
4447 | | * or a user-entered manual name resolution? |
4448 | | */ |
4449 | 0 | if (!gbl_resolv_flags.network_name || |
4450 | 0 | !gbl_resolv_flags.use_external_net_name_resolver) { |
4451 | 0 | return false; |
4452 | 0 | } |
4453 | | |
4454 | 0 | if (!async_dns_initialized || name_resolve_concurrency < 1) { |
4455 | 0 | return false; |
4456 | 0 | } |
4457 | 0 | ahe.addr_size = (int) sizeof (struct in_addr); |
4458 | 0 | ahe.copied = 0; |
4459 | 0 | ahe.addrp = addrp; |
4460 | 0 | ares_gethostbyname(ghbn_chan, host, AF_INET, c_ares_ghi_cb, &ahe); |
4461 | 0 | FD_ZERO(&rfds); |
4462 | 0 | FD_ZERO(&wfds); |
4463 | 0 | nfds = ares_fds(ghbn_chan, &rfds, &wfds); |
4464 | 0 | if (nfds > 0) { |
4465 | 0 | tvp = ares_timeout(ghbn_chan, &tv, &tv); |
4466 | 0 | if (select(nfds, &rfds, &wfds, NULL, tvp) == -1) { /* call to select() failed */ |
4467 | | /* If it's interrupted by a signal, no need to put out a message */ |
4468 | 0 | if (errno != EINTR) |
4469 | 0 | fprintf(stderr, "Warning: call to select() failed, error is %s\n", g_strerror(errno)); |
4470 | 0 | return false; |
4471 | 0 | } |
4472 | 0 | ares_process(ghbn_chan, &rfds, &wfds); |
4473 | 0 | } |
4474 | 0 | ares_cancel(ghbn_chan); |
4475 | 0 | if (ahe.addr_size == ahe.copied) { |
4476 | 0 | return true; |
4477 | 0 | } |
4478 | 0 | return false; |
4479 | 0 | } |
4480 | | |
4481 | 0 | return true; |
4482 | 0 | } |
4483 | | |
4484 | | /* |
4485 | | * Translate IPv6 numeric address or FQDN hostname into binary IPv6 address. |
4486 | | * Return true if we succeed and set "*addrp" to that numeric IPv6 address; |
4487 | | * return false if we fail. |
4488 | | */ |
4489 | | bool |
4490 | | get_host_ipaddr6(const char *host, ws_in6_addr *addrp) |
4491 | 0 | { |
4492 | 0 | struct timeval tv = { 0, GHI_TIMEOUT }, *tvp; |
4493 | 0 | int nfds; |
4494 | 0 | fd_set rfds, wfds; |
4495 | 0 | async_hostent_t ahe; |
4496 | |
|
4497 | 0 | if (str_to_ip6(host, addrp)) |
4498 | 0 | return true; |
4499 | | |
4500 | | /* It's not a valid dotted-quad IP address; is it a valid |
4501 | | * host name? |
4502 | | * |
4503 | | * XXX - are there places where this is used to translate something |
4504 | | * that's *only* supposed to be an IPv6 address, and where it |
4505 | | * *shouldn't* translate host names? |
4506 | | */ |
4507 | | |
4508 | | /* If we're not allowed to do name resolution, don't do name |
4509 | | * resolution... |
4510 | | * XXX - What if we're allowed to do name resolution, and the name |
4511 | | * is in a DNS packet we've dissected or in a Name Resolution Block, |
4512 | | * or a user-entered manual name resolution? |
4513 | | */ |
4514 | 0 | if (!gbl_resolv_flags.network_name || |
4515 | 0 | !gbl_resolv_flags.use_external_net_name_resolver) { |
4516 | 0 | return false; |
4517 | 0 | } |
4518 | | |
4519 | | /* try FQDN */ |
4520 | 0 | if (!async_dns_initialized || name_resolve_concurrency < 1) { |
4521 | 0 | return false; |
4522 | 0 | } |
4523 | 0 | ahe.addr_size = (int) sizeof (ws_in6_addr); |
4524 | 0 | ahe.copied = 0; |
4525 | 0 | ahe.addrp = addrp; |
4526 | 0 | ares_gethostbyname(ghbn_chan, host, AF_INET6, c_ares_ghi_cb, &ahe); |
4527 | 0 | FD_ZERO(&rfds); |
4528 | 0 | FD_ZERO(&wfds); |
4529 | 0 | nfds = ares_fds(ghbn_chan, &rfds, &wfds); |
4530 | 0 | if (nfds > 0) { |
4531 | 0 | tvp = ares_timeout(ghbn_chan, &tv, &tv); |
4532 | 0 | if (select(nfds, &rfds, &wfds, NULL, tvp) == -1) { /* call to select() failed */ |
4533 | | /* If it's interrupted by a signal, no need to put out a message */ |
4534 | 0 | if (errno != EINTR) |
4535 | 0 | fprintf(stderr, "Warning: call to select() failed, error is %s\n", g_strerror(errno)); |
4536 | 0 | return false; |
4537 | 0 | } |
4538 | 0 | ares_process(ghbn_chan, &rfds, &wfds); |
4539 | 0 | } |
4540 | 0 | ares_cancel(ghbn_chan); |
4541 | 0 | if (ahe.addr_size == ahe.copied) { |
4542 | 0 | return true; |
4543 | 0 | } |
4544 | | |
4545 | 0 | return false; |
4546 | 0 | } |
4547 | | |
4548 | | wmem_map_t * |
4549 | | get_manuf_hashtable(void) |
4550 | 0 | { |
4551 | 0 | return manuf_hashtable; |
4552 | 0 | } |
4553 | | |
4554 | | wmem_map_t * |
4555 | | get_wka_hashtable(void) |
4556 | 0 | { |
4557 | 0 | return wka_hashtable; |
4558 | 0 | } |
4559 | | |
4560 | | wmem_map_t * |
4561 | | get_eth_hashtable(void) |
4562 | 0 | { |
4563 | 0 | return eth_hashtable; |
4564 | 0 | } |
4565 | | |
4566 | | wmem_map_t * |
4567 | | get_serv_port_hashtable(void) |
4568 | 0 | { |
4569 | 0 | return serv_port_hashtable; |
4570 | 0 | } |
4571 | | |
4572 | | wmem_map_t * |
4573 | | get_ipxnet_hash_table(void) |
4574 | 0 | { |
4575 | 0 | return ipxnet_hash_table; |
4576 | 0 | } |
4577 | | |
4578 | | wmem_map_t * |
4579 | | get_vlan_hash_table(void) |
4580 | 0 | { |
4581 | 0 | return vlan_hash_table; |
4582 | 0 | } |
4583 | | |
4584 | | wmem_map_t * |
4585 | | get_ipv4_hash_table(void) |
4586 | 0 | { |
4587 | 0 | return ipv4_hash_table; |
4588 | 0 | } |
4589 | | |
4590 | | wmem_map_t * |
4591 | | get_ipv6_hash_table(void) |
4592 | 0 | { |
4593 | 0 | return ipv6_hash_table; |
4594 | 0 | } |
4595 | | /* Initialize all the address resolution subsystems in this file */ |
4596 | | void |
4597 | | addr_resolv_init(const char* app_env_var_prefix) |
4598 | 28 | { |
4599 | 28 | ws_assert(addr_resolv_scope == NULL); |
4600 | 28 | addr_resolv_scope = wmem_allocator_new(WMEM_ALLOCATOR_BLOCK); |
4601 | 28 | initialize_services(app_env_var_prefix); |
4602 | 28 | initialize_ethers(app_env_var_prefix); |
4603 | 28 | initialize_ipxnets(app_env_var_prefix); |
4604 | 28 | initialize_vlans(app_env_var_prefix); |
4605 | 28 | initialize_enterprises(app_env_var_prefix); |
4606 | 28 | host_name_lookup_init(app_env_var_prefix); |
4607 | 28 | initialize_tacs(app_env_var_prefix); |
4608 | 28 | } |
4609 | | |
4610 | | /* Clean up all the address resolution subsystems in this file */ |
4611 | | void |
4612 | | addr_resolv_cleanup(void) |
4613 | 14 | { |
4614 | 14 | vlan_name_lookup_cleanup(); |
4615 | 14 | service_name_lookup_cleanup(); |
4616 | 14 | ethers_cleanup(); |
4617 | 14 | ipx_name_lookup_cleanup(); |
4618 | 14 | enterprises_cleanup(); |
4619 | 14 | host_name_lookup_cleanup(); |
4620 | 14 | tac_name_lookup_cleanup(); |
4621 | | |
4622 | 14 | wmem_destroy_allocator(addr_resolv_scope); |
4623 | 14 | addr_resolv_scope = NULL; |
4624 | 14 | } |
4625 | | |
4626 | | bool |
4627 | | str_to_ip(const char *str, void *dst) |
4628 | 388 | { |
4629 | 388 | return ws_inet_pton4(str, (uint32_t *)dst); |
4630 | 388 | } |
4631 | | |
4632 | | bool |
4633 | | str_to_ip6(const char *str, void *dst) |
4634 | 17 | { |
4635 | 17 | return ws_inet_pton6(str, (ws_in6_addr *)dst); |
4636 | 17 | } |
4637 | | |
4638 | | /* |
4639 | | * convert a 0-terminated string that contains an ethernet address into |
4640 | | * the corresponding sequence of 6 bytes |
4641 | | * eth_bytes is a buffer >= 6 bytes that was allocated by the caller |
4642 | | */ |
4643 | | bool |
4644 | | str_to_eth(const char *str, uint8_t (*eth_bytes)[6]) |
4645 | 0 | { |
4646 | 0 | ether_t eth; |
4647 | 0 | unsigned mask; |
4648 | |
|
4649 | 0 | if (!parse_ether_address(str, ð, &mask, false)) |
4650 | 0 | return false; |
4651 | | |
4652 | 0 | if (mask == 48) { |
4653 | 0 | memcpy(eth_bytes, eth.addr, 6); |
4654 | 0 | } |
4655 | | return true; |
4656 | 0 | } |
4657 | | |
4658 | | /* |
4659 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
4660 | | * |
4661 | | * Local variables: |
4662 | | * c-basic-offset: 4 |
4663 | | * tab-width: 8 |
4664 | | * indent-tabs-mode: nil |
4665 | | * End: |
4666 | | * |
4667 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
4668 | | * :indentSize=4:tabSize=8:noTabs=true: |
4669 | | */ |