forked from osmocom/wireshark
Do IP address resolution synchronously before printing in TShark.
Otherwise, the first N packets printed, for a value of N dependent on various factors, won't get IP addresses resolved to names, even if the user wants them resolved. Unlike Wireshark, which, when a name is resolved in asynchronously, can go back and fix up the displayed packet when the asynchronous operation completes, once TShark has written packet data to the standard output, it can't go back and fix it if it did name resolution asynchronously. Bug: 14711 Change-Id: I8ebbd83103e5780c903b5560e01b7b92fa39c924 Reviewed-on: https://code.wireshark.org/review/27668 Reviewed-by: Guy Harris <guy@alum.mit.edu>
This commit is contained in:
parent
0f1f1d0ab8
commit
0542c5b700
|
@ -293,6 +293,7 @@ e_addr_resolve gbl_resolv_flags = {
|
|||
};
|
||||
#ifdef HAVE_C_ARES
|
||||
static guint name_resolve_concurrency = 500;
|
||||
static gboolean resolve_synchronously = FALSE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -318,7 +319,7 @@ gchar *g_penterprises_path = NULL; /* personal enterprises file */
|
|||
/* c-ares */
|
||||
#ifdef HAVE_C_ARES
|
||||
/*
|
||||
* Submitted queries trigger a callback (c_ares_ghba_cb()).
|
||||
* Submitted asynchronous queries trigger a callback (c_ares_ghba_cb()).
|
||||
* Queries are added to c_ares_queue_head. During processing, queries are
|
||||
* popped off the front of c_ares_queue_head and submitted using
|
||||
* ares_gethostbyaddr().
|
||||
|
@ -339,12 +340,171 @@ typedef struct _async_hostent {
|
|||
void *addrp;
|
||||
} async_hostent_t;
|
||||
|
||||
/*
|
||||
* Submitted synchronous queries trigger a callback (c_ares_ghba_sync_cb()).
|
||||
* The callback processes the response, sets completed to TRUE if
|
||||
* completed is non-NULL, then frees the request.
|
||||
*/
|
||||
typedef struct _sync_dns_data
|
||||
{
|
||||
union {
|
||||
guint32 ip4;
|
||||
ws_in6_addr ip6;
|
||||
} addr;
|
||||
int family;
|
||||
gboolean *completed;
|
||||
} sync_dns_data_t;
|
||||
|
||||
static ares_channel ghba_chan; /* ares_gethostbyaddr -- Usually non-interactive, no timeout */
|
||||
static ares_channel ghbn_chan; /* ares_gethostbyname -- Usually interactive, timeout */
|
||||
|
||||
static gboolean async_dns_initialized = FALSE;
|
||||
static guint async_dns_in_flight = 0;
|
||||
static wmem_list_t *async_dns_queue_head = NULL;
|
||||
|
||||
static void
|
||||
c_ares_ghba_sync_cb(void *arg, int status, int timeouts _U_, struct hostent *he) {
|
||||
sync_dns_data_t *sdd = (sync_dns_data_t *)arg;
|
||||
char **p;
|
||||
|
||||
if (status == ARES_SUCCESS) {
|
||||
for (p = he->h_addr_list; *p != NULL; p++) {
|
||||
switch(sdd->family) {
|
||||
case AF_INET:
|
||||
add_ipv4_name(sdd->addr.ip4, he->h_name);
|
||||
break;
|
||||
case AF_INET6:
|
||||
add_ipv6_name(&sdd->addr.ip6, he->h_name);
|
||||
break;
|
||||
default:
|
||||
/* Throw an exception? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Let our caller know that this is complete.
|
||||
*/
|
||||
*sdd->completed = TRUE;
|
||||
|
||||
/*
|
||||
* Free the structure for this call.
|
||||
*/
|
||||
g_free(sdd);
|
||||
}
|
||||
|
||||
static void
|
||||
wait_for_sync_resolv(gboolean *completed) {
|
||||
int nfds;
|
||||
fd_set rfds, wfds;
|
||||
struct timeval tv;
|
||||
|
||||
while (!*completed) {
|
||||
/*
|
||||
* Not yet resolved; wait for something to show up on the
|
||||
* address-to-name C-ARES channel.
|
||||
*
|
||||
* To quote the source code for ares_timeout() as of C-ARES
|
||||
* 1.12.0, "WARNING: Beware that this is linear in the number
|
||||
* of outstanding requests! You are probably far better off
|
||||
* just calling ares_process() once per second, rather than
|
||||
* calling ares_timeout() to figure out when to next call
|
||||
* ares_process().", although we should have only one request
|
||||
* outstanding.
|
||||
*
|
||||
* And, yes, we have to reset it each time, as select(), in
|
||||
* some OSes modifies the timeout to reflect the time remaining
|
||||
* (e.g., Linux) and select() in other OSes doesn't (most if not
|
||||
* all other UN*Xes, Windows?), so we can't rely on *either*
|
||||
* behavior.
|
||||
*/
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
nfds = ares_fds(ghba_chan, &rfds, &wfds);
|
||||
if (nfds > 0) {
|
||||
if (select(nfds, &rfds, &wfds, NULL, &tv) == -1) { /* call to select() failed */
|
||||
fprintf(stderr, "Warning: call to select() failed, error is %s\n", g_strerror(errno));
|
||||
return;
|
||||
}
|
||||
ares_process(ghba_chan, &rfds, &wfds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sync_lookup_ip4(const guint32 addr)
|
||||
{
|
||||
gboolean completed = FALSE;
|
||||
sync_dns_data_t *sdd;
|
||||
|
||||
if (!async_dns_initialized) {
|
||||
/*
|
||||
* c-ares not initialized. Bail out.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the request.
|
||||
*/
|
||||
sdd = g_new(sync_dns_data_t, 1);
|
||||
sdd->family = AF_INET;
|
||||
sdd->addr.ip4 = addr;
|
||||
sdd->completed = &completed;
|
||||
ares_gethostbyaddr(ghba_chan, &addr, sizeof(guint32), AF_INET,
|
||||
c_ares_ghba_sync_cb, sdd);
|
||||
|
||||
/*
|
||||
* Now wait for it to finish.
|
||||
*/
|
||||
wait_for_sync_resolv(&completed);
|
||||
}
|
||||
|
||||
static void
|
||||
sync_lookup_ip6(const ws_in6_addr *addr)
|
||||
{
|
||||
gboolean completed = FALSE;
|
||||
sync_dns_data_t *sdd;
|
||||
|
||||
if (!async_dns_initialized) {
|
||||
/*
|
||||
* c-ares not initialized. Bail out.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the request.
|
||||
*/
|
||||
sdd = g_new(sync_dns_data_t, 1);
|
||||
sdd->family = AF_INET6;
|
||||
memcpy(&sdd->addr.ip6, addr, sizeof(sdd->addr.ip6));
|
||||
sdd->completed = &completed;
|
||||
ares_gethostbyaddr(ghba_chan, &addr, sizeof(ws_in6_addr), AF_INET6,
|
||||
c_ares_ghba_sync_cb, sdd);
|
||||
|
||||
/*
|
||||
* Now wait for it to finish.
|
||||
*/
|
||||
wait_for_sync_resolv(&completed);
|
||||
}
|
||||
|
||||
void
|
||||
set_resolution_synchrony(gboolean synchronous)
|
||||
{
|
||||
resolve_synchronously = synchronous;
|
||||
}
|
||||
#else
|
||||
void
|
||||
set_resolution_synchrony(gboolean synchronous _U_)
|
||||
{
|
||||
/* Nothing to set. */
|
||||
}
|
||||
#endif /* HAVE_C_ARES */
|
||||
|
||||
typedef struct {
|
||||
|
@ -849,9 +1009,6 @@ static hashipv4_t *
|
|||
host_lookup(const guint addr)
|
||||
{
|
||||
hashipv4_t * volatile tp;
|
||||
#ifdef HAVE_C_ARES
|
||||
async_dns_queue_msg_t *caqm;
|
||||
#endif
|
||||
|
||||
tp = (hashipv4_t *)wmem_map_lookup(ipv4_hash_table, GUINT_TO_POINTER(addr));
|
||||
if (tp == NULL) {
|
||||
|
@ -878,11 +1035,28 @@ host_lookup(const guint addr)
|
|||
tp->flags |= TRIED_RESOLVE_ADDRESS;
|
||||
|
||||
#ifdef HAVE_C_ARES
|
||||
if (async_dns_initialized && name_resolve_concurrency > 0) {
|
||||
caqm = wmem_new(wmem_epan_scope(), async_dns_queue_msg_t);
|
||||
caqm->family = AF_INET;
|
||||
caqm->addr.ip4 = addr;
|
||||
wmem_list_append(async_dns_queue_head, (gpointer) caqm);
|
||||
if (async_dns_initialized) {
|
||||
/* c-ares is initialized, so we can use it */
|
||||
if (resolve_synchronously || name_resolve_concurrency == 0) {
|
||||
/*
|
||||
* Either all names are to be resolved synchronously or
|
||||
* the concurrencly level is 0; do the resolution
|
||||
* synchronously.
|
||||
*/
|
||||
sync_lookup_ip4(addr);
|
||||
} else {
|
||||
/*
|
||||
* Names are to be resolved asynchronously, and we
|
||||
* allow at least one asynchronous request in flight;
|
||||
* post an asynchronous request.
|
||||
*/
|
||||
async_dns_queue_msg_t *caqm;
|
||||
|
||||
caqm = wmem_new(wmem_epan_scope(), async_dns_queue_msg_t);
|
||||
caqm->family = AF_INET;
|
||||
caqm->addr.ip4 = addr;
|
||||
wmem_list_append(async_dns_queue_head, (gpointer) caqm);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -908,9 +1082,6 @@ static hashipv6_t *
|
|||
host_lookup6(const ws_in6_addr *addr)
|
||||
{
|
||||
hashipv6_t * volatile tp;
|
||||
#ifdef HAVE_C_ARES
|
||||
async_dns_queue_msg_t *caqm;
|
||||
#endif
|
||||
|
||||
tp = (hashipv6_t *)wmem_map_lookup(ipv6_hash_table, addr);
|
||||
if (tp == NULL) {
|
||||
|
@ -939,12 +1110,30 @@ host_lookup6(const ws_in6_addr *addr)
|
|||
|
||||
if (gbl_resolv_flags.use_external_net_name_resolver) {
|
||||
tp->flags |= TRIED_RESOLVE_ADDRESS;
|
||||
|
||||
#ifdef HAVE_C_ARES
|
||||
if (async_dns_initialized && name_resolve_concurrency > 0) {
|
||||
caqm = wmem_new(wmem_epan_scope(), async_dns_queue_msg_t);
|
||||
caqm->family = AF_INET6;
|
||||
memcpy(&caqm->addr.ip6, addr, sizeof(caqm->addr.ip6));
|
||||
wmem_list_append(async_dns_queue_head, (gpointer) caqm);
|
||||
if (async_dns_initialized) {
|
||||
/* c-ares is initialized, so we can use it */
|
||||
if (resolve_synchronously || name_resolve_concurrency == 0) {
|
||||
/*
|
||||
* Either all names are to be resolved synchronously or
|
||||
* the concurrencly level is 0; do the resolution
|
||||
* synchronously.
|
||||
*/
|
||||
sync_lookup_ip6(addr);
|
||||
} else {
|
||||
/*
|
||||
* Names are to be resolved asynchronously, and we
|
||||
* allow at least one asynchronous request in flight;
|
||||
* post an asynchronous request.
|
||||
*/
|
||||
async_dns_queue_msg_t *caqm;
|
||||
|
||||
caqm = wmem_new(wmem_epan_scope(), async_dns_queue_msg_t);
|
||||
caqm->family = AF_INET6;
|
||||
memcpy(&caqm->addr.ip6, addr, sizeof(caqm->addr.ip6));
|
||||
wmem_list_append(async_dns_queue_head, (gpointer) caqm);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -349,6 +349,14 @@ wmem_map_t *get_ipv4_hash_table(void);
|
|||
WS_DLL_PUBLIC
|
||||
wmem_map_t *get_ipv6_hash_table(void);
|
||||
|
||||
/*
|
||||
* XXX - if we ever have per-session host name etc. information, we
|
||||
* should probably have the "resolve synchronously or asynchronously"
|
||||
* flag be per-session, set with an epan API.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
void set_resolution_synchrony(gboolean synchronous);
|
||||
|
||||
/*
|
||||
* private functions (should only be called by epan directly)
|
||||
*/
|
||||
|
|
35
tshark.c
35
tshark.c
|
@ -2466,6 +2466,13 @@ capture(void)
|
|||
if (!ret)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
* Force synchronous resolution of IP addresses; we're doing only
|
||||
* one pass, so we can't do it in the background and fix up past
|
||||
* dissections.
|
||||
*/
|
||||
set_resolution_synchrony(TRUE);
|
||||
|
||||
/* the actual capture loop
|
||||
*
|
||||
* XXX - glib doesn't seem to provide any event based loop handling.
|
||||
|
@ -2905,11 +2912,6 @@ process_packet_first_pass(capture_file *cf, epan_dissect_t *edt,
|
|||
from the dissection or running taps on the packet; if we're doing
|
||||
any of that, we'll do it in the second pass.) */
|
||||
if (edt) {
|
||||
if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name ||
|
||||
gbl_resolv_flags.transport_name)
|
||||
/* Grab any resolved addresses */
|
||||
host_name_lookup_process();
|
||||
|
||||
/* If we're running a read filter, prime the epan_dissect_t with that
|
||||
filter. */
|
||||
if (cf->rfcode)
|
||||
|
@ -2986,11 +2988,6 @@ process_packet_second_pass(capture_file *cf, epan_dissect_t *edt,
|
|||
passes over the packets; that's the pass where we print
|
||||
packet information or run taps.) */
|
||||
if (edt) {
|
||||
if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name ||
|
||||
gbl_resolv_flags.transport_name)
|
||||
/* Grab any resolved addresses */
|
||||
host_name_lookup_process();
|
||||
|
||||
/* If we're running a display filter, prime the epan_dissect_t with that
|
||||
filter. */
|
||||
if (cf->dfcode)
|
||||
|
@ -3275,6 +3272,12 @@ process_cap_file(capture_file *cf, char *save_file, int out_file_type,
|
|||
edt = epan_dissect_new(cf->epan, create_proto_tree, print_packet_info && print_details);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force synchronous resolution of IP addresses; in this pass, we
|
||||
* can't do it in the background and fix up past dissections.
|
||||
*/
|
||||
set_resolution_synchrony(TRUE);
|
||||
|
||||
for (framenum = 1; err == 0 && framenum <= cf->count; framenum++) {
|
||||
fdata = frame_data_sequence_find(cf->provider.frames, framenum);
|
||||
if (wtap_seek_read(cf->provider.wth, fdata->file_off, &rec, &buf, &err,
|
||||
|
@ -3357,6 +3360,13 @@ process_cap_file(capture_file *cf, char *save_file, int out_file_type,
|
|||
edt = epan_dissect_new(cf->epan, create_proto_tree, print_packet_info && print_details);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force synchronous resolution of IP addresses; we're doing only
|
||||
* one pass, so we can't do it in the background and fix up past
|
||||
* dissections.
|
||||
*/
|
||||
set_resolution_synchrony(TRUE);
|
||||
|
||||
while (wtap_read(cf->provider.wth, &err, &err_info, &data_offset)) {
|
||||
framenum++;
|
||||
|
||||
|
@ -3498,11 +3508,6 @@ process_packet_single_pass(capture_file *cf, epan_dissect_t *edt, gint64 offset,
|
|||
over the packets, so, if we'll be printing packet information
|
||||
or running taps, we'll be doing it here.) */
|
||||
if (edt) {
|
||||
if (print_packet_info && (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name ||
|
||||
gbl_resolv_flags.transport_name))
|
||||
/* Grab any resolved addresses */
|
||||
host_name_lookup_process();
|
||||
|
||||
/* If we're running a filter, prime the epan_dissect_t with that
|
||||
filter. */
|
||||
if (cf->dfcode)
|
||||
|
|
Loading…
Reference in New Issue