lft-3.98/000755 000765 000024 00000000000 15174131662 012174 5ustar00vicstaff000000 000000 lft-3.98/lft_lib.c000644 000765 000024 00001122106 15173526114 013755 0ustar00vicstaff000000 000000 /* * lft_lib.c * Layer Four Traceroute * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2010 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * */ #define _USE_32BIT_TIME_T #include "lft_lib.h" #include "lft_btcptrace.h" #include "lft_icmptrace.h" #ifndef WIN32 #include #include #include #include #include #endif /*---------------------------------------------------------------------------*/ /* GraphViz output defines */ #define GVGRAPHNAME "lftpath" /* #define GVHOPSTYLE_BASE "shape = rect, penwidth=1" #define GVHOPSTYLE_SOURCE "shape = rect, style=filled, fillcolor=lightskyblue, color=skyblue, penwidth=2" #define GVHOPSTYLE_TARGET_OPEN "shape = rect, style=filled, fillcolor=palegreen, color=mediumspringgreen, penwidth=2" #define GVHOPSTYLE_TARGET_CLOSED "shape = rect, style=filled, fillcolor=tomato, color=orangered, penwidth=2" #define GVHOPSTYLE_TARGET_FILTERED "shape = rect, style=filled, fillcolor=grey, color=mediumspringgreen, penwidth=2" #define GVHOPSTYLE_HOLE "shape = note, style=filled, fillcolor=lightgrey, color=slategrey, penwidth=2" #define GVHOPSTYLE_ANOMALY1 "shape = rect, style=filled, fillcolor=salmon, skew=.4, color=orangered, penwidth=2" #define GVHOPSTYLE_ANOMALY2 "shape = rect, style=filled, fillcolor=lemonchiffon, skew=.4, color=orangered, penwidth=2" #define GVHOPSTYLE_ANOMALY3 "shape = rect, style=filled, fillcolor=lightgrey, color=slategrey, skew=.4, penwidth=2" */ #define GVHOPSTYLE_BASE "shape = none" #define GVHOPSTYLE_SOURCE "shape = none" #define GVHOPSTYLE_TARGET_OPEN "shape = none" #define GVHOPSTYLE_TARGET_CLOSED "shape = none" #define GVHOPSTYLE_TARGET_FILTERED "shape = none" #define GVHOPSTYLE_HOLE "shape = none" /* Firewall - The next gateway may statefully inspect packets */ #define GVHOPSTYLE_ANOMALY1 "shape = none" /* Firewall - The next gateway may implement a flag-based state filter */ #define GVHOPSTYLE_ANOMALY2 "shape = none" /* 4.2-3 BSD bug - The next gateway may errantly reply with reused TTLs */ #define GVHOPSTYLE_ANOMALY3 "shape = none" #define GV_ANOMALY1_TEXT "Stateful Firewall" #define GV_ANOMALY2_TEXT "Flag-based Firewall" #define GV_ANOMALY3_TEXT "BSD-stack TTL Reused" #define GVFONTSIZE "12" #define GVFONTNAME "Helvetica" static const char GVNTBEG[]="
"; static const char GVNTEND[]="
"; static const char GVNIMG_SOURCE[]="source.png"; static const char GVNIMG_TRGOPEN[]="dest.png"; static const char GVNIMG_TRGCLOSED[]="dest_closed.png"; static const char GVNIMG_TRGFILTERED[]="dest_prohibited.png"; static const char GVNIMG_ANOMALY1[]="firewall_smart.png"; static const char GVNIMG_ANOMALY2[]="firewall_stupid.png"; static const char GVNIMG_ANOMALY3[]="firewall_stupid.png"; static const char GVNIMG_REGULAR[]="router.png"; static const char GVNIMG_HOLE[]="router_cloaked.png"; static const char GVNIMG_SEAM[]="router_seam.png"; #if defined(WIN32) || defined(_WIN32) static const char DIRECTORY_SPLITTER='\\'; #else static const char DIRECTORY_SPLITTER='/'; #endif /*---------------------------------------------------------------------------*/ static const int start_dport = 33434; /* starting port for UDP tracing (traceroute compatibility for some targets that care */ const int maxpacklen = 16 * 1024; /* maximum user-supplied probe length */ /* Ethernet=1500, GigEther(jumbo)=9000 */ static const int minpacklen = 60; /* minimum user-supplied probe length */ const char * icmp_messages[] = { "endpoint", "net unreachable", "host unreachable", "protocol unreachable", "port unreachable", "need fragment", "source fail", "net unknown", "host unknown", "src isolated", "net prohib", "host prohib", "bad tos/net", "bad tos/hst", "prohibited", "precedence violation", "precedence cutoff" }; /* Returns a title-case ICMP name for -VV verbose output. * stored_icmp_type is trace_packet_info_s->icmp_type: * -2 = TTL exceeded (type 11 code 0) * -1 = target reached (RST/open — not an ICMP unreachable) * 0-15 = ICMP type-3 UNREACH sub-code */ static const char * lft_icmp_verbose_name(int t) { switch (t) { case -2: return "Time Exceeded"; case 0: return "Net Unreachable"; case 1: return "Host Unreachable"; case 2: return "Protocol Unreachable"; case 3: return "Port Unreachable"; case 4: return "Fragmentation Needed"; case 5: return "Source Route Failed"; case 6: return "Net Unknown"; case 7: return "Host Unknown"; case 8: return "Source Isolated"; case 9: return "Net Prohibited"; case 10: return "Host Prohibited"; case 11: return "Net Unreachable for TOS"; case 12: return "Host Unreachable for TOS"; case 13: return "Communication Prohibited"; case 14: return "Precedence Violation"; case 15: return "Precedence Cutoff"; default: return "Unknown"; } } /* Map raw on-wire ICMP type + code to a human name. * Used by EVT_RCVD_ICMP_ICMP/Echo where the raw struct is available. */ static const char * lft_icmp_name_raw(int raw_type, int raw_code) { if (raw_type == ICMP_TIMXCEED) return "Time Exceeded"; if (raw_type == ICMP_UNREACH) return lft_icmp_verbose_name(raw_code); if (raw_type == ICMP_ECHOREPLY) return "Echo Reply"; return "ICMP"; } static char time_format[] = "%d-%b-%y %H:%M:%S %Z"; /* time display format */ static char tbuf[128]; const char *appname = "LFT"; static const int hop_info_size = 256; /* can't be more than this */ static const unsigned int max_net_dev_input = 64; /* only take this much input */ static const int def_payload_len = 10; /* default payload length for UDP packets */ #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) static int sock = -1; #endif static int global_output_style=0; /* 0 - ordinary output, 1 - xml output, 2 - GraphViz output */ void GraphVizOutput(lft_session_params * sess); void ASCIIOutput(lft_session_params * sess); /* ========================================================================= * -o mode: live probe counter + SIGALRM spinner * * Display format (written to /dev/tty every 250 ms): * Tracing icmp://x.x.x.x ↓14 ↑3 ⬡6 ◎✗ ⠹ * * ↓ replies received (bold green) * ↑ in-flight probes (cyan→yellow→red heat scale; hidden when 0) * ⬡ hops discovered (magenta hex + bold-white number) * ◎✗ target not yet reached (dim red) → ◎✔ reached (bold green) * ⠹ braille spinner (dim; pulses bold-green on target reply or * when in-flight drops to 0 with target reached) * ========================================================================= */ #ifndef WIN32 /* Unicode multi-byte literals */ #define OS_DOWN "\xe2\x86\x93" /* ↓ U+2193 */ #define OS_UP "\xe2\x86\x91" /* ↑ U+2191 */ #define OS_HEX "\xe2\xac\xa1" /* ⬡ U+2B21 */ #define OS_BULL "\xe2\x97\x8e" /* ◎ U+25CE */ #define OS_CROSS "\xe2\x9c\x97" /* ✗ U+2717 */ #define OS_CHECK "\xe2\x9c\x94" /* ✔ U+2714 */ /* ANSI colour escapes — mirror --watch's ncurses palette so the -o * progress spinner (and any other temporarily-displayed status bar) * has the same look as the watch-mode footer. */ #define OS_BGREEN "\033[1;32m" #define OS_BCYAN "\033[1;36m" #define OS_BBLUE "\033[1;34m" #define OS_BYELLOW "\033[1;33m" #define OS_BRED "\033[1;31m" #define OS_BMAGENTA "\033[1;35m" #define OS_BWHITE "\033[1;37m" #define OS_DIM "\033[2m" #define OS_DIMRED "\033[2;31m" #define OS_SENT "\033[1;38;5;75m" /* bold medium-slate-blue (matches watch CP_SENT) */ #define OS_RST "\033[0m" #define OS_CLEOL "\033[K" static const char *os_braille[] = { "\xe2\xa0\x8b", "\xe2\xa0\x99", "\xe2\xa0\xb9", "\xe2\xa0\xb8", "\xe2\xa0\xbc", "\xe2\xa0\xb4", "\xe2\xa0\xa6", "\xe2\xa0\xa7", "\xe2\xa0\x87", "\xe2\xa0\x8f" }; #define OS_BRAILLE_N 10 static volatile sig_atomic_t g_o_active = 0; static volatile sig_atomic_t g_o_replies = 0; static volatile sig_atomic_t g_o_sent = 0; /* cumulative probes sent */ static volatile sig_atomic_t g_o_hops = 0; static volatile sig_atomic_t g_o_target = 0; static volatile sig_atomic_t g_o_timeout_flash = 0; static volatile sig_atomic_t g_o_target_flash = 0; static volatile sig_atomic_t g_o_spin_frame = 0; /* ---- inline progress spinner (default output style 0) ---- */ static volatile sig_atomic_t g_inline_spinning = 0; /* 1 = spinner char is on stdout */ static volatile sig_atomic_t g_inline_frame = 0; /* animation frame index */ static int g_inline_armed = 0; /* 1 = SIGALRM armed for inline spinner */ static int g_inline_utf8 = 0; /* 1 = braille (3-byte), 0 = ASCII |/-\ (1-byte) */ /* ---- cursor hide/show during inline spinner ---- */ static volatile sig_atomic_t g_cursor_hidden = 0; /* 1 = cursor hidden via \033[?25l */ static int g_cursor_sigs_set = 0; /* 1 = termination signal handlers installed */ static int g_cursor_atexit = 0; /* 1 = atexit handler already registered */ static struct sigaction g_old_sa_sigint; static struct sigaction g_old_sa_sigterm; static struct sigaction g_old_sa_sighup; static struct sigaction g_old_sa_sigquit; static const char g_ascii_spin[] = "|/-\\"; #define G_ASCII_SPIN_N 4 static int g_o_tty_fd = -1; static char g_o_prefix[160]; /* "Tracing " prefix string */ static char g_o_seen_hops[256]; /* non-zero once hopno has replied */ static struct sigaction g_o_sa_old; /* saved SIGALRM disposition for -o spinner */ /* Saved termination-signal dispositions for the -o spinner's Ctrl-C guard */ static struct sigaction g_o_old_sa_sigint; static struct sigaction g_o_old_sa_sigterm; static struct sigaction g_o_old_sa_sighup; static struct sigaction g_o_old_sa_sigquit; static volatile sig_atomic_t g_o_sigs_set = 0; /* 1 = handlers installed */ static int g_o_atexit = 0; /* 1 = atexit registered */ /* Format integer with comma thousands separator into buf[size]. * Returns chars written (not including NUL). No heap, no stdio — safe from * signal handlers (where we also use snprintf, a common practical compromise). */ static int fmt_comma(char *buf, int size, int val) { char tmp[24]; int n, i, j, next_comma; if (size <= 1) { if (size == 1) buf[0] = '\0'; return 0; } n = snprintf(tmp, sizeof(tmp), "%d", val); next_comma = n % 3; if (next_comma == 0) next_comma = 3; j = 0; for (i = 0; i < n; i++) { if (i == next_comma && j < size - 1) { buf[j++] = ','; next_comma += 3; } if (j < size - 1) buf[j++] = tmp[i]; } if (j < size) buf[j] = '\0'; return j; } static void o_spinner_sigalrm(int sig) { (void)sig; /* Inline progress spinner for default output style (style 0). * Overwrites the single spinner glyph in-place on stdout using * backspace + next frame. All I/O via write() (async-signal-safe). */ if (g_inline_spinning) { int frame = (int)(g_inline_frame++); char buf[8]; int n; if (g_inline_utf8) { const char *sp = os_braille[frame % OS_BRAILLE_N]; buf[0] = '\b'; buf[1] = '\b'; /* back over trailing space + glyph */ memcpy(buf + 2, sp, 3); buf[5] = ' '; /* re-emit trailing space */ n = 6; } else { buf[0] = '\b'; buf[1] = '\b'; /* back over trailing space + glyph */ buf[2] = g_ascii_spin[frame % G_ASCII_SPIN_N]; buf[3] = ' '; /* re-emit trailing space */ n = 4; } write(STDOUT_FILENO, buf, (size_t)n); return; } if (!g_o_active || g_o_tty_fd < 0) return; int frame = (int)(g_o_spin_frame++); int replies = (int)g_o_replies; int sent = (int)g_o_sent; int hops = (int)g_o_hops; int tgt = (int)g_o_target; int tfail = (int)g_o_timeout_flash; int treply = (int)g_o_target_flash; /* consume one-frame flash flags */ if (tfail) g_o_timeout_flash = 0; if (treply) g_o_target_flash = 0; /* format counts with comma thousands separators */ char c_replies[16], c_sent[16], c_hops[16]; fmt_comma(c_replies, sizeof(c_replies), replies); fmt_comma(c_sent, sizeof(c_sent), sent); fmt_comma(c_hops, sizeof(c_hops), hops); char buf[512]; int n = 0; /* Rewrite whole line from column 0 */ n += snprintf(buf + n, sizeof(buf) - n, "\r%s", g_o_prefix); /* ↓ replies (bold green) */ n += snprintf(buf + n, sizeof(buf) - n, OS_BGREEN OS_DOWN "%s" OS_RST, c_replies); /* ↑ total sent (medium slate blue — matches watch-mode CP_SENT) */ n += snprintf(buf + n, sizeof(buf) - n, " " OS_SENT OS_UP "%s" OS_RST, c_sent); /* ⬡ hops (bold cyan — glyph + space + number unified) */ n += snprintf(buf + n, sizeof(buf) - n, " " OS_BCYAN OS_HEX " %s" OS_RST, c_hops); /* target status: ✗ bold-red until reached, ✔ bold-green once reached * (matches --watch's sticky target mark in the status-bar counter row). */ if (tgt) n += snprintf(buf + n, sizeof(buf) - n, " " OS_BGREEN OS_CHECK OS_RST); else n += snprintf(buf + n, sizeof(buf) - n, " " OS_BRED OS_CROSS OS_RST); /* spinner: bright white always; brief red flash on timeout */ const char *spin = os_braille[frame % OS_BRAILLE_N]; const char *spin_color = tfail ? OS_BRED : OS_BWHITE; n += snprintf(buf + n, sizeof(buf) - n, " %s%s" OS_RST, spin_color, spin); /* clear remainder of line */ n += snprintf(buf + n, sizeof(buf) - n, OS_CLEOL); write(g_o_tty_fd, buf, (size_t)n); } /* Common init shared by lft_o_spinner_start and lft_o_spinner_start_prefix. */ static void o_spinner_arm_sigalrm(void) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = o_spinner_sigalrm; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGALRM, &sa, &g_o_sa_old); struct itimerval itv; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 250000; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 250000; setitimer(ITIMER_REAL, &itv, NULL); } /* Signal handler: Ctrl-C (and SIGTERM/HUP/QUIT) while the -o spinner is live. * Stops the spinner (erases the status line, restores the cursor), reinstates * the previous handlers, then re-raises so the process exits with the right * status (SIG_DFL termination, coredump for SIGQUIT, etc.). * All calls here are async-signal-safe: lft_o_spinner_stop uses only * write(), close(), setitimer(), sigaction(), and volatile flag stores. */ static void o_spinner_sig(int sig) { lft_o_spinner_stop(); /* erase line + restore cursor before we die */ if (g_o_sigs_set) { sigaction(SIGINT, &g_o_old_sa_sigint, NULL); sigaction(SIGTERM, &g_o_old_sa_sigterm, NULL); sigaction(SIGHUP, &g_o_old_sa_sighup, NULL); sigaction(SIGQUIT, &g_o_old_sa_sigquit, NULL); g_o_sigs_set = 0; } raise(sig); } /* atexit backstop: catches exit() paths not reached by the signal handler * (e.g. a fatal error in LFTExecute that calls exit() directly). */ static void o_spinner_atexit(void) { lft_o_spinner_stop(); } /* Start the spinner with a caller-supplied prefix string (e.g. watch mode * startup message). The prefix is copied into g_o_prefix as-is. */ void lft_o_spinner_start_prefix(const char *prefix) { if (!isatty(STDOUT_FILENO)) return; /* stdout redirected — no spinner */ snprintf(g_o_prefix, sizeof(g_o_prefix), "%s", prefix); /* reset all counters */ g_o_replies = g_o_sent = g_o_hops = 0; g_o_target = g_o_timeout_flash = g_o_target_flash = 0; g_o_spin_frame = 0; memset(g_o_seen_hops, 0, sizeof(g_o_seen_hops)); g_o_tty_fd = open("/dev/tty", O_WRONLY); if (g_o_tty_fd < 0) return; /* no tty — skip spinner silently */ /* Hide the block cursor while the spinner runs, and install handlers for * SIGINT/SIGTERM/SIGHUP/SIGQUIT so the cursor is restored before the * process terminates (Ctrl-C, kill, hangup, etc.). */ write(g_o_tty_fd, "\033[?25l", 6); if (!g_o_sigs_set) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = o_spinner_sig; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, &g_o_old_sa_sigint); sigaction(SIGTERM, &sa, &g_o_old_sa_sigterm); sigaction(SIGHUP, &sa, &g_o_old_sa_sighup); sigaction(SIGQUIT, &sa, &g_o_old_sa_sigquit); g_o_sigs_set = 1; } if (!g_o_atexit) { atexit(o_spinner_atexit); g_o_atexit = 1; } /* print initial placeholder; SIGALRM handler will overwrite from \r */ printf("%s" OS_BWHITE "-" OS_RST, g_o_prefix); fflush(stdout); o_spinner_arm_sigalrm(); g_o_active = 1; } void lft_o_spinner_start(lft_session_params *sess) { /* Use hostname (as typed by user); remote_address may be 0.0.0.0 before * LFTExecute runs DNS resolution */ const char *tgt = (sess->hostname && sess->hostname[0]) ? sess->hostname : inet_ntoa(sess->remote_address); char url[80]; switch (sess->protocol) { case 1: snprintf(url, sizeof(url), "udp://%s:%d", tgt, sess->dport); break; case 2: case 3: snprintf(url, sizeof(url), "icmp://%s", tgt); break; default: snprintf(url, sizeof(url), "tcp://%s:%d", tgt, sess->dport); break; } char prefix[200]; snprintf(prefix, sizeof(prefix), "Tracing %s ", url); lft_o_spinner_start_prefix(prefix); } void lft_o_spinner_stop(void) { if (!g_o_active) return; g_o_active = 0; struct itimerval zero; memset(&zero, 0, sizeof(zero)); setitimer(ITIMER_REAL, &zero, NULL); sigaction(SIGALRM, &g_o_sa_old, NULL); /* Restore termination signal handlers installed in lft_o_spinner_start_prefix */ if (g_o_sigs_set) { sigaction(SIGINT, &g_o_old_sa_sigint, NULL); sigaction(SIGTERM, &g_o_old_sa_sigterm, NULL); sigaction(SIGHUP, &g_o_old_sa_sighup, NULL); sigaction(SIGQUIT, &g_o_old_sa_sigquit, NULL); g_o_sigs_set = 0; } if (g_o_tty_fd >= 0) { /* erase the spinner line completely, restore cursor */ write(g_o_tty_fd, "\r" OS_CLEOL "\033[?25h", sizeof("\r" OS_CLEOL "\033[?25h") - 1); close(g_o_tty_fd); g_o_tty_fd = -1; } } /* Enable counter accumulation without arming SIGALRM. * Used by watch mode: lft_o_counters_activate() before LFTExecute, * watch_spinner_arm() for the /dev/tty spinner — two separate steps. */ void lft_o_counters_activate(void) { g_o_replies = g_o_sent = g_o_hops = 0; g_o_target = g_o_timeout_flash = g_o_target_flash = 0; g_o_spin_frame = 0; memset(g_o_seen_hops, 0, sizeof(g_o_seen_hops)); g_o_active = 1; } /* Disable counter accumulation (g_o_active → 0). Values are preserved * so callers can read them via lft_o_get_*() immediately after. */ void lft_o_counters_deactivate(void) { g_o_active = 0; } /* Getters — trivial reads of volatile sig_atomic_t globals. */ int lft_o_get_replies(void) { return (int)g_o_replies; } int lft_o_get_sent(void) { return (int)g_o_sent; } int lft_o_get_hops(void) { return (int)g_o_hops; } int lft_o_get_target(void) { return (int)g_o_target; } /* Read and clear the timeout-flash flag (one-shot consumption). */ int lft_o_consume_tfail(void) { int v = (int)g_o_timeout_flash; if (v) g_o_timeout_flash = 0; return v; } #endif /* !WIN32 */ /* Called from send paths in lft_icmptrace.c / lft_btcptrace.c — must be non-static so other translation units can link to it. No-op on Windows. */ void lft_o_probe_sent(void) { #ifndef WIN32 if (g_o_active) g_o_sent++; #endif } /*---------------------------------------------------------------------------*/ static void LFTDefaultErrorHandler(lft_session_params * sess, int code, const void * params) { const WrnBadHopStateParam * wbhsp; switch(code) { case WRN_CANT_SETUP_FIN: if(global_output_style==1) printf("%s",code, "TCP flags are selected automatically using (-E) option.\n\t\t\tIgnoring FINs-only (-F) option and using adaptive Engine.\n"); else fprintf (stderr, "LFT warning: TCP flags are selected automatically using (-E) option.\n\t\t\tIgnoring FINs-only (-F) option and using adaptive Engine.\n"); break; case WRN_CANT_DISP_HOST_NAMES: if(global_output_style==1) printf("%s",code, "I can't display hostnames (-h) unless I resolve them.\n\t\t\tIgnoring your request to display hostnames exclusively.\n"); else fprintf (stderr, "LFT warning: I can't display hostnames (-h) unless I resolve them.\n\t\t\tIgnoring your request to display hostnames exclusively.\n"); break; case WRN_ADAPTIVE_DISABLED_BY_UDP: if(global_output_style==1) printf("%s",code,"Disabling adaptive mode while using UDP."); else fprintf (stderr,"LFT warning: Disabling adaptive mode while using UDP.\n"); break; case WRN_FIN_DISABLED_BY_UDP: if(global_output_style==1) printf("%s",code,"Disabling FINs-only mode while using UDP."); else fprintf (stderr,"LFT warning: Disabling FINs-only mode while using UDP.\n"); break; case WRN_ONLY_ONE_ASN_LOOKUP: if(global_output_style==1) printf("%s",code,"Only one ASN lookup source may be used--you selected "); else fprintf (stderr,"LFT warning: Only one ASN lookup source may be used--you selected "); if(global_output_style!=1) { if(sess->use_cymru) fprintf (stderr,"Cymru.\n\t\t\tIgnoring your request to use "); if(sess->use_ris) fprintf (stderr,"RIPE NCC RIS.\n\t\t\tIgnoring your request to use "); if(sess->use_radb) fprintf (stderr,"RADB.\n\t\t\tIgnoring your request to use "); switch(*((const int *)params)) { case ASN_LOOKUP_RIS: fprintf (stderr,"RIPE NCC RIS.\n"); break; case ASN_LOOKUP_RADB: fprintf (stderr,"RADB.\n"); break; case ASN_LOOKUP_CYMRU: fprintf (stderr,"Cymru.\n"); break; } if(global_output_style==1) printf(""); } break; case WRN_UDP_PORT_TOO_HIGH: if(global_output_style==1) printf("Starting UDP port %d is too high. Will start with %d instead.", code, *((const int *)params), sess->dport); else fprintf (stderr, "LFT warning: Starting UDP port %d is too high. Will start with %d instead.\n", *((const int *)params), sess->dport); break; case WRN_PACKET_LENGTH_TOO_HIGH: if(global_output_style==1) printf("Packet length %d is too high. Will use %d instead.", code, *((const int *)params), maxpacklen); else fprintf (stderr, "LFT warning: Packet length %d is too high. Will use %d instead.\n", *((const int *)params), maxpacklen); break; case WRN_PACKET_LENGTH_TOO_LOW: if(global_output_style==1) printf("Packet length %d is too low. Will use %d instead.", code, *((const int *)params), minpacklen); else fprintf (stderr, "LFT warning: Packet length %d is too low. Will use %d instead.\n", *((const int *)params), minpacklen); break; case WRN_CANT_DISABLE_RESOLVER: if(global_output_style==1) printf("%s",code, "LFT warning: I can't display hostnames (-h) unless I resolve them.\n\t\t\tIgnoring your request not to resolve DNS.\n"); else fprintf (stderr, "LFT warning: I can't display hostnames (-h) unless I resolve them.\n\t\t\tIgnoring your request not to resolve DNS.\n"); break; case WRN_ALREADY_RANDOM_SPORT: if(global_output_style==1) printf("%s",code, "LFT warning: You already asked to use a random source port.\n\t\t\tIgnoring request to set specific source port.\n"); else fprintf (stderr, "LFT warning: You already asked to use a random source port.\n\t\t\tIgnoring request to set specific source port.\n"); break; case WRN_ADAPTIVE_DISABLED_BY_FIN: if(global_output_style==1) printf("%s",code, "LFT warning: TCP flags are selected automatically using (-E) option.\n\t\t\tIgnoring adaptive Engine (-E) option and using FINs-only (-F).\n"); else fprintf (stderr, "LFT warning: TCP flags are selected automatically using (-E) option.\n\t\t\tIgnoring adaptive Engine (-E) option and using FINs-only (-F).\n"); break; case ERR_DEVNAME_TOO_LONG: if(global_output_style==1) printf("Net interface names are limited to %d characters. (e.g., \"eth0\" or \"ppp0\" or \"10.10.10.10\")",code, max_net_dev_input); else fprintf (stderr, "Net interface names are limited to %d characters. (e.g., \"eth0\" or \"ppp0\" or \"10.10.10.10\")\n", max_net_dev_input); sess->exit_state=-1; break; case WRN_UNABLE_SETUP_UTC: if(global_output_style==1) printf("%s",code,"Unable to set TZ to UTC."); else fprintf(stderr, "LFT: Unable to set TZ to UTC.\n"); break; case ERR_UNKNOWN_HOST: if(global_output_style==1) printf("Unknown host: %s",code,(const char *)params); else fprintf (stderr, "LFT: Unknown host: %s\n", (const char *)params); sess->exit_state=-2; break; case WRN_GETIFFORREMOTE_SOCKET: if(global_output_style==1) #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) printf("socket: %s",code,strerror(errno)); #else printf("Socket trouble; unable to determine interface",code); #endif else #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) perror("socket"); #else perror("LFT: Socket trouble; unable to determine interface"); #endif break; case WRN_GETIFFORREMOTE_CONNECT: if(global_output_style==1) printf("UDP connect(); unable to determine interface: %s",code,strerror(errno)); else perror("LFT: UDP connect(); unable to determine interface"); break; case WRN_GETIFFORREMOTE_SOCKNAME: if(global_output_style==1) printf("getsockname: %s",code,strerror(errno)); else perror("getsockname"); break; case ERR_RAW_SOCKET: if(global_output_style==1) printf("raw socket: %s",code,strerror(errno)); else { perror ("LFT: raw socket"); #if defined(linux) || defined(__linux__) fprintf(stderr, "LFT: On Linux, grant capabilities with:\n" " setcap cap_net_raw,cap_net_admin=eip /usr/sbin/lft\n" " or install lft setuid root (chown root lft && chmod u+s lft)\n"); #else fprintf(stderr, "LFT: Install lft setuid root (chown root lft && chmod u+s lft)\n"); #endif } sess->exit_state=-3; break; case ERR_SOCKET_BIND: if(global_output_style==1) printf("bind: %s",code,strerror(errno)); else perror ("LFT: bind"); sess->exit_state=-4; break; case WRN_WSAIOCTL: if(global_output_style==1) printf("WSAIoctl: %s",code,strerror(errno)); else perror("LFT: WSAIoctl"); break; case ERR_IP_HDRINCL: if(global_output_style==1) printf("IP_HDRINCL: %s",code,strerror(errno)); else perror ("LFT: IP_HDRINCL"); sess->exit_state=-5; break; case ERR_NOT_ENOUGH_MEM: if(global_output_style==1) printf("malloc(): %s",code,strerror(errno)); else perror("malloc"); sess->exit_state=-6; break; case ERR_RAW_TCP_DISABLED: if(global_output_style==1) { printf("sendto: %s\n",code,strerror(errno)); #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) if (!sess->protocol) { printf("Your platform may prevent you from using raw TCP sockets.\n"); printf("Try UDP-based tracing instead using the \"-u\" option.\n"); } #else printf("Your platform may prevent you from using raw TCP sockets.\n"); printf("This could be the result of a local (host-based) firewall\n"); printf("or a permissions problem on this binary or the BPF device.\n"); if(sess->adaptive) printf("You can try TCP-based tracing without using adaptive mode.\n"); printf("You can try UDP-based tracing using the \"-u\" option.\n"); #endif printf("\n"); } else { #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) perror ("sendto"); if (!sess->protocol) { fprintf(stderr,"LFT: Your platform may prevent you from using raw TCP sockets.\n"); fprintf(stderr," Try UDP-based tracing instead using the \"-u\" option.\n"); } #else perror ("sendto"); fprintf(stderr,"LFT: Your platform may prevent you from using raw TCP sockets.\n"); fprintf(stderr," This could be the result of a local (host-based) firewall\n"); fprintf(stderr," or a permissions problem on this binary or the BPF device.\n"); if (sess->adaptive) fprintf(stderr," You can try TCP-based tracing without using adaptive mode.\n"); fprintf(stderr," You can try UDP-based tracing using the \"-u\" option.\n"); #endif } sess->exit_state=-7; break; case WRN_BAD_HOP_STATE: wbhsp=(const WrnBadHopStateParam *)params; if(global_output_style==1) printf("Bad state %#x for hop %d", code, wbhsp->h->state, wbhsp->nhop); else fprintf(stderr,"Bad state %#x for hop %d\n", wbhsp->h->state, wbhsp->nhop); break; case WRN_NS_LOOKUP_FAILED: if(global_output_style==1) { printf("", code); if(sess->use_radb) printf ("The RADB lookup failed."); else if(sess->use_cymru) printf ("The Cymru lookup failed."); else if(sess->use_ris) printf ("The RIPE NCC RIS lookup failed."); else printf ("The Prefix WhoIs lookup failed."); printf(""); } else { if(sess->use_radb) fprintf(stderr,"The RADB lookup failed.\n"); else if(sess->use_cymru) fprintf(stderr,"The Cymru lookup failed.\n"); else if(sess->use_ris) fprintf(stderr,"The RIPE NCC RIS lookup failed.\n"); else fprintf(stderr,"The Prefix WhoIs lookup failed.\n"); } break; case ERR_WIN_SELECT: if(global_output_style==1) printf("select: %s",code,strerror(errno)); else perror("select"); sess->exit_state=-8; break; case ERR_WIN_RECV: if(global_output_style==1) printf("read: %s",code,strerror(errno)); else perror("read"); sess->exit_state=-9; break; case ERR_WIN_WSASTARTUP: if(global_output_style==1) printf("WSAStartup: %s",code,strerror(errno)); else perror("WSAStartup"); sess->exit_state=-10; break; case ERR_PCAP_ERROR: if(global_output_style==1) printf("%s",code,(const char *)params); else fprintf (stderr, "LFT: %s\n", (const char *)params); sess->exit_state=-11; break; case ERR_DISCOVER_INTERFACE: if(global_output_style==1) printf("Failed to discover an appropriate interface",code); else fprintf (stderr, "LFT: Failed to discover an appropriate interface.\n"); sess->exit_state=-12; break; case ERR_UNKNOWN_INTERFACE: if(global_output_style==1) printf("Unable to locate a local interface with IP address %s",code,sess->userdev); else fprintf (stderr, "LFT: Unable to locate a local interface with IP address %s\n", sess->userdev); sess->exit_state=-13; break; case ERR_PCAP_DEV_UNAVAILABLE: if(global_output_style==1) printf("The network device \"%s\" isn\'t available to LFT. Try another or fix:\nERROR: %s",code,sess->pcap_dev,(const char *)params); else fprintf (stderr, "The network device \"%s\" isn\'t available to LFT. Try another or fix:\nERROR: %s\n", sess->pcap_dev, (const char *)params); sess->exit_state=-14; break; case WRN_BIOCIMMEDIATE: if(global_output_style==1) printf("BIOCIMMEDIATE: %s", code, (const char *)params); else fprintf(stderr, "BIOCIMMEDIATE: %s\n",(const char *)params); sess->exit_state=-34; break; case WRN_OCHECK_OPEN_SOCK: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error opening socket.\n"); if(global_output_style==1) printf("Error opening socket", code); break; case WRN_OCHECK_IOCTL: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error setting nonblocking mode (IOCTL).\n"); if(global_output_style==1) printf("Error setting nonblocking mode (IOCTL)", code); break; case WRN_OCHECK_SELECT: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error on socket select call.\n"); if(global_output_style==1) printf("Error on socket select call", code); break; case WRN_OCHECK_GETERROR: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error trying to read socket error.\n"); if(global_output_style==1) printf("Error trying to read socket error", code); break; case WRN_OCHECK_SOCKERROR: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error on socket.\n"); if(global_output_style==1) printf("Error on socket", code); break; case WRN_OCHECK_TIMEOUT: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Timeout on socket.\n"); if(global_output_style==1) printf("Timeout on socket", code); break; case WRN_OCHECK_FCNTLGET: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error setting nonblocking mode (FCNTL GET).\n"); if(global_output_style==1) printf("Error setting nonblocking mode (FCNTL GET)", code); break; case WRN_OCHECK_FCNTLSET: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error setting nonblocking mode (FCNTL SET).\n"); if(global_output_style==1) printf("Error setting nonblocking mode (FCNTL SET)", code); break; case WRN_OCHECK_CONNECTERR: if(global_output_style!=1 && sess->noisy>1) fprintf(stderr, "LFT: Error trying socket connect.\n"); if(global_output_style==1) printf("Error trying socket connect", code); break; case ERR_PCAP_NONBLOCK_ERROR: if(global_output_style==1) printf("%s",code,(const char *)params); else fprintf (stderr, "LFT: Failed to set nonblocking mode.\n %s\n", (const char *)params); sess->exit_state=-35; break; case ERR_PCAP_ACTIVATE_ERROR: if(global_output_style==1) printf("Failed to activate capture on device \"%s\": %s",code,sess->pcap_dev,(const char *)params); else fprintf(stderr, "LFT: Failed to activate capture on device \"%s\". Try another or fix:\n %s\n",sess->pcap_dev,(const char *)params); sess->exit_state=-36; break; case WRN_PCAP_ACTIVATE: if(global_output_style==1) printf("Warning activating capture on device \"%s\": %s",code,sess->pcap_dev,(const char *)params); else fprintf(stderr, "LFT: Warning activating capture on device \"%s\": %s\n",sess->pcap_dev,(const char *)params); /* non-fatal: do not set exit_state */ break; } } /*---------------------------------------------------------------------------*/ /* c-ares async DNS helpers * * Design summary: * - lft_ares_fire() is called whenever a new hop IP is first seen; it adds * an entry to the session DNS cache (addr + empty name) * and kicks off an ares_gethostbyaddr() PTR query. * - lft_ares_callback()fires when c-ares completes a query; it fills the name * field in the cache entry and decrements ares_pending. * - lft_ares_poll() is called on every main-loop iteration (non-blocking * select + ares_process) to harvest completed replies. * - lft_ares_wait() is called once after the trace loop exits; it blocks * up to 1.2 s for any still-pending queries (queries * fired for the last few hops may not have finished yet). * - print_host() checks the cache first; if a name is present it uses * it, otherwise falls back to the numeric IP address. * The synchronous getnameinfo()+alarm() path is still * compiled in as a fallback for -Q / no-c-ares builds. * * Timeout policy: TIMEOUTMS=500ms, TRIES=2 (one retry for dropped UDP). * Total worst-case per query: 1 000 ms — same ceiling as the alarm(1) path, * but since queries run concurrently with probing the cost is largely free. */ #ifdef HAVE_CARES typedef struct { lft_session_params *sess; struct in_addr addr; } lft_ares_ud_t; static void lft_ares_callback(void *arg, int status, int timeouts __attribute__((unused)), struct hostent *host) { lft_ares_ud_t *ud = (lft_ares_ud_t *)arg; lft_session_params *sess = ud->sess; int i; if (status == ARES_SUCCESS && host && host->h_name) { for (i = 0; i < sess->dns_cache_count; i++) { if (sess->dns_cache[i].addr.s_addr == ud->addr.s_addr) { strncpy(sess->dns_cache[i].name, host->h_name, sizeof(sess->dns_cache[i].name) - 1); sess->dns_cache[i].name[sizeof(sess->dns_cache[i].name)-1] = '\0'; break; } } } sess->ares_pending--; free(ud); } /* Fire an async PTR query for addr if not already queued. No-op if full. */ static void lft_ares_fire(lft_session_params *sess, struct in_addr addr) { lft_ares_ud_t *ud; int i; if (!sess->ares_chan || sess->async_dns_disabled || !sess->resolve_names) return; for (i = 0; i < sess->dns_cache_count; i++) if (sess->dns_cache[i].addr.s_addr == addr.s_addr) return; /* already queued or resolved */ if (sess->dns_cache_count >= LFT_DNS_CACHE_SIZE) return; /* cache full — shouldn't happen within ttl_limit */ sess->dns_cache[sess->dns_cache_count].addr = addr; sess->dns_cache[sess->dns_cache_count].name[0] = '\0'; sess->dns_cache_count++; ud = malloc(sizeof(lft_ares_ud_t)); if (!ud) return; ud->sess = sess; ud->addr = addr; sess->ares_pending++; ares_gethostbyaddr(sess->ares_chan, &addr, sizeof(addr), AF_INET, lft_ares_callback, ud); } /* Public wrapper so other translation units (lft_icmptrace.c, lft_btcptrace.c) * can queue PTR lookups without needing access to the static lft_ares_fire(). */ void lft_ares_queue(lft_session_params *sess, struct in_addr addr) { lft_ares_fire(sess, addr); } /* Socket-state callback: c-ares calls this whenever it opens, changes interest * on, or closes a socket. We maintain ares_fd_table[] so lft_ares_poll() and * lft_ares_wait() know exactly which fds to select() on without calling the * deprecated ares_fds(). */ static void lft_ares_sock_state_cb(void *data, ares_socket_t fd, int readable, int writable) { lft_session_params *sess = (lft_session_params *)data; int i; /* Update an existing entry */ for (i = 0; i < sess->ares_nfds; i++) { if (sess->ares_fd_table[i].fd == fd) { if (!readable && !writable) { /* c-ares no longer needs this fd — remove by swap with last */ sess->ares_fd_table[i] = sess->ares_fd_table[--sess->ares_nfds]; } else { sess->ares_fd_table[i].events = (readable ? (unsigned)ARES_FD_EVENT_READ : 0u) | (writable ? (unsigned)ARES_FD_EVENT_WRITE : 0u); } return; } } /* New fd */ if (!readable && !writable) return; /* shouldn't happen; ignore */ if (sess->ares_nfds >= LFT_ARES_MAX_FDS) return; /* table full */ sess->ares_fd_table[sess->ares_nfds].fd = fd; sess->ares_fd_table[sess->ares_nfds].events = (readable ? (unsigned)ARES_FD_EVENT_READ : 0u) | (writable ? (unsigned)ARES_FD_EVENT_WRITE : 0u); sess->ares_nfds++; } /* Non-blocking poll: harvest any c-ares replies that have arrived. * Uses ares_process_fds() with the fd table maintained by the sock_state_cb. */ void lft_ares_poll(lft_session_params *sess) { fd_set rfd, wfd; struct timeval tv = {0, 0}; ares_fd_events_t events[LFT_ARES_MAX_FDS]; size_t nevents = 0; int i, nfds = 0; if (!sess->ares_chan || sess->ares_pending == 0 || sess->ares_nfds == 0) return; FD_ZERO(&rfd); FD_ZERO(&wfd); for (i = 0; i < sess->ares_nfds; i++) { if (sess->ares_fd_table[i].events & ARES_FD_EVENT_READ) FD_SET(sess->ares_fd_table[i].fd, &rfd); if (sess->ares_fd_table[i].events & ARES_FD_EVENT_WRITE) FD_SET(sess->ares_fd_table[i].fd, &wfd); if ((int)sess->ares_fd_table[i].fd + 1 > nfds) nfds = (int)sess->ares_fd_table[i].fd + 1; } if (nfds == 0) return; select(nfds, &rfd, &wfd, NULL, &tv); /* non-blocking: tv = {0, 0} */ for (i = 0; i < sess->ares_nfds; i++) { unsigned int ev = ARES_FD_EVENT_NONE; if (FD_ISSET(sess->ares_fd_table[i].fd, &rfd)) ev |= ARES_FD_EVENT_READ; if (FD_ISSET(sess->ares_fd_table[i].fd, &wfd)) ev |= ARES_FD_EVENT_WRITE; if (ev != ARES_FD_EVENT_NONE) { events[nevents].fd = sess->ares_fd_table[i].fd; events[nevents].events = ev; nevents++; } } ares_process_fds(sess->ares_chan, events, nevents, 0); } /* Post-trace blocking wait: drain remaining queries up to a hard deadline. * Worst-case wait = TIMEOUTMS * TRIES = 500 * 2 = 1 000 ms; the 1.2 s * ceiling gives 200 ms of headroom for scheduler jitter. * Time spent here is stored in sess->ares_wait_ms for -T display. */ void lft_ares_wait(lft_session_params *sess) { struct timeval deadline, now, remaining, ares_tv, *atvp, wait_start; ares_fd_events_t events[LFT_ARES_MAX_FDS]; int i, nfds; sess->ares_wait_ms = 0.0; if (!sess->ares_chan || sess->ares_pending == 0) return; gettimeofday(&wait_start, NULL); gettimeofday(&deadline, NULL); deadline.tv_sec += 1; deadline.tv_usec += 200000; /* 1.2 s total ceiling */ if (deadline.tv_usec >= 1000000) { deadline.tv_sec++; deadline.tv_usec -= 1000000; } while (sess->ares_pending > 0) { fd_set rfd, wfd; size_t nevents = 0; gettimeofday(&now, NULL); timersub(&deadline, &now, &remaining); if (remaining.tv_sec < 0) break; /* deadline passed — display remaining hops numerically */ if (sess->ares_nfds == 0) { /* No fds yet registered; drive timeout processing only */ ares_process_fds(sess->ares_chan, NULL, 0, 0); break; } FD_ZERO(&rfd); FD_ZERO(&wfd); nfds = 0; for (i = 0; i < sess->ares_nfds; i++) { if (sess->ares_fd_table[i].events & ARES_FD_EVENT_READ) FD_SET(sess->ares_fd_table[i].fd, &rfd); if (sess->ares_fd_table[i].events & ARES_FD_EVENT_WRITE) FD_SET(sess->ares_fd_table[i].fd, &wfd); if ((int)sess->ares_fd_table[i].fd + 1 > nfds) nfds = (int)sess->ares_fd_table[i].fd + 1; } if (nfds == 0) break; atvp = ares_timeout(sess->ares_chan, &remaining, &ares_tv); select(nfds, &rfd, &wfd, NULL, atvp); for (i = 0; i < sess->ares_nfds; i++) { unsigned int ev = ARES_FD_EVENT_NONE; if (FD_ISSET(sess->ares_fd_table[i].fd, &rfd)) ev |= ARES_FD_EVENT_READ; if (FD_ISSET(sess->ares_fd_table[i].fd, &wfd)) ev |= ARES_FD_EVENT_WRITE; if (ev != ARES_FD_EVENT_NONE) { events[nevents].fd = sess->ares_fd_table[i].fd; events[nevents].events = ev; nevents++; } } ares_process_fds(sess->ares_chan, events, nevents, 0); } gettimeofday(&now, NULL); sess->ares_wait_ms = timediff_ms(wait_start, now); } #endif /* HAVE_CARES */ /*---------------------------------------------------------------------------*/ /* Aggressive reverse-DNS timeout for the trace hot path. * _res.retrans=1 / _res.retry=1 covers Linux/glibc where getnameinfo() * uses the same resolver machinery. On macOS, getnameinfo() may route * through mDNSResponder which ignores _res, so alarm(1) provides a hard * 1-second ceiling regardless of platform. LFT is single-threaded so * SIGALRM is safe here; there are no other SIGALRM users in the process. */ #ifndef WIN32 static volatile sig_atomic_t lft_revdns_timed_out; static void lft_sigalrm(int sig __attribute__((unused))) { lft_revdns_timed_out = 1; } #define LFT_REVDNS_TIMEOUT 1 #else /* Windows: thread-based 1-second timeout for getnameinfo() on the * synchronous fallback path (used when c-ares is not compiled in or -Q). */ struct lft_gni_args { struct sockaddr *sa; socklen_t salen; char *hbuf; size_t hbufsz; int flags; int rc; }; static DWORD WINAPI lft_gni_thread(LPVOID arg) { struct lft_gni_args *a = (struct lft_gni_args *)arg; a->rc = getnameinfo(a->sa, a->salen, a->hbuf, (DWORD)a->hbufsz, NULL, 0, a->flags); return 0; } static int lft_getnameinfo_timed(struct sockaddr *sa, socklen_t salen, char *hbuf, size_t hbufsz, int flags) { struct lft_gni_args a; HANDLE th; DWORD tid; a.sa = sa; a.salen = salen; a.hbuf = hbuf; a.hbufsz = hbufsz; a.flags = flags; a.rc = EAI_AGAIN; th = CreateThread(NULL, 0, lft_gni_thread, &a, 0, &tid); if (!th) return EAI_AGAIN; if (WaitForSingleObject(th, 1000) == WAIT_TIMEOUT) TerminateThread(th, 0); CloseHandle(th); return a.rc; } #endif /* WIN32 */ static void print_host (lft_session_params * sess, struct in_addr addr) { char hbuf[NI_MAXHOST]; struct sockaddr_in sa; int rc; #ifdef HAVE_CARES /* When c-ares is active, DNS results are pre-populated in the session * cache during the trace; no blocking call is needed at display time. */ if (sess->ares_chan && !sess->async_dns_disabled) { const char *name = NULL; int i; for (i = 0; i < sess->dns_cache_count; i++) { if (sess->dns_cache[i].addr.s_addr == addr.s_addr) { if (sess->dns_cache[i].name[0]) name = sess->dns_cache[i].name; break; } } if (name) { if (global_output_style == 1) printf (" host=\"%s\"", name); else printf ("%s", name); if (!sess->hostnames_only) { if (global_output_style == 1) printf (" ip=\"%s\"", inet_ntoa (addr)); else printf (" (%s)", inet_ntoa (addr)); } } else { if (global_output_style == 1) printf (" ip=\"%s\"", inet_ntoa (addr)); else printf ("%s", inet_ntoa (addr)); } return; } #endif /* HAVE_CARES */ /* Synchronous fallback: used when c-ares is not compiled in or -Q is set. */ if (!sess->resolve_names) { if (global_output_style == 1) printf (" ip=\"%s\"", inet_ntoa (addr)); else printf ("%s", inet_ntoa (addr)); return; } memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr = addr; #ifndef WIN32 lft_revdns_timed_out = 0; signal(SIGALRM, lft_sigalrm); alarm(LFT_REVDNS_TIMEOUT); rc = getnameinfo((struct sockaddr *)&sa, sizeof(sa), hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD); alarm(0); signal(SIGALRM, SIG_DFL); if (lft_revdns_timed_out) rc = EAI_AGAIN; #else rc = lft_getnameinfo_timed((struct sockaddr *)&sa, sizeof(sa), hbuf, sizeof(hbuf), NI_NAMEREQD); #endif if (rc == 0) { if (global_output_style == 1) printf (" host=\"%s\"", hbuf); else printf ("%s", hbuf); if (!sess->hostnames_only) { if (global_output_style == 1) printf (" ip=\"%s\"", inet_ntoa (addr)); else printf (" (%s)", inet_ntoa (addr)); } } else { if (global_output_style == 1) printf (" ip=\"%s\"", inet_ntoa (addr)); else printf ("%s", inet_ntoa (addr)); } } /*---------------------------------------------------------------------------*/ double timediff_ms (struct timeval prior, struct timeval latter) { return (latter.tv_usec - prior.tv_usec) / 1000. + (latter.tv_sec - prior.tv_sec) * 1000.; } /* Per-hop RTT statistics (used by text/XML output and ASCII chart) */ typedef struct { double rtt_min; double rtt_avg; double rtt_max; double rtt_stddev; int n; /* number of RTT samples */ } hop_rtt_stats_t; static hop_rtt_stats_t compute_hop_rtt_stats(lft_session_params *sess, int hopno) { hop_rtt_stats_t s; struct trace_packet_info_s *tp; double sum = 0.0, sum2 = 0.0; int n = 0; double rtt_min = 0.0, rtt_max = 0.0; memset(&s, 0, sizeof(s)); if (hopno < 0 || hopno >= sess->hop_info_length) return s; SLIST_FOREACH(tp, &sess->hop_info[hopno].packets, next_by_hop) { if (!tp->recv.tv_sec) continue; { double rtt = timediff_ms(tp->sent, tp->recv); if (n == 0 || rtt < rtt_min) rtt_min = rtt; if (n == 0 || rtt > rtt_max) rtt_max = rtt; sum += rtt; sum2 += rtt * rtt; n++; } } if (n == 0) return s; s.n = n; s.rtt_min = rtt_min; s.rtt_max = rtt_max; s.rtt_avg = sum / n; s.rtt_stddev = (n > 1) ? sqrt(sum2/n - (sum/n)*(sum/n)) : 0.0; if (s.rtt_stddev < 0.0) s.rtt_stddev = 0.0; return s; } /*---------------------------------------------------------------------------*/ static void EvtPacketInfoDefaultHandler(lft_session_params * sess, const EvtPacketInfoParam * ehip) { char ind=' '; /* rtt_stats: skip duplicate-hop packets (only first probe's host info is emitted; * RTT suppressed here — stats are printed at EVT_RPT_PACKET_LIST_END) */ if (sess->rtt_stats && ehip->last_hop.s_addr == ehip->tp->hopaddr.s_addr) return; if(ehip->tp->recv.tv_sec) { if (ehip->last_hop.s_addr != ehip->tp->hopaddr.s_addr) { /* Fix #1: check whether this IP was already annotated at an earlier hop. * Suppresses repeated ASN/netname for routers that respond at multiple * TTL levels (CGNAT gateways, -i probe-through-firewall mode, etc.). */ int _hop_seen = 0; { int _j; for (_j = 0; _j < sess->n_shown_asn_ips; _j++) { if (sess->shown_asn_ips[_j].s_addr == ehip->tp->hopaddr.s_addr) { _hop_seen = 1; break; } } } if (!_hop_seen) { if (sess->do_aslookup) { if(global_output_style==1) printf(" asn=\"%d\"", ehip->asnumber); else { /* Check IANA special-purpose table first — catches RFC-reserved * addresses (RFC1918, RFC6598 CGNAT, etc.) that may have a wrong * ASN carried over from a prior hop's ipaslist slot. */ char _irfc[16], _iname[64]; if (lft_iana_v4_lookup(ehip->tp->hopaddr, _irfc, sizeof(_irfc), _iname, sizeof(_iname))) printf(" [%s]", _irfc); else if (ehip->asnumber) printf(" [%d]", ehip->asnumber); else printf(" [AS?]"); } } if (sess->do_netlookup) { if(global_output_style==1) printf(" net=\"%s\"", ehip->netname); else { /* Check IANA first (same reason as ASN block above) */ char _irfc[16], _iname[64]; if (lft_iana_v4_lookup(ehip->tp->hopaddr, _irfc, sizeof(_irfc), _iname, sizeof(_iname))) printf(" [%s]", _iname); else if(ehip->netname && strlen(ehip->netname)>0 && strcmp(ehip->netname,"NULL")!=0) printf(" [%s]", ehip->netname); else printf(" [NET?]"); } } /* Register this IP as annotated for subsequent hops */ if (sess->n_shown_asn_ips < 256) sess->shown_asn_ips[sess->n_shown_asn_ips++] = ehip->tp->hopaddr; } /* !_hop_seen */ /* Fix #2: ICMP message label. * For a router already seen at a prior hop, ICMP type-3 codes * (port/host/net unreachable) mean the router is filtering the probe * rather than being the destination — label it [filtered] for clarity. * For a new router, show the specific ICMP code as before. */ if (ehip->tp->icmp_type < -2 || ehip->tp->icmp_type > 17) { if(global_output_style==1) printf(" icmpcode=\"%d\"", ehip->tp->icmp_type); else printf (" [icmp code %d]", ehip->tp->icmp_type); } else if (ehip->tp->icmp_type >= 0) { const char *_icmplabel = (_hop_seen) ? "filtered" : icmp_messages[ehip->tp->icmp_type + 1]; if(global_output_style==1) printf (" icmpmsg=\"%s\"", _icmplabel); else printf (" [%s]", _icmplabel); } if (ehip->tp->icmp_type == -1) { if(global_output_style==1) printf(" trgstate=\""); else printf(" [target"); if(sess->protocol==0 || sess->protocol==4) { if(!(global_output_style==1)) printf(" "); if (sess->target_open > 0) printf("open"); else if (sess->pmtud) /* RST to an oversized SYN does not confirm the port is * closed — the server may be rejecting the malformed probe * rather than the connection. Report what was observed. */ printf("RST"); else { if(sess->target_filtered > 0) printf("filtered"); else printf("closed"); } } else if (sess->protocol == 2 || sess->protocol == 3) { /* ICMP mode: target sent Echo Reply — no TCP port state */ if(global_output_style != 1) printf(" replied"); } if(global_output_style==1) printf("\""); else printf("]"); } if(ehip->is_asseam) { if(global_output_style==1) printf(" asseam=\"1"); else printf(" (AS-Method Seam"); } if(ehip->is_netseam) { if(global_output_style==1) printf(" netseam=\"1"); else { if(ehip->is_asseam) printf(", Network-Method Seam"); else printf(" (Network-Method Seam"); } } if(ehip->is_asseam || ehip->is_netseam) { if(ehip->seam_traced) { if(global_output_style==1) printf("\" seamstate=\""); else printf(": "); if(ehip->is_open) printf("OPEN"); else { if(ehip->is_filtered) printf("FILTERED"); else printf("CLOSED"); } } if(global_output_style==1) printf("\""); else printf(")"); } if(global_output_style!=1) printf(" "); print_host (sess, ehip->tp->hopaddr); if (ehip->tp->icmp_type == -1 && (sess->protocol<2 || sess->protocol>3)) { if(global_output_style==1) printf(" port=\"%d\"",sess->dport); else printf(":%d",sess->dport); } /* PMTUD annotation: only show when a PTB actually forced a step-down * below the starting probe size. When path_mtu == pmtud_initial_mtu * no bottleneck was found — showing [MTU: 1500] would imply a * constraint was discovered when it wasn't. */ if (sess->pmtud) { if (ehip->path_mtu > 0 && ehip->path_mtu < sess->pmtud_initial_mtu) { if(global_output_style==1) printf(" pathmtu=\"%u\"", (unsigned)ehip->path_mtu); else printf(" [MTU: %u]", (unsigned)ehip->path_mtu); } } } else { if(global_output_style==1) ind=';'; else ind='/'; } /* Individual RTT — suppressed when rtt_stats active (stats at EVT_RPT_PACKET_LIST_END) */ if (!sess->rtt_stats) { if(global_output_style==1) { if(ind==';') printf (";%.1f", timediff_ms(ehip->tp->sent, ehip->tp->recv)); else printf (" timems=\"%.1f", timediff_ms(ehip->tp->sent, ehip->tp->recv)); } else printf ("%c%.1f", ind, timediff_ms(ehip->tp->sent, ehip->tp->recv)); } } } /* ---- inline spinner helpers (style-0 default output, non-Windows) -------- */ #ifndef WIN32 /* ---- cursor visibility helpers (async-signal-safe: write() only) ---- */ /* Hide the terminal text cursor via DECTCEM while the spinner is running. * Only called when isatty(STDOUT_FILENO) is already confirmed. */ static void inline_cursor_hide(void) { write(STDOUT_FILENO, "\033[?25l", 6); g_cursor_hidden = 1; } /* Restore the cursor. Idempotent — safe to call multiple times. */ static void inline_cursor_show(void) { if (g_cursor_hidden) { write(STDOUT_FILENO, "\033[?25h", 6); g_cursor_hidden = 0; } } /* Signal handler for SIGINT / SIGTERM / SIGHUP / SIGQUIT: restore the cursor, * put back each original handler, then re-raise the signal so the process * terminates with the correct exit status (and coredumps for SIGQUIT). */ static void inline_cursor_sig(int sig) { inline_cursor_show(); if (g_cursor_sigs_set) { sigaction(SIGINT, &g_old_sa_sigint, NULL); sigaction(SIGTERM, &g_old_sa_sigterm, NULL); sigaction(SIGHUP, &g_old_sa_sighup, NULL); sigaction(SIGQUIT, &g_old_sa_sigquit, NULL); g_cursor_sigs_set = 0; } raise(sig); } /* atexit backstop: covers exit() paths not reached by the signal handler * (e.g. a fatal error path that calls exit() directly). */ static void inline_cursor_atexit(void) { inline_cursor_show(); } /* Emit one progress character, replacing the current spinner glyph and * advancing to the next animation frame. Must NOT be called from a signal * handler. Blocks SIGALRM around the write so the handler cannot race us. */ static void inline_spin_emit(char c) { if (!g_inline_spinning) { /* spinner not running — plain emit */ printf("%c", c); return; } sigset_t blk, old; sigemptyset(&blk); sigaddset(&blk, SIGALRM); sigprocmask(SIG_BLOCK, &blk, &old); int frame = (int)(++g_inline_frame); char buf[8]; int n = 0; buf[n++] = '\b'; /* back over trailing space (1 display col) */ buf[n++] = '\b'; /* back over current spinner glyph (1 display col) */ buf[n++] = c; /* leave result character behind */ if (g_inline_utf8) { const char *sp = os_braille[frame % OS_BRAILLE_N]; memcpy(buf + n, sp, 3); n += 3; } else { buf[n++] = g_ascii_spin[frame % G_ASCII_SPIN_N]; } buf[n++] = ' '; /* re-emit trailing space after new spinner glyph */ write(STDOUT_FILENO, buf, (size_t)n); sigprocmask(SIG_UNBLOCK, &blk, &old); } /* Tear down the inline spinner, emit one optional suffix (e.g. "T\n" or " \n"), * and restore the previous SIGALRM disposition. Safe to call even if spinner * was never started (g_inline_armed == 0). */ static void inline_spin_finish(const char *suffix) { if (!g_inline_armed) { if (suffix) printf("%s", suffix); return; } sigset_t blk, old; sigemptyset(&blk); sigaddset(&blk, SIGALRM); sigprocmask(SIG_BLOCK, &blk, &old); /* disarm timer, restore saved SIGALRM handler */ struct itimerval zero; memset(&zero, 0, sizeof(zero)); setitimer(ITIMER_REAL, &zero, NULL); sigaction(SIGALRM, &g_o_sa_old, NULL); g_inline_armed = 0; g_inline_spinning = 0; /* restore cursor and termination signal handlers before printing */ inline_cursor_show(); if (g_cursor_sigs_set) { sigaction(SIGINT, &g_old_sa_sigint, NULL); sigaction(SIGTERM, &g_old_sa_sigterm, NULL); sigaction(SIGHUP, &g_old_sa_sighup, NULL); sigaction(SIGQUIT, &g_old_sa_sigquit, NULL); g_cursor_sigs_set = 0; } if (suffix && suffix[0]) { /* backspace over trailing space + spinner glyph, then emit suffix */ char buf[32]; int n = 0; buf[n++] = '\b'; /* back over trailing space */ buf[n++] = '\b'; /* back over spinner glyph */ size_t slen = strlen(suffix); if (n + (int)slen <= (int)(sizeof(buf) - 1)) { memcpy(buf + n, suffix, slen); n += (int)slen; } write(STDOUT_FILENO, buf, (size_t)n); } sigprocmask(SIG_UNBLOCK, &blk, &old); } #endif /* !WIN32 */ /*---------------------------------------------------------------------------*/ static void LFTDefaultEventHandler(lft_session_params * sess, int code, const void * params) { const EvtSentPacketParam * spparam; const EvtNoReplyParam * nrparam; const struct trace_packet_s * packet; const EvtDebugCheckpoint1Param * edcpparam; const EvtNonSeqPacketParam * enspparam; const EvtRecvPacketParam * erpparam; const EvtIncomingICMPUDPParam * eiiuparam; const EvtIncomingICMPTCPParam * eiitparam; const EvtIncomingICMPEchoParam * eiiiparam; const EvtIncomingICMPICMPParam * eicmparam; const struct tcphdr *tcp; const struct udphdr *udp; const struct icmp_echo_header_s * echo; const struct ip * ip; const struct icmp * icmp; if(global_output_style==1 && code!=EVT_RPT_PACKET_LIST_END && code!=EVT_RPT_PACKET_INFO) printf("\n",sess->sport,sess->dport); else printf ("Autoconfigured to source port %d, destination port %d.\n",sess->sport, sess->dport); } break; case EVT_ADDRESS_INITIALIZED: if(global_output_style<2) { print_host (sess, sess->local_address); if(global_output_style==1) { printf(" protocol=\"%d\"",sess->protocol); if(sess->protocol!=2 && sess->protocol!=3) { if (sess->random_source) printf (" rndsport=\"1\" sport=\"%d\"", sess->sport); else printf (" rndsport=\"0\" sport=\"%d\"", sess->sport); } printf(" />\n"); } else { if(!global_output_style) { if(sess->protocol==2 || sess->protocol==3) printf("\n"); else if (sess->random_source) printf (":%d (pseudo-random)\n", sess->sport); else printf (":%d\n", sess->sport); } } } break; case EVT_SENT_PACKET: spparam=(const EvtSentPacketParam *)params; if(global_output_style==1) { printf(" protocol=\"%d\" ttl=\"%d\"", sess->protocol, spparam->nhop+1); if(!sess->protocol || sess->protocol==4) { int flcnt=0; printf(" seq=\"%u\" xflags=\"%#x\" flags=\"", spparam->tseq, spparam->flags); if (spparam->flags & TH_RST) { printf ("RST"); flcnt++; } if (spparam->flags & TH_ACK) { if(flcnt) printf(";"); printf("ACK"); flcnt++; } if (spparam->flags & TH_SYN) { if(flcnt) printf(";"); printf ("SYN"); flcnt++; } if (spparam->flags & TH_FIN) { if(flcnt) printf(";"); printf ("FIN"); flcnt++; } if(!flcnt) printf("none"); printf("\" />\n"); } else if(sess->protocol==1) { printf(" dport=\"%d\" ipid=\"%u\" />\n", sess->dport, (spparam->tseq & 0xFFFF) ? (unsigned)(spparam->tseq & 0xFFFF) : 0xFFFFu); } else { printf(" />\n"); } } else if(!global_output_style) { if(!sess->protocol || sess->protocol==4) { printf("SENT TCP TTL=%d SEQ=%u FLAGS=%#x ( ", spparam->nhop+1, spparam->tseq, spparam->flags); if (spparam->flags & TH_RST) printf ("RST "); if (spparam->flags & TH_ACK) printf ("ACK "); if (spparam->flags & TH_SYN) printf ("SYN "); if (spparam->flags & TH_FIN) printf ("FIN "); if (sess->pmtud) { #ifdef SCREWED_IP_LEN int pkt_len = (int)sess->trace_packet.ip_hdr.ip_len; #else int pkt_len = (int)ntohs(sess->trace_packet.ip_hdr.ip_len); #endif printf(") LEN=%d [PMTUD%s]\n", pkt_len, sess->pmtud_verifying ? " verify" : ""); } else { printf(")\n"); } } else if(sess->protocol==1) { if (sess->pmtud) { #ifdef SCREWED_IP_LEN int pkt_len = (int)sess->trace_packet.ip_hdr.ip_len; #else int pkt_len = (int)ntohs(sess->trace_packet.ip_hdr.ip_len); #endif printf("SENT UDP TTL=%d DPORT=%d IPID=%u LEN=%d [PMTUD%s]\n", spparam->nhop+1, sess->dport, (spparam->tseq & 0xFFFF) ? (unsigned)(spparam->tseq & 0xFFFF) : 0xFFFFu, pkt_len, sess->pmtud_verifying ? " verify" : ""); } else { printf("SENT UDP TTL=%d DPORT=%d IPID=%u\n", spparam->nhop+1, sess->dport, (spparam->tseq & 0xFFFF) ? (unsigned)(spparam->tseq & 0xFFFF) : 0xFFFFu); } } else { if (sess->pmtud) printf("SENT ICMP TTL=%d SEQ=%u LEN=%d [PMTUD%s]\n", spparam->nhop+1, (unsigned)spparam->tseq, sess->userlen, sess->pmtud_verifying ? " verify" : ""); else printf("SENT ICMP TTL=%d\n", spparam->nhop+1); } } break; case EVT_SHOW_PAYLOAD: packet=(const struct trace_packet_s *)params; if(global_output_style==1) { if(!packet->payload_len) printf(" payloadlen=\"%d\" />\n", packet->payload_len); else printf(" payloadlen=\"%d\">%s\n", packet->payload_len, packet->payload); } else if(!global_output_style) { if(!packet->payload_len) printf("Payload: Length=%d Contents=EMPTY\n", packet->payload_len); else printf("Payload: Length=%d Contents=\"%s\"\n", packet->payload_len, packet->payload); } break; case EVT_SHOW_UDP_CHECKSUM: packet=(const struct trace_packet_s *)params; if(global_output_style==1) printf(" udpsum=\"%#x\"/>\n",packet->udp_hdr.uh_sum); else if(!global_output_style) printf("UDP Checksum = %#x\n",packet->udp_hdr.uh_sum); break; case EVT_SHOW_TCP_CHECKSUM: packet=(const struct trace_packet_s *)params; if(global_output_style==1) printf(" tcpsum=\"%#x\"/>\n",packet->tcp_hdr.th_sum); else if(!global_output_style) printf("TCP Checksum = %#x\n",packet->tcp_hdr.th_sum); break; case EVT_SHOW_HOPS: if(global_output_style==1) printf(" uphops=\"%d\" />\n", (int)(*((const short *)params))); else if(!global_output_style) printf("Upping states of the hops following %d\n", (int)(*((const short *)params))); break; case EVT_SHOW_NUM_HOPS: if(global_output_style<2) { if(global_output_style) printf(" numhops=\"%d\" />\n", (sess->num_hops+1)); else printf ("Concluding with %d hops.\n", (sess->num_hops+1)); } break; case EVT_TRACE_COMPLETED: if(global_output_style<2) { if(global_output_style) { printf(" />\n"); } else { if (sess->num_hops && !sess->nostatus && !sess->noisy) { #ifndef WIN32 /* spinner eats the T: \b + T + \n */ if (g_inline_armed) inline_spin_finish("T\n"); else #endif printf ("T\n"); } else if (!sess->noisy && !sess->nostatus) { #ifndef WIN32 /* spinner: \b + space (erase glyph) + \n */ if (g_inline_armed) inline_spin_finish(" \n"); else #endif printf ("\n"); } } } break; case EVT_ON_RESOLUTION: if(global_output_style<2) { if(global_output_style) { printf(" asresolutiontype=\""); if(sess->use_radb) printf ("RADB"); else if(sess->use_cymru) printf ("Cymru"); else if(sess->use_ris) printf ("RIPE"); else printf ("PWhoIs"); printf("\" />\n"); } else { if(sess->use_radb) printf ("Using RADB for in-line AS resolution...\n"); else if(sess->use_cymru) printf ("Using Cymru for bulk AS resolution...\n"); else if(sess->use_ris) printf ("Using RIPE NCC RIS for bulk AS resolution...\n"); else printf ("Using Prefix WhoIs for bulk AS resolution...\n"); } } break; case EVT_TRACE_REPORT_START: if(global_output_style<2) { if(!global_output_style) printf ("TTL LFT trace to "); print_host (sess, sess->remote_address); if(global_output_style) { printf(" protocol=\"%d\"",sess->protocol); switch(sess->protocol) { case 0: printf(" protocolname=\"TCP\" dport=\"%d\" />\n",sess->dport); break; case 4: printf(" protocolname=\"TCP\" dportrange=\"%d-%d\" />\n",sess->dport,(sess->dport + (*((const int *)params)))); break; case 1: printf(" protocolname=\"UDP\" dport=\"%d\" />\n",sess->dport); break; case 2: printf(" protocolname=\"ICMP\" />\n"); break; case 3: printf(" protocolname=\"RFC1393\" />\n"); break; } } else { switch(sess->protocol) { case 0: printf(":%d/tcp\n",sess->dport); break; case 4: printf(":%d-%d/tcp\n",sess->dport, (sess->dport + (*((const int *)params)))); break; case 1: printf(":%d/udp\n",sess->dport); break; case 2: printf("/icmp\n"); break; case 3: printf("/rfc1393\n"); break; } } } break; case EVT_RPT_NO_REPLY: if(global_output_style<2) { nrparam=(const EvtNoReplyParam *)params; if(global_output_style) { printf(" firstholehop=\"%d\" lastholehop=\"%d\" />\n", nrparam->hopno - nrparam->noreply + 1, nrparam->hopno); } else { if (nrparam->noreply == 1) printf("** [neglected] no reply packets received from TTL %d\n", nrparam->hopno); if (nrparam->noreply > 1) printf("** [neglected] no reply packets received from TTLs %d through %d\n", nrparam->hopno - nrparam->noreply + 1, nrparam->hopno); } } break; case EVT_RPT_FRW_INSPECT_PACKS: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("** [firewall] the next gateway may statefully inspect packets\n"); } break; case EVT_RPT_FRW_STATE_FILTER: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("** [firewall] the next gateway may implement a flag-based state filter\n"); } break; case EVT_RPT_BSD_BUG: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("** [4.2-3 BSD bug?] the next gateway may errantly reply with reused TTLs\n"); } break; case EVT_RPT_PACKET_INFO: if(global_output_style<2) { EvtPacketInfoDefaultHandler(sess, (const EvtPacketInfoParam *)params); } break; case EVT_RPT_PACKET_LIST_END: if(global_output_style<2) { if (sess->rtt_stats && sess->_rtt_stat_hopno >= 0) { hop_rtt_stats_t rs = compute_hop_rtt_stats(sess, sess->_rtt_stat_hopno); if (rs.n > 0) { if (global_output_style==1) printf(" timems_min=\"%d\" timems_avg=\"%d\" timems_max=\"%d\"" " timems_jitter=\"%d\" />\n", (int)round(rs.rtt_min), (int)round(rs.rtt_avg), (int)round(rs.rtt_max), (int)round(rs.rtt_stddev)); else printf(" %d/%d/%d/%dms\n", (int)round(rs.rtt_min), (int)round(rs.rtt_avg), (int)round(rs.rtt_max), (int)round(rs.rtt_stddev)); } else { if (global_output_style==1) printf(" />\n"); else printf("\n"); } } else { if(global_output_style==1) printf("\" />\n"); else printf ("ms\n"); } } break; case EVT_RPT_HOP_INFO_START: sess->_rtt_stat_hopno = *((const int *)params); /* track for rtt_stats */ if(global_output_style<2) { if(global_output_style) printf (" index=\"%2d\"", (*((const int *)params)) + 1); else printf ("%2d ", (*((const int *)params)) + 1); } break; case EVT_RPT_NO_HOPS: if(global_output_style<2) { if(global_output_style) { printf(" protocol=\"%d\"",sess->protocol); if(sess->protocol==0 || sess->protocol==4) printf(" protocolname=\"TCP\""); else if(sess->protocol==1) printf(" protocolname=\"UDP\""); else printf(" protocolname=\"ICMP\""); if(sess->target_anomaly) printf(" targetanomaly=\"1\""); if(sess->target_anomaly || sess->protocol==0) printf(" dport=\"%d\"",sess->dport); if(!sess->target_anomaly && sess->protocol==4) printf(" dportrange=\"%d-%d\"",sess->dport,(sess->dport + (*((const int *)params)))); if(!sess->target_anomaly && sess->protocol==1) printf(" dport=\"%d\"",sess->dport); printf(" />\n"); } else { if (sess->target_anomaly) printf("** [%d/tcp sequence anomaly from target] Try advanced options (use -VV to see packets).\n", sess->dport); else if (sess->protocol==1) printf("** [%d/udp no reply from target] Use -VV to see packets.\n", sess->dport); else if (sess->protocol==0) printf("** [%d/tcp no reply from target] Try advanced options (use -VV to see packets).\n", sess->dport); else if (sess->protocol==4) printf("** [%d-%d/tcp no reply from target] Use -VV to see packets.\n", sess->dport, (sess->dport + (*((const int *)params)))); else printf("** [icmp no reply from target] Try advanced options (use -VV to see packets).\n"); } } break; case EVT_RPT_TIME_TRACE: if(global_output_style<2) { gettimeofday (&(sess->now), NULL); #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) if(!sess->UseLocalTime) (void)strftime(tbuf, sizeof(tbuf), time_format, (struct tm *) gmtime((time_t *) &(sess->now.tv_sec))); else #endif (void)strftime(tbuf, sizeof(tbuf), time_format, (struct tm *)localtime((time_t *) &(sess->now.tv_sec))); if(global_output_style) { printf (" finishtime=\"%s\"", tbuf); printf (" elapsed=\"%.2f\"",(timediff_ms(sess->begin_time, sess->now) / 1000)); printf (" tracingtime=\"%.2f\"", (timediff_ms(sess->begin_time, sess->trace_done_time) / 1000)); if (sess->resolve_names || sess->do_aslookup || sess->do_netlookup) { #ifdef HAVE_CARES if (sess->ares_chan && !sess->async_dns_disabled) printf(" resolvingtime=\"%.2f\" asyncdns=\"1\"", sess->ares_wait_ms / 1000); else #endif printf(" resolvingtime=\"%.2f\"", (timediff_ms(sess->trace_done_time, sess->now) / 1000)); } printf(" />\n"); } else { printf ("LFT trace finished at %s", tbuf); printf (" (%.2fs elapsed)",(timediff_ms(sess->begin_time, sess->now) / 1000)); if (sess->noisy) { printf ("\nTime spent tracing: %.2fs", (timediff_ms(sess->begin_time, sess->trace_done_time) / 1000)); if (sess->resolve_names || sess->do_aslookup || sess->do_netlookup) { #ifdef HAVE_CARES if (sess->ares_chan && !sess->async_dns_disabled) printf(", resolving: %.2fs (async)", sess->ares_wait_ms / 1000); else #endif printf(", resolving: %.2fs", (timediff_ms(sess->trace_done_time, sess->now) / 1000)); } } printf("\n"); } } break; case EVT_ON_EXIT: if(global_output_style==1) printf(" />\n"); sess->exit_state=-100; if(global_output_style==2) GraphVizOutput(sess); if(global_output_style==3) ASCIIOutput(sess); break; case EVT_TTL_NO_REPLY: if(global_output_style<2) { if(global_output_style) printf(" ttl=\"%d\" />\n", (*((const int *)params))); else printf("No reply on TTL %d\n", (*((const int *)params))); } break; case EVT_PROGRESS_NO_REPLY: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else { #ifndef WIN32 inline_spin_emit('*'); #else printf("*"); #endif } } break; case EVT_PROGRESS_SKIP_PACKET: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else { #ifndef WIN32 inline_spin_emit('?'); #else printf("?"); #endif } } break; case EVT_TTL_TOUT_RESEND: if(global_output_style<2) { if(global_output_style) printf(" ttl=\"%d\" />\n", (*((const int *)params))); else printf("TTL %d timed out, (resending)\n",(*((const int *)params))); } break; case EVT_TTL_TOUT_GIVINGUP: #ifndef WIN32 if (g_o_active) g_o_timeout_flash = 1; #endif if(global_output_style<2) { if(global_output_style) printf(" ttl=\"%d\" />\n", (*((const int *)params))); else printf("TTL %d timed out, (giving up)\n",(*((const int *)params))); } break; case EVT_DBG_CHECKPOINT1: if(global_output_style<2) { edcpparam=(const EvtDebugCheckpoint1Param *)params; if(global_output_style) { printf (" hilength=\"%d\" last_return=\"%d\"", sess->hop_info_length, edcpparam->last_return); printf (" no_reply=\"%d\" ahead_limit=\"%d\"", edcpparam->no_reply, sess->ahead_limit); printf (" num_hops=\"%d\" need_reply=\"%d\" />\n", sess->num_hops, edcpparam->need_reply); } else { printf ("| hilength %d, last_return %d\n", sess->hop_info_length, edcpparam->last_return); printf ("| no_reply %d, ahead_limit %d\n", edcpparam->no_reply, sess->ahead_limit); printf ("| num_hops %d, need_reply %d\n", sess->num_hops, edcpparam->need_reply); } } break; case EVT_CANT_RELIABLY_RTRIP: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("LFT can\'t reliably round-trip. Close-proximity filter in the way?\n"); } break; case EVT_HAVE_UNANSWERRED_HOPS: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("I still have unanswered hops.\n"); } break; case EVT_TOO_FAR_AHEAD: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("I\'m too far ahead, returning.\n"); } break; case EVT_HAVE_GAPS: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("I know the distance to the target, but I have gaps to fill. Returning...\n"); } break; case EVT_EITHER_RESP_OR_TOUT: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("Everyone either responded or timed out. "); } break; case EVT_LOOKFOR_UNINC_ACK: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("\nNo match in sequence check, looking for a match inside seq-.\n"); } break; case EVT_LOOKFOR_OFF_BY_LEN: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("No match in seq-, looking for a match inside seq+len.\n"); } break; case EVT_LOOKFOR_LAST_RESORT: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("No match in seq+len, looking for a match inside last resort loop.\n"); } break; case EVT_SKIP_PACKET: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("(packet not meant for us, skip)\n"); } break; case EVT_ACK_WAS_NOT_INC: if(global_output_style<2) { enspparam=(const EvtNonSeqPacketParam *)params; if(global_output_style) printf (" src=\"%s\" pttl=\"%d\" />\n", inet_ntoa (enspparam->ipaddr), enspparam->tp->hopno+1); else { printf ("SRC=%s PTTL=%d\n", inet_ntoa (enspparam->ipaddr), enspparam->tp->hopno+1); printf ("Target\'s ACK was not incremented. "); } } break; case EVT_RST_REL_TO_ISN: if(global_output_style<2) { enspparam=(const EvtNonSeqPacketParam *)params; if(global_output_style) { printf (" src=\"%s\" pttl=\"%d\"", inet_ntoa (enspparam->ipaddr), enspparam->tp->hopno+1); printf (" payloadlen=\"%d\" />\n", sess->payloadlen); } else { printf ("SRC=%s PTTL=%d\n", inet_ntoa (enspparam->ipaddr), enspparam->tp->hopno+1); printf ("Target\'s RST relates to the ISN + payload length (%d). ", sess->payloadlen); } } break; case EVT_ACK_WAS_WAY_OFF: if(global_output_style<2) { enspparam=(const EvtNonSeqPacketParam *)params; if(global_output_style) printf (" src=\"%s\" pttl=\"%d\" />\n", inet_ntoa (enspparam->ipaddr), enspparam->tp->hopno+1); else { printf ("SRC=%s PTTL=%d\n", inet_ntoa (enspparam->ipaddr), enspparam->tp->hopno+1); printf ("Target\'s ACK was way off. "); } } break; case EVT_DUPLICATE_PACKET: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf ("(duplicate packet, skip)\n"); } break; case EVT_PROGRESS_DUPLICATE: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else { #ifndef WIN32 inline_spin_emit('!'); #else printf("!"); #endif } } break; case EVT_RECV_PACKET: #ifndef WIN32 /* Guard: sub-sessions (seam-check via -y) fire events while the main * session's spinner is still active. Their hop_info is tiny and would * clobber g_o_hops / g_o_replies for the main session display. */ if (g_o_active && !sess->is_graphviz_subquery) { /* EVT_RECV_PACKET fires when noisy>1 (watch mode forces noisy=2 so * verbose output feeds the log drawer). EVT_PROGRESS_OK fires in the * non-verbose path. Both represent a valid reply, so both must * increment g_o_replies for the ↓ counter in watch mode. */ g_o_replies++; erpparam = (const EvtRecvPacketParam *)params; if (erpparam && erpparam->tp) { /* Recount hops from sess->hop_info, capping at num_hops+1 * once the target is known. Same logic as EVT_PROGRESS_OK, * keeps the ⬡ counter consistent and ensures probes that * overshot the target (-a ahead-limit replies at TTL > * target hop) do not inflate the count. */ int _h, _hcount = 0; int _max = sess->hop_info_length; if (sess->num_hops > 0 && sess->num_hops < _max) _max = sess->num_hops + 1; for (_h = 0; _h < _max; _h++) if (sess->hop_info[_h].all_rcvd > 0) _hcount++; g_o_hops = (sig_atomic_t)_hcount; /* icmp_type == -1 means target reached (no TTL-exceeded) */ if (erpparam->tp->icmp_type == -1) { g_o_target = 1; g_o_target_flash = 1; } } } #endif if(global_output_style<2) { erpparam=(const EvtRecvPacketParam *)params; if(global_output_style) { /* For TCP/UDP/BCP modes, add icmptype/icmpcode/icmpname when * this is an ICMP error hop (tp->icmp_type correctly set early). */ int _is_icmp_engine = (sess->protocol == 2 || sess->protocol == 3); if (!_is_icmp_engine && erpparam->tp && erpparam->tp->icmp_type != -1) { int _rt = (erpparam->tp->icmp_type == -2) ? 11 : 3; int _rc = (erpparam->tp->icmp_type == -2) ? 0 : erpparam->tp->icmp_type; printf (" icmptype=\"%d\" icmpcode=\"%d\" icmpname=\"%s\"" " src=\"%s\" pttl=\"%d\" pseq=\"%u\" />\n", _rt, _rc, lft_icmp_verbose_name(erpparam->tp->icmp_type), inet_ntoa (erpparam->ipaddr), erpparam->tp->hopno+1, erpparam->seq); } else { printf (" src=\"%s\" pttl=\"%d\" pseq=\"%u\" />\n", inet_ntoa (erpparam->ipaddr), erpparam->tp->hopno+1, erpparam->seq); } } else { /* Routing: * ICMP-engine modes (protocol 2/3): * EVT_RCVD_ICMP_ICMP/Echo already printed * "RCVD ICMP type=N code=N NAME='...' " on this line. * Just append SRC/PTTL/PSEQ to close it. * TCP/UDP/BCP modes (protocol 0/1/4): * EVT_RCVD_ICMP_TCP/UDP is suppressed. * For ICMP error hops print the full normalized line here. * For TCP RST/SYN-ACK (icmp_type==-1) just append the tail * to the "RCVD TCP FLAGS=... SEQ=N ACK=N " line from * EVT_RCVD_TCP. */ int _is_icmp_engine = (sess->protocol == 2 || sess->protocol == 3); if (!_is_icmp_engine && erpparam->tp && erpparam->tp->icmp_type != -1) { int _rt = (erpparam->tp->icmp_type == -2) ? 11 : 3; int _rc = (erpparam->tp->icmp_type == -2) ? 0 : erpparam->tp->icmp_type; printf ("RCVD ICMP type=%d code=%d NAME='%s' SRC=%s PTTL=%d PSEQ=%u\n", _rt, _rc, lft_icmp_verbose_name(erpparam->tp->icmp_type), inet_ntoa (erpparam->ipaddr), erpparam->tp->hopno+1, erpparam->seq); } else { /* ICMP-engine tail, or TCP RST continuation */ printf ("SRC=%s PTTL=%d PSEQ=%u\n", inet_ntoa (erpparam->ipaddr), erpparam->tp->hopno+1, erpparam->seq); } } } break; case EVT_PROGRESS_OK: #ifndef WIN32 if (g_o_active && !sess->is_graphviz_subquery) { int _h, _hcount = 0; g_o_replies++; /* Count unique responding hops; cap at num_hops+1 to avoid * counting extra target replies at probed-beyond-target TTLs */ int _max = sess->hop_info_length; if (sess->num_hops > 0 && sess->num_hops < _max) _max = sess->num_hops + 1; for (_h = 0; _h < _max; _h++) if (sess->hop_info[_h].all_rcvd > 0) _hcount++; g_o_hops = (sig_atomic_t)_hcount; /* Target detection: num_hops set when target replies (ICMP & TCP) */ if (!g_o_target && sess->num_hops > 0) { g_o_target = 1; g_o_target_flash = 1; } } #endif if(global_output_style<2) { if(global_output_style) printf(" />\n"); else { #ifndef WIN32 inline_spin_emit('.'); #else printf("."); #endif } } break; case EVT_TCP_PORT_CLOSED: #ifndef WIN32 if (g_o_active && !sess->is_graphviz_subquery) { g_o_target = 1; g_o_target_flash = 1; } #endif if(global_output_style<2) { if(global_output_style) printf (" dport=\"%d\" />\n", sess->dport); else if (sess->pmtud) printf ("Port %d/tcp responded to our oversized probes in PMTUD" " mode; target sent RST.\n", sess->dport); else printf ("Port %d/tcp appears to be closed; target sent RST.\n", sess->dport); } break; case EVT_TCP_PORT_OPEN: #ifndef WIN32 if (g_o_active && !sess->is_graphviz_subquery) { g_o_target = 1; g_o_target_flash = 1; } #endif if(global_output_style<2) { if(global_output_style) printf (" dport=\"%d\" />\n", sess->dport); else if (sess->pmtud) printf ("Port %d/tcp responded to our oversized probes in PMTUD" " mode; target attempted handshake.\n", sess->dport); else printf ("Port %d/tcp open; target attempted handshake.\n", sess->dport); } break; case EVT_PROCESS_PACKET_START: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("Received new data from packet capture; processing.\n"); } break; case EVT_UDP_NOT_FOR_US: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else { if (sess->noisy>3) printf("Not for us\n"); else printf("?"); } } break; case EVT_INCOMING_ICMP_ICMP: if(global_output_style<2) { eicmparam=(const EvtIncomingICMPICMPParam *)params; if(global_output_style) { printf(" src=\"%s\" proto=\"%d\" ttl=\"%d\"", inet_ntoa(eicmparam->ip->ip_src), eicmparam->ip->ip_p, eicmparam->ip->ip_ttl); printf(" protocolname=\"ICMP\""); if (eicmparam->icmp->icmp_type == ICMP_TIMXCEED) printf(" icmptype=\"ICMP_TIMXCEED\""); else if (eicmparam->icmp->icmp_type == ICMP_UNREACH) printf(" icmptype=\"ICMP_UNREACH\""); printf(" echoid=\"%d\" echoseq=\"%d\"",(int)eicmparam->echo->id, (int)eicmparam->echo->sequence); printf(" />\n"); } else { printf("INCOMING IP: SRC=%s PROTO=%d TTL=%d\n", inet_ntoa(eicmparam->ip->ip_src), eicmparam->ip->ip_p, eicmparam->ip->ip_ttl); printf("\\->ICMP+ICMP: "); if (eicmparam->icmp->icmp_type == ICMP_TIMXCEED) printf("TTL exceeded; "); else if (eicmparam->icmp->icmp_type == ICMP_UNREACH) printf("unreachable; "); printf("ICMP echo ID=%d SEQ=%d\n",(int)eicmparam->echo->id, (int)eicmparam->echo->sequence); } } break; case EVT_INCOMING_ICMP_Echo: if(global_output_style<2) { eiiiparam=(const EvtIncomingICMPEchoParam *)params; if(global_output_style) { printf(" src=\"%s\" proto=\"%d\" ttl=\"%d\"", inet_ntoa(eiiiparam->ip->ip_src), eiiiparam->ip->ip_p, eiiiparam->ip->ip_ttl); printf(" protocolname=\"ECHO\" echoid=\"%d\" echoseq=\"%d\"",(int)eiiiparam->echo->id, (int)eiiiparam->echo->sequence); printf(" />\n"); } else { printf("INCOMING IP: SRC=%s PROTO=%d TTL=%d\n", inet_ntoa(eiiiparam->ip->ip_src), eiiiparam->ip->ip_p, eiiiparam->ip->ip_ttl); printf("\\->ICMP+ICMP echo: "); printf("ICMP echo ID=%d SEQ=%d\n",(int)eiiiparam->echo->id, (int)eiiiparam->echo->sequence); } } break; case EVT_INCOMING_ICMP_UDP: if(global_output_style<2) { eiiuparam=(const EvtIncomingICMPUDPParam *)params; if(global_output_style) { printf(" src=\"%s\" proto=\"%d\" ttl=\"%d\"", inet_ntoa (eiiuparam->orig_ip->ip_src), eiiuparam->ip->ip_p, eiiuparam->ip->ip_ttl); if (eiiuparam->icmp->icmp_type == ICMP_TIMXCEED) printf(" icmptype=\"ICMP_TIMXCEED\""); else if (eiiuparam->icmp->icmp_type == ICMP_UNREACH) printf(" icmptype=\"ICMP_UNREACH\""); printf(" protocolname=\"UDP\" sport=\"%d\" dport=\"%d\"",(ntohs (eiiuparam->udp->uh_sport)), (ntohs (eiiuparam->udp->uh_dport))); printf(" />\n"); } else { printf("INCOMING IP: SRC=%s PROTO=%d TTL=%d\n", inet_ntoa (eiiuparam->orig_ip->ip_src), eiiuparam->ip->ip_p, eiiuparam->ip->ip_ttl); printf("\\->ICMP+UDP: "); if (eiiuparam->icmp->icmp_type == ICMP_TIMXCEED) printf("TTL exceeded; "); else if (eiiuparam->icmp->icmp_type == ICMP_UNREACH) printf("unreachable; "); printf("UDP SPORT=%d DPORT=%d\n", (ntohs (eiiuparam->udp->uh_sport)), (ntohs (eiiuparam->udp->uh_dport))); } } break; case EVT_INCOMING_ICMP_TCP: if(global_output_style<2) { eiitparam=(const EvtIncomingICMPTCPParam *)params; if(global_output_style) { printf(" src=\"%s\" proto=\"%d\" ttl=\"%d\"", inet_ntoa (eiitparam->orig_ip->ip_src), eiitparam->ip->ip_p, eiitparam->ip->ip_ttl); if (eiitparam->icmp->icmp_type == ICMP_TIMXCEED) printf(" icmptype=\"ICMP_TIMXCEED\""); else if (eiitparam->icmp->icmp_type == ICMP_UNREACH) printf(" icmptype=\"ICMP_UNREACH\""); printf(" protocolname=\"TCP\" sport=\"%d\" dport=\"%d\"",(ntohs (eiitparam->tcp->th_sport)), (ntohs (eiitparam->tcp->th_dport))); printf(" />\n"); } else { printf("INCOMING IP: SRC=%s PROTO=%d TTL=%d\n", inet_ntoa (eiitparam->orig_ip->ip_src), eiitparam->ip->ip_p, eiitparam->ip->ip_ttl); printf("\\->ICMP+TCP: "); if (eiitparam->icmp->icmp_type == ICMP_TIMXCEED) printf("TTL exceeded; "); else if (eiitparam->icmp->icmp_type == ICMP_UNREACH) printf("unreachable; "); printf("TCP SPORT=%d DPORT=%d\n", (ntohs (eiitparam->tcp->th_sport)), (ntohs (eiitparam->tcp->th_dport))); } } break; case EVT_RCVD_ICMP_Echo: if(global_output_style<2) { if(global_output_style) { echo=(const struct icmp_echo_header_s *)params; /* Keep id/seq for backward compat; add type/code/name for parity. */ printf(" icmptype=\"%d\" icmpcode=\"0\" icmpname=\"%s\"" " id=\"%d\" seq=\"%d\" />\n", ICMP_ECHOREPLY, lft_icmp_name_raw(ICMP_ECHOREPLY, 0), (int)echo->id, (int)echo->sequence); } else printf("RCVD ICMP type=%d code=%d NAME='%s' ", ICMP_ECHOREPLY, 0, lft_icmp_name_raw(ICMP_ECHOREPLY, 0)); } break; case EVT_RCVD_ICMP_ICMP: if(global_output_style<2) { icmp=(const struct icmp *)params; if(global_output_style) printf(" icmptype=\"%d\" icmpcode=\"%d\" icmpname=\"%s\" />\n", (int)icmp->icmp_type, (int)icmp->icmp_code, lft_icmp_name_raw(icmp->icmp_type, icmp->icmp_code)); else printf("RCVD ICMP type=%d code=%d NAME='%s' ", (int)icmp->icmp_type, (int)icmp->icmp_code, lft_icmp_name_raw(icmp->icmp_type, icmp->icmp_code)); } break; case EVT_RCVD_ICMP_UDP: if(global_output_style<2) { udp=(const struct udphdr *)params; if(global_output_style) printf(" dport=\"%u\" />\n", (unsigned int)ntohs(udp->uh_dport)); /* plain text: suppressed — EVT_RECV_PACKET prints the full normalized line */ } break; case EVT_RCVD_ICMP_TCP: if(global_output_style<2) { tcp=(const struct tcphdr *)params; if(global_output_style) printf(" seq=\"%u\" />\n",ntohl (tcp->th_seq)); /* plain text: suppressed — EVT_RECV_PACKET prints the full normalized line */ } break; case EVT_RCVD_TCP: if(global_output_style<2) { tcp=(const struct tcphdr *)params; if(global_output_style) { int flcnt=0; printf(" xflags=\"%#x\" flags=\"", tcp->th_flags); if (tcp->th_flags & TH_RST) { printf ("RST"); flcnt++; } if (tcp->th_flags & TH_ACK) { if(flcnt) printf(";"); printf("ACK"); flcnt++; } if (tcp->th_flags & TH_SYN) { if(flcnt) printf(";"); printf ("SYN"); flcnt++; } if (tcp->th_flags & TH_FIN) { if(flcnt) printf(";"); printf ("FIN"); flcnt++; } if(!flcnt) printf("none\""); else printf("\""); printf (" seq=\"%u\" ack=\"%u\"", ntohl (tcp->th_seq), ntohl (tcp->th_ack)); printf(" />\n"); } else { printf("RCVD TCP FLAGS=%#x ( ",tcp->th_flags); if (tcp->th_flags & TH_SYN) printf ("SYN "); if (tcp->th_flags & TH_ACK) printf ("ACK "); if (tcp->th_flags & TH_FIN) printf ("FIN "); if (tcp->th_flags & TH_RST) printf ("RST "); printf (") SEQ=%u ACK=%u ", ntohl (tcp->th_seq), ntohl (tcp->th_ack)); } } break; case EVT_RCVD_UNKNOWN: if(global_output_style<2) { ip=(const struct ip *)params; if(global_output_style) { printf(" protocolcode=\"%d\"", ip->ip_p); #if defined(WIN32) || defined(_WIN32) printf(" ipfrom=\"%d.%d.%d.%d\" ipto=\"%d.%d.%d.%d\"", (int)ip->ip_src.s_net,(int)ip->ip_src.s_host,(int)ip->ip_src.s_lh,(int)ip->ip_src.s_impno, (int)ip->ip_dst.s_net,(int)ip->ip_dst.s_host,(int)ip->ip_dst.s_lh,(int)ip->ip_dst.s_impno); #endif printf(" />\n"); } else { printf("Incoming datagram contains unsupported protocol %d.\n", ip->ip_p); #if defined(WIN32) || defined(_WIN32) if (sess->noisy > 4) printf("\t(from %d.%d.%d.%d to %d.%d.%d.%d)\n", (int)ip->ip_src.s_net,(int)ip->ip_src.s_host,(int)ip->ip_src.s_lh,(int)ip->ip_src.s_impno, (int)ip->ip_dst.s_net,(int)ip->ip_dst.s_host,(int)ip->ip_dst.s_lh,(int)ip->ip_dst.s_impno); #endif } } break; case EVT_DEVICE_SELECTED: if(global_output_style<2) { if(global_output_style) printf (" device=\"%s\" linktype=\"%d\" linktypename=\"%s\" />\n", sess->pcap_dev, sess->pcap_datalink, pcap_datalink_val_to_name(sess->pcap_datalink)); else printf ("Receiving on %s, type %d (%s), transmitting on %s as ", sess->pcap_dev, sess->pcap_datalink, pcap_datalink_val_to_name(sess->pcap_datalink), sess->pcap_send_dev); } break; case EVT_SHOW_INITIAL_SEQNUM: if(global_output_style<2) { if(global_output_style) printf (" seqstart=\"%d\" />\n", sess->seq_start); else { printf ("Receive link type is %s (%d), skipping %0d bytes\n", pcap_datalink_val_to_name(sess->pcap_datalink),sess->pcap_datalink,sess->skip_header_len); printf ("Transmit Initial Sequence Number (ISN) will be %d\n", sess->seq_start); } } break; case EVT_TRACE_START: if(global_output_style<2) { if(global_output_style) { memset(&tbuf, 0, sizeof(tbuf)); #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) if(!sess->UseLocalTime) (void)strftime(tbuf, sizeof(tbuf), time_format, (struct tm *) gmtime((time_t *) &(sess->begin_time.tv_sec))); else #endif (void)strftime(tbuf, sizeof(tbuf), time_format, (struct tm *) localtime((time_t *) &(sess->begin_time.tv_sec))); printf (" begtime=\"%s\" />\n", tbuf); } else { if (sess->timetrace) { memset(&tbuf, 0, sizeof(tbuf)); #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) if(!sess->UseLocalTime) (void)strftime(tbuf, sizeof(tbuf), time_format, (struct tm *) gmtime((time_t *) &(sess->begin_time.tv_sec))); else #endif (void)strftime(tbuf, sizeof(tbuf), time_format, (struct tm *) localtime((time_t *) &(sess->begin_time.tv_sec))); printf ("LFT trace started at %s\n", tbuf); } if (!sess->nostatus && !sess->noisy) { printf("Tracing "); #ifndef WIN32 /* Start inline progress spinner when: * - default text output (style 0) * - stdout is a real terminal (not piped/redirected) * - not silent (-S) and not verbose (-V/-v) */ if (!global_output_style && isatty(STDOUT_FILENO)) { const char *term = getenv("TERM"); const char *lang = getenv("LANG"); const char *lc = getenv("LC_ALL"); /* Use braille if terminal isn't "dumb" and locale * hints UTF-8. Default to braille on unknown locales * since virtually all modern terminals handle it. */ g_inline_utf8 = 1; if (term && (strncmp(term, "dumb", 4) == 0 || strncmp(term, "unknown", 7) == 0)) g_inline_utf8 = 0; else if ((lang && strstr(lang, "8859")) || (lc && strstr(lc, "8859"))) g_inline_utf8 = 0; /* Latin-1 locale — ASCII safe */ g_inline_frame = 0; g_inline_spinning = 0; g_inline_armed = 0; o_spinner_arm_sigalrm(); g_inline_armed = 1; /* emit the first spinner frame + trailing space so * the block cursor sits one column clear of the glyph */ if (g_inline_utf8) printf("%s ", os_braille[0]); else printf("| "); fflush(stdout); g_inline_spinning = 1; /* Hide the cursor while the spinner is live. * Install SIGINT/SIGTERM/SIGHUP/SIGQUIT handlers that * restore it before re-raising the signal, and a one-time * atexit backstop for any exit() path. */ if (!g_cursor_sigs_set) { struct sigaction sa_cur; memset(&sa_cur, 0, sizeof(sa_cur)); sa_cur.sa_handler = inline_cursor_sig; sigemptyset(&sa_cur.sa_mask); sa_cur.sa_flags = 0; sigaction(SIGINT, &sa_cur, &g_old_sa_sigint); sigaction(SIGTERM, &sa_cur, &g_old_sa_sigterm); sigaction(SIGHUP, &sa_cur, &g_old_sa_sighup); sigaction(SIGQUIT, &sa_cur, &g_old_sa_sigquit); g_cursor_sigs_set = 1; } if (!g_cursor_atexit) { atexit(inline_cursor_atexit); g_cursor_atexit = 1; } inline_cursor_hide(); } #endif } } } break; case EVT_DBG_CHECKPOINT2: if(global_output_style<2) { if(global_output_style) printf(" />\n"); else printf("Left dispatch.\n"); } break; case EVT_DBG_LOG_MESSAGE: if(global_output_style<2) { if(global_output_style) printf(">%s\n",(const char *)params); else printf("%s",(const char *)params); } break; case EVT_OCHECK_START: if(global_output_style<2) { if(global_output_style) { print_host (sess, sess->remote_address); printf(" dport=\"%d\" />\n",sess->dport); } else if(sess->noisy) { printf("TCP open checking started for "); print_host (sess, sess->remote_address); printf(" port %d.\n",sess->dport); } } break; case EVT_OCHECK_OPEN: if(global_output_style<2) { if(global_output_style) { print_host (sess, sess->remote_address); printf(" dport=\"%d\" />\n",sess->dport); } else if(sess->noisy) { printf("TCP port %d is open on ",sess->dport); print_host (sess, sess->remote_address); printf(".\n"); } } break; } } /*---------------------------------------------------------------------------*/ static LFT_CALLBACK LFTErrHandler=LFTDefaultErrorHandler; static LFT_CALLBACK LFTEvtHandler=LFTDefaultEventHandler; /*---------------------------------------------------------------------------*/ void LFTInitializeCallbacks(LFT_CALLBACK error_handler, LFT_CALLBACK event_handler) { if(error_handler) LFTErrHandler=error_handler; else LFTErrHandler=LFTDefaultErrorHandler; if(event_handler) LFTEvtHandler=event_handler; else LFTEvtHandler=LFTDefaultEventHandler; } /*---------------------------------------------------------------------------*/ lft_session_params * LFTSessionOpen(void) { lft_session_params * sess= (lft_session_params *)malloc(sizeof(lft_session_params)); memset(sess, 0,sizeof(lft_session_params)); sess->scatter_ms = 20; sess->ttl_min = 0; sess->hop_info_length = 0; sess->hop_info=NULL; sess->tcp_flags = TH_SYN; sess->use_fins = 0; sess->seq_start = 0; /* generate ISN internally by default */ sess->dport = 443; /* set default destination to tcp/443 HTTP */ sess->sport = rand()%16384+49152; /* set default source to universal random-dynamic */ sess->auto_ports = 1; /* enable port autoselection by default */ sess->random_source = 0; /* disable random source port by default */ sess->set_tos = 0; /* disable set ToS bit by default */ sess->userlen = 0; /* user-requested packet length */ sess->payloadlen = 0; /* the final probe payloadlength */ sess->payload = NULL; sess->win_len = 32768; sess->timeout_ms = 250; /* timeout between retries */ sess->retry_max = 2; /* number of retries before giving up */ sess->retry_min = 1; /* minimum number of checks per hop */ sess->ahead_limit = 5; /* number of probes we can send * without replies if we don't know * the number of hops */ sess->dflag = 0; sess->ttl_limit = 30; /* max # hops to traverse (highest TTL) */ /* LFT-AUTOTUNE — topology-inheritance defaults (zero learned data, * 2-TTL margin above learned path length, feature enabled). */ memset(&sess->learned, 0, sizeof(sess->learned)); sess->no_autotune = 0; sess->learn_margin = 2; sess->break_on_icmp = 1; /* break on icmp other than time exceeded */ sess->noisy = 0; /* disable verbose debug by default */ sess->nostatus = 0; /* print status bar by default */ sess->userdevsel = 0; /* by default, we'll select the device */ sess->senddevsel = 0; /* by default, we won't use a spoof device */ sess->resolve_names = 1; /* dns resolution enabled by default */ sess->hostnames_only = 0; /* disable printing of IP addresses */ sess->timetrace = 0; /* disable tracer timing by default */ sess->adaptive = 0; /* disable state engine by default */ sess->protocol = 0; /* use UDP instead of TCP */ sess->do_netlookup = 0; /* disable netname lookup by default */ sess->do_aslookup = 0; /* disable asn lookup by default */ sess->use_radb = 0; /* use RADB instead of pwhois */ sess->use_cymru = 0; /* use Cymru instead of pwhois */ sess->use_ris = 0; /* use RIPE NCC RIS instead of pwhois */ sess->num_hops = 0; /*sess->num_sent = 0;*/ sess->num_rcvd = 0; sess->target_open = 0; sess->target_filtered = 0; sess->target_anomaly = 0; sess->hostname = NULL; sess->hostname_lsrr_size = 0; SLIST_INIT(&(sess->trace_packets)); sess->trace_packets_num = 0; sess->pcap_dev = NULL; sess->pcap_datalink = -1; sess->pcap_send_dev = NULL; sess->userdev = NULL; sess->senddev = NULL; sess->send_sock = 0; sess->btcpmap = NULL; sess->btcpmapsize = 0; sess->btcpdpucnt = 0; sess->trg_probe_is_sent = 0; sess->icmp_packet.packet = NULL; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) sess->recv_sock = 0; sess->wsastarted = 0; #else sess->pcapdescr = 0; #endif sess->UseLocalTime=1; sess->exit_state=0; sess->is_graphviz_subquery=0; sess->check_seam=0; sess->graphviz_icon_path=NULL; /*sess->wsess=w_init();*/ return sess; } /*---------------------------------------------------------------------------*/ void LFTSessionClose(lft_session_params * sess) { struct trace_packet_info_s *tp; #ifdef HAVE_CARES if (sess->ares_chan) { ares_destroy(sess->ares_chan); sess->ares_chan = NULL; } #endif if(sess->hop_info) free(sess->hop_info); if(sess->send_sock > 0) #if defined(WIN32) || defined(_WIN32) closesocket(sess->send_sock); #else close(sess->send_sock); #endif #if defined(WIN32) || defined(_WIN32) if(sess->pcap_dev != NULL) free(sess->pcap_dev); if(sess->recv_sock > 0) closesocket(sess->send_sock); #else if(sess->pcapdescr != 0) { pcap_close(sess->pcapdescr); sess->pcapdescr=0; } #endif while(SLIST_FIRST(&(sess->trace_packets))) { tp=SLIST_FIRST(&(sess->trace_packets)); SLIST_REMOVE_HEAD(&(sess->trace_packets), next); free(tp); } if(sess->payload) free(sess->payload); if(sess->icmp_packet.packet) free(sess->icmp_packet.packet); if(sess->btcpmap) free(sess->btcpmap); free(sess); } /*---------------------------------------------------------------------------*/ /*Use TCP FIN packets exclusively (defaults are SYN)*/ int LFTSetupFIN(lft_session_params * sess) { if (sess->adaptive) { LFTErrHandler(sess, WRN_CANT_SETUP_FIN, NULL); return 0; } sess->tcp_flags = TH_FIN; sess->use_fins = 1; sess->dport = 25000; sess->auto_ports = 0; return 1; } /*---------------------------------------------------------------------------*/ /* ========================================================================= * IANA Special-Purpose Address Registry lookup (IPv4 + IPv6) * * Sorted most-specific-first so the first match wins. Prefix octets are * in network (big-endian) order; comparison uses ntohl() to work in host * byte order regardless of machine endianness. * ========================================================================= */ static const struct { uint8_t p[4]; /* prefix octets, network order */ int len; /* prefix length in bits */ const char *rfc; const char *name; } g_iana_v4[] = { /* /32 — most specific */ {{ 0, 0, 0, 0}, 32, "RFC1122", "This host on this network"}, {{192, 0, 0, 8}, 32, "RFC7600", "IPv4 dummy address"}, {{192, 0, 0, 9}, 32, "RFC7723", "Port Control Protocol Anycast"}, {{192, 0, 0, 10}, 32, "RFC8155", "Traversal Using Relays around NAT Anycast"}, {{192, 0, 0,170}, 32, "RFC8880", "NAT64/DNS64 Discovery"}, {{192, 0, 0,171}, 32, "RFC8880", "NAT64/DNS64 Discovery"}, {{192, 88, 99, 2}, 32, "RFC6751", "6a44 relay anycast address"}, {{255,255,255,255}, 32, "RFC8190", "Limited Broadcast"}, /* /29 */ {{192, 0, 0, 0}, 29, "RFC7335", "IPv4 Service Continuity Prefix"}, /* /24 */ {{192, 0, 0, 0}, 24, "RFC6890", "IETF Protocol Assignments"}, {{192, 0, 2, 0}, 24, "RFC5737", "Documentation (TEST-NET-1)"}, {{192, 31,196, 0}, 24, "RFC7535", "AS112-v4"}, {{192, 52,193, 0}, 24, "RFC7450", "AMT"}, {{192, 88, 99, 0}, 24, "RFC7526", "Deprecated (6to4 Relay Anycast)"}, {{192,175, 48, 0}, 24, "RFC7534", "Direct Delegation AS112 Service"}, {{198, 51,100, 0}, 24, "RFC5737", "Documentation (TEST-NET-2)"}, {{203, 0,113, 0}, 24, "RFC5737", "Documentation (TEST-NET-3)"}, /* /16 */ {{169,254, 0, 0}, 16, "RFC3927", "Link Local"}, {{192,168, 0, 0}, 16, "RFC1918", "Private-Use"}, /* /15 */ {{198, 18, 0, 0}, 15, "RFC2544", "Benchmarking"}, /* /12 */ {{172, 16, 0, 0}, 12, "RFC1918", "Private-Use"}, /* /10 */ {{100, 64, 0, 0}, 10, "RFC6598", "Shared Address Space"}, /* /8 */ {{ 0, 0, 0, 0}, 8, "RFC791", "This network"}, {{ 10, 0, 0, 0}, 8, "RFC1918", "Private-Use"}, {{127, 0, 0, 0}, 8, "RFC1122", "Loopback"}, /* /4 */ {{240, 0, 0, 0}, 4, "RFC1112", "Reserved"}, }; #define G_IANA_V4_COUNT ((int)(sizeof(g_iana_v4)/sizeof(g_iana_v4[0]))) /* LFT-AUTOTUNE — apply learned-path knowledge to trace knobs. Called by the * caller (currently watch_run_one_cycle) BEFORE each cycle's LFTExecute when * the previous cycle's results are available. Three guards keep this from * doing harm: * 1. sess->no_autotune set → skip entirely (--no-learn flag) * 2. learned.valid == 0 → no prior cycle data, leave defaults * 3. stable_cycles < 3 → path length unstable, don't narrow * 4. last_protocol != current → protocol changed, defaults safer * * When all guards pass, narrow ttl_limit, widen ahead_limit to the full * known path width, and shrink the cloaked-hop timeout. Cap ahead_limit * at 16 to stay friendly to per-source ICMP rate limits. */ void lft_auto_tune_from_learned(lft_session_params *sess) { if (!sess || sess->no_autotune) return; if (!sess->learned.valid) return; if (sess->learned.stable_cycles < 3) return; if (sess->learned.last_protocol != sess->protocol) return; if (sess->learned.num_hops <= 0) return; /* Narrow ttl_limit to known path length plus a small margin so we * still notice if the path lengthens. */ int new_ttl_limit = sess->learned.num_hops + sess->learn_margin; if (new_ttl_limit < 4) new_ttl_limit = 4; if (new_ttl_limit > 30) new_ttl_limit = 30; sess->ttl_limit = new_ttl_limit; /* Widen ahead_limit to do the entire path in one parallel burst, * but cap at 16 to keep ICMP rate-limit triggers at bay. */ int new_ahead = sess->learned.num_hops; if (new_ahead < 4) new_ahead = 4; if (new_ahead > 16) new_ahead = 16; sess->ahead_limit = new_ahead; /* Adaptive timeout: 8 × max RTT seen, floor 200ms. */ int new_timeout = sess->learned.max_rtt_ms * 8; if (new_timeout < 200) new_timeout = 200; if (new_timeout > sess->timeout_ms) new_timeout = sess->timeout_ms; sess->timeout_ms = new_timeout; /* Tighter probe pacing — routers have already seen our flow. */ if (sess->scatter_ms > 5) sess->scatter_ms = 5; } int lft_iana_v4_lookup(struct in_addr addr, char *rfc_out, size_t rfc_sz, char *name_out, size_t name_sz) { uint32_t a = ntohl(addr.s_addr); /* address in host byte order */ int i; for (i = 0; i < G_IANA_V4_COUNT; i++) { const uint8_t *pb = g_iana_v4[i].p; int pl = g_iana_v4[i].len; uint32_t prefix = ((uint32_t)pb[0] << 24) | ((uint32_t)pb[1] << 16) | ((uint32_t)pb[2] << 8) | (uint32_t)pb[3]; uint32_t mask = (pl == 0) ? 0u : (0xFFFFFFFFu << (32 - pl)); if ((a & mask) == (prefix & mask)) { if (rfc_out && rfc_sz > 0) snprintf(rfc_out, rfc_sz, "%s", g_iana_v4[i].rfc); if (name_out && name_sz > 0) snprintf(name_out, name_sz, "%s", g_iana_v4[i].name); return 1; } } return 0; } /* IPv6 special-purpose table — prefix bytes + prefix length in bits */ static const struct { uint8_t p[16]; int len; const char *rfc; const char *name; } g_iana_v6[] = { /* /128 */ {{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1},128,"RFC4291","Loopback"}, {{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},128,"RFC4291","Unspecified Address"}, /* /96 */ {{0,0,0,0, 0,0,0,0, 0,0,0xFF,0xFF,0,0,0,0},96,"RFC4291","IPv4-mapped Address"}, {{0,0x64,0xFF,0x9B, 0,0,0,0, 0,0,0,0, 0,0,0,0},96,"RFC6052","IPv4-IPv6 Translation"}, /* /64 */ {{0,0x64,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},64,"RFC6666","Discard-Only Address Block"}, /* /48 */ {{0x20,0x01,0,0x02, 0,0,0,0, 0,0,0,0, 0,0,0,0},48,"RFC5180","Benchmarking"}, {{0x20,0x01,0,0x04, 0x01,0x12,0,0, 0,0,0,0, 0,0,0,0},48,"RFC7535","AS112-v6"}, {{0x26,0x20,0,0x4F, 0x80,0,0,0, 0,0,0,0, 0,0,0,0},48,"RFC7534","Direct Delegation AS112 Service"}, /* /32 */ {{0x20,0x01,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},32,"RFC4380","TEREDO"}, {{0x20,0x01,0,0x03, 0,0,0,0, 0,0,0,0, 0,0,0,0},32,"RFC7450","AMT"}, {{0x20,0x01,0x0D,0xB8, 0,0,0,0, 0,0,0,0, 0,0,0,0},32,"RFC3849","Documentation"}, /* /28 */ {{0x20,0x01,0,0x10, 0,0,0,0, 0,0,0,0, 0,0,0,0},28,"RFC4843","Deprecated (ORCHID)"}, {{0x20,0x01,0,0x20, 0,0,0,0, 0,0,0,0, 0,0,0,0},28,"RFC7343","ORCHIDv2"}, {{0x20,0x01,0,0x30, 0,0,0,0, 0,0,0,0, 0,0,0,0},28,"RFC9374","Drone Remote ID Entity Tags"}, /* /23 */ {{0x20,0x01,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},23,"RFC2928","IETF Protocol Assignments"}, /* /20 */ {{0x3F,0xFF,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},20,"RFC9637","Documentation"}, /* /16 */ {{0x20,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},16,"RFC3056","6to4"}, {{0x5F,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},16,"RFC9602","Segment Routing (SRv6) SIDs"}, /* /10 */ {{0xFE,0x80,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0},10,"RFC4291","Link-Local Unicast"}, /* /7 */ {{0xFC,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}, 7,"RFC4193","Unique-Local"}, }; #define G_IANA_V6_COUNT ((int)(sizeof(g_iana_v6)/sizeof(g_iana_v6[0]))) int lft_iana_v6_lookup(const struct in6_addr *addr, char *rfc_out, size_t rfc_sz, char *name_out, size_t name_sz) { const uint8_t *ab = addr->s6_addr; int i; for (i = 0; i < G_IANA_V6_COUNT; i++) { const uint8_t *pb = g_iana_v6[i].p; int pl = g_iana_v6[i].len; int whole_bytes = pl / 8; int partial_bits = pl % 8; int match = 1; int j; for (j = 0; j < whole_bytes && match; j++) if (ab[j] != pb[j]) match = 0; if (match && partial_bits) { uint8_t mask = (uint8_t)(0xFF << (8 - partial_bits)); if ((ab[whole_bytes] & mask) != (pb[whole_bytes] & mask)) match = 0; } if (match) { if (rfc_out && rfc_sz > 0) snprintf(rfc_out, rfc_sz, "%s", g_iana_v6[i].rfc); if (name_out && name_sz > 0) snprintf(name_out, name_sz, "%s", g_iana_v6[i].name); return 1; } } return 0; } /*---------------------------------------------------------------------------*/ /*Display hosts symbolically; suppress IP address display*/ int LFTSetupDispSymbHost(lft_session_params * sess) { if(!sess->resolve_names) { LFTErrHandler(sess, WRN_CANT_DISP_HOST_NAMES, NULL); return 0; } sess->hostnames_only = 1; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupUDPMode(lft_session_params * sess) { int prev_protocol; char tmp[30]; prev_protocol = sess->protocol; sess->protocol = 1; if(sess->adaptive) { LFTErrHandler(sess, WRN_ADAPTIVE_DISABLED_BY_UDP, NULL); sess->adaptive = 0; } if(sess->use_fins) { LFTErrHandler(sess, WRN_FIN_DISABLED_BY_UDP, NULL); sess->use_fins = 0; } if(!sess->dflag) sess->dport = start_dport; else { if(!prev_protocol) { sprintf(tmp,"%d",sess->dport); LFTSetupDestinationPort(sess, tmp); } } sess->auto_ports = 0; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupRISLookup(lft_session_params * sess) { int asnlkptp=ASN_LOOKUP_RIS; if(sess->use_cymru || sess->use_radb){ LFTErrHandler(sess, WRN_ONLY_ONE_ASN_LOOKUP, &asnlkptp); return 0; } sess->use_radb = 0; sess->use_ris = 1; sess->use_cymru = 0; sess->do_aslookup = 1; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupRADBLookup(lft_session_params * sess) { int asnlkptp=ASN_LOOKUP_RADB; if(sess->use_cymru || sess->use_ris){ LFTErrHandler(sess, WRN_ONLY_ONE_ASN_LOOKUP, &asnlkptp); return 0; } sess->use_radb = 1; sess->use_ris = 0; sess->use_cymru = 0; sess->do_aslookup = 1; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupCYMRULookup(lft_session_params * sess) { int asnlkptp=ASN_LOOKUP_CYMRU; if(sess->use_radb || sess->use_ris){ LFTErrHandler(sess, WRN_ONLY_ONE_ASN_LOOKUP, &asnlkptp); return 0; } sess->use_radb = 0; sess->use_ris = 0; sess->use_cymru = 1; sess->do_aslookup = 1; return 1; } /*---------------------------------------------------------------------------*/ int lft_resolve_port(lft_session_params * sess, const char *strport) { struct addrinfo hint, *ai; struct sockaddr_in addr; char *end; unsigned long port; /* * Check if this is numeric, if so simply convert. */ port = strtoul(strport, &end, 10); if (*end == '\0') { if (port > 65535) { /* XXX Error. Nothing checks for error, though. */ return (-1); } return (port); } /* * This is a named service, look it up. */ memset(&hint, 0, sizeof hint); hint.ai_family = AF_INET; if (sess->protocol == 1) { hint.ai_socktype = SOCK_DGRAM; hint.ai_protocol = IPPROTO_UDP; /* UDP */ } else { hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = IPPROTO_TCP; /* TCP */ } if (getaddrinfo(NULL, strport, &hint, &ai) != 0) { /* XXX Error. Nothing checks for error, though. */ return (-1); } if (ai->ai_addrlen != sizeof addr) { /* XXX Error. Nothing checks for error, though. */ return (-1); } memcpy(&addr, ai->ai_addr, ai->ai_addrlen); return (ntohs(addr.sin_port)); } /*---------------------------------------------------------------------------*/ int LFTSetupDestinationPort(lft_session_params * sess, char * userport) { /* char strport[50]; sprintf(strport,"%u",userport); */ sess->dflag++; if(sess->protocol==1) { if(lft_resolve_port(sess, userport) > 65534) { sess->dport = 65534; LFTErrHandler(sess, WRN_UDP_PORT_TOO_HIGH, &userport); } else sess->dport = lft_resolve_port(sess, userport); } else { sess->dport = lft_resolve_port (sess, userport); } sess->auto_ports = 0; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupLengthOfPacket(lft_session_params * sess, int plen) { if(plen > maxpacklen) { LFTErrHandler(sess, WRN_PACKET_LENGTH_TOO_HIGH, &plen); sess->userlen = maxpacklen; } else if(plen < minpacklen) { LFTErrHandler(sess, WRN_PACKET_LENGTH_TOO_LOW, &plen); sess->userlen = 0; } else sess->userlen = plen; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupDisableResolver(lft_session_params * sess) { if(!sess->hostnames_only) { sess->resolve_names = 0; } else { LFTErrHandler(sess, WRN_CANT_DISABLE_RESOLVER, NULL); } return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupSourcePort(lft_session_params * sess, int port) { if(sess->random_source) { LFTErrHandler(sess, WRN_ALREADY_RANDOM_SPORT, NULL); return 0; } sess->sport = port; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupAdaptiveMode(lft_session_params * sess) { if (sess->protocol==1) { LFTErrHandler(sess, WRN_ADAPTIVE_DISABLED_BY_UDP, NULL); return 0; } if (sess->use_fins) { LFTErrHandler(sess, WRN_ADAPTIVE_DISABLED_BY_FIN, NULL); return 0; } sess->adaptive = 1; sess->break_on_icmp = 0; /* adaptive probes through firewalls; don't stop on unreachables */ return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupDevice(lft_session_params * sess,char * udev) { if (strlen (udev) > max_net_dev_input) { LFTErrHandler(sess, ERR_DEVNAME_TOO_LONG, NULL); return 0; } sess->userdevsel = 1; sess->userdev = udev; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupSendDevice(lft_session_params * sess,char * sdev) { if (strlen (sdev) > max_net_dev_input) { LFTErrHandler(sess, ERR_DEVNAME_TOO_LONG, NULL); return 0; } sess->senddevsel = 1; sess->senddev = sdev; return 1; } /*---------------------------------------------------------------------------*/ int LFTSetupUTCTimes(lft_session_params * sess) { #if defined(sun) if (putenv("TZ=GMT0") == -1) { LFTErrHandler(sess, WRN_UNABLE_SETUP_UTC, NULL); } #else #if !defined(WIN32) && !defined(_WIN32) if (setenv("TZ", "GMT0", 1) == -1) { LFTErrHandler(sess, WRN_UNABLE_SETUP_UTC, NULL); } #endif #endif sess->UseLocalTime=0; sess->timetrace = 1; return 1; } /*---------------------------------------------------------------------------*/ /* Refactored part of code */ /*---------------------------------------------------------------------------*/ unsigned int new_seq(lft_session_params * sess) { if (sess->adaptive) { return rand(); } else { return sess->seq_start + sess->trace_packets_num; } } /*---------------------------------------------------------------------------*/ #ifndef SCREWED_IP_LEN u_int32_t ip_cksum (const struct ip *ip) { register const u_short *sp = (u_short *) ip; register u_int32_t sum = 0; register int count; /* * No need for endian conversions. */ for (count = ip->ip_hl * 2; --count >= 0;) sum += *sp++; while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum = ~sum & 0xffff; return (sum); } #endif /*---------------------------------------------------------------------------*/ /* A standardized way of calculating checksums */ static unsigned short udp_cksum (unsigned short *addr, signed int len) { unsigned short answer = 0; register unsigned short *w = addr; register int nleft = len; register int sum = 0; while(nleft > 1) { sum += *w++; nleft -= 2; } if(nleft == 1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = (sum>>16) + (sum&0xffff); sum += (sum>>16); answer = ~sum; return (answer); } /*---------------------------------------------------------------------------*/ u_int32_t tcp_cksum (struct ip *ip, struct tcphdr *tcp, const char * payload, int payload_len) { u_int32_t sum = 0; register int count; u_short *sp; u_short *temptcp = (u_short *)malloc(sizeof(struct tcphdr)+payload_len+4); u_int32_t tempip; memset(temptcp, 0,sizeof(struct tcphdr)+payload_len+4); memcpy(temptcp, (char *)tcp, sizeof(struct tcphdr)); memcpy((u_char *)temptcp+sizeof(struct tcphdr),payload,payload_len); sp = temptcp; for(count = (sizeof(struct tcphdr)+payload_len+(sizeof(struct tcphdr)+payload_len)%2)/2; --count >= 0;) sum += *sp++; free(temptcp); memcpy(&tempip, &ip->ip_src, sizeof ip->ip_src); sum += (tempip >> 16) + (tempip & 0xffff); memcpy(&tempip, &ip->ip_dst, sizeof ip->ip_dst); sum += (tempip >> 16) + (tempip & 0xffff); sum += ip->ip_p << 8; sum += htons (sizeof (struct tcphdr)+payload_len); while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum = ~sum & 0xffff; return (sum); } /*---------------------------------------------------------------------------*/ static char * strtolower (char *input) { char *iter = input; if (input == NULL) return NULL; while (*iter) { *iter = tolower (*iter); iter++; } return input; } /*---------------------------------------------------------------------------*/ static void do_auto_ports (lft_session_params * sess, char *hostname, int init_dport) { /* * Provides rudimentary auto-select of source and destination ports * based on parsing the hostname supplied by the user * * EXPECTS a string like "mail.example.com" and RETURNS the * auto-selected src and dst ports if changed or NULL if unchanged * * Currently operates on user's input only as a call to * getnameinfo() before tracing could slow us down too much */ if (strlen(hostname) > 5) { const char *mailservers[] = { "mail", "smtp", "mx", "relay", "inbound" }; int cnt = 0; hostname = (char *) strtolower(hostname); for (cnt = 0 ; cnt < 5 ; cnt++) { if (strstr(hostname,mailservers[cnt])) { sess->dport = 25; sess->sport = 52025; break; } } if (strncmp (hostname,"ftp",3) == 0) { sess->dport = 21; sess->sport = 52021; } else if (strncmp (hostname,"whois",5) == 0) { sess->dport = 43; sess->sport = 52043; } else if (strncmp (hostname,"imap",4) == 0) { sess->dport = 143; sess->sport = 52143; } else if (strncmp (hostname,"pop",3) == 0) { sess->dport = 110; sess->sport = 52110; } else if ((strncmp (hostname,"dns",3) == 0) || (strncmp (hostname,"ns",2) == 0) || (strncmp (hostname,"udns",4) == 0)) { sess->dport = 53; sess->sport = 53; } else if ((strncmp (hostname,"ntp",3) == 0) || (strncmp (hostname,"clock",5) == 0)) { sess->dport = 123; sess->sport = 123; } else if ((strncmp (hostname,"ike",3) == 0) || (strncmp (hostname,"isakmp",6) == 0)) { sess->dport = 500; sess->sport = 500; } else if ((strncmp (hostname,"ike",3) == 0) || (strncmp (hostname,"isakmp",6) == 0) || (strncmp (hostname,"ipsec",5) == 0)) { sess->dport = 500; sess->sport = 500; } if (sess->noisy && (sess->dport != init_dport)) LFTEvtHandler(sess,EVT_AUTOCONFIGURED_TO_PORTS, NULL); } } /*---------------------------------------------------------------------------*/ unsigned int get_address(lft_session_params * sess, const char *host) { struct addrinfo hint, *ai; struct sockaddr_in addr; /* * Check if this is numeric, if so simply convert. */ if (inet_aton(host, &addr.sin_addr) == 1) return (addr.sin_addr.s_addr); /* * This is a named host, look it up. */ memset(&hint, 0, sizeof hint); hint.ai_family = AF_INET; if (getaddrinfo(host, NULL, &hint, &ai) != 0) { LFTErrHandler(sess, ERR_UNKNOWN_HOST, host); return (0); } if (ai->ai_addrlen != sizeof addr) { /* * XXX * Is there a better error? */ LFTErrHandler(sess, ERR_UNKNOWN_HOST, host); return (0); } memcpy(&addr, ai->ai_addr, ai->ai_addrlen); return (addr.sin_addr.s_addr); } /*---------------------------------------------------------------------------*/ #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) /* The Windows/Cygwin version of this function uses the winsock built-in WSAIoctl SIO_ROUTING_INTERFACE_QUERY to find the appropriate interface. */ static char * lft_getifforremote(lft_session_params * sess, const char *remote) { struct sockaddr_in in, out; unsigned long nBytesReturned; /* Only do this once of course */ if (sock < 0) { if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { LFTErrHandler(sess, WRN_GETIFFORREMOTE_SOCKET, NULL); if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return NULL; } } /* Ask what i/f the packet should go out on */ in.sin_family = AF_INET; in.sin_port = 0; in.sin_addr.s_addr = get_address(sess, (char *)remote); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return NULL; } if (SOCKET_ERROR != WSAIoctl(sock, SIO_ROUTING_INTERFACE_QUERY, &in, sizeof(in), &out, sizeof(out), &nBytesReturned, NULL, NULL)) return lft_getifname(out.sin_addr); /* not found */ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return NULL; } #else /* The non-Windows version of this function uses connect() to acquire a (UDP) socket to the target and uses the OS's decision as to what interface is appropriate. */ static char * lft_getifforremote(lft_session_params * sess, const char *remote) { int sd; struct sockaddr_in sock; uint32_t socklen; uint16_t p1; p1 = rand()%16384+49152; if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { LFTErrHandler(sess, WRN_GETIFFORREMOTE_SOCKET, NULL); if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return NULL; } sock.sin_family = AF_INET; sock.sin_addr.s_addr = get_address(sess, remote); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return NULL; } sock.sin_port = htons(p1); if (connect(sd, (const struct sockaddr *)(const void *)&sock, sizeof sock) == -1) { LFTErrHandler(sess, WRN_GETIFFORREMOTE_CONNECT, NULL); #if defined(WIN32) || defined(_WIN32) closesocket(sd); #else close(sd); #endif if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return NULL; } socklen = sizeof sock; if (getsockname(sd, (struct sockaddr *)(void *)&sock, &socklen) == -1) { LFTErrHandler(sess, WRN_GETIFFORREMOTE_SOCKNAME, NULL); #if defined(WIN32) || defined(_WIN32) closesocket(sd); #else close(sd); #endif if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return NULL; } #if defined(WIN32) || defined(_WIN32) closesocket(sd); #else close(sd); #endif return lft_getifname(sock.sin_addr); } #endif /*---------------------------------------------------------------------------*/ static void init_address (lft_session_params * sess, char *remote, const char *pcap_dev) { (void)pcap_dev; /* this is now set inside device selection routines */ /* sess->local_address.s_addr = lft_getifaddr (pcap_dev); */ if (sess->noisy) { LFTEvtHandler(sess, EVT_ADDRESS_INITIALIZED, NULL); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return; } } sess->remote_address.s_addr = get_address(sess, remote); } /*---------------------------------------------------------------------------*/ /* RFC 1191 plateau table — used when a router sends PTB with nextmtu == 0 */ static const uint16_t pmtud_plateaus[] = { 65535, 32000, 17914, 8166, 4352, 2002, 1492, 1006, 508, 296, 68, 0 }; /* Return the next plateau value strictly below current_mtu, or 68 (minimum). */ static uint16_t lft_pmtud_next_plateau(uint16_t current_mtu) { int i; for (i = 0; pmtud_plateaus[i] != 0; i++) { if (pmtud_plateaus[i] < current_mtu) return pmtud_plateaus[i]; } return 68; } /* * Handle an ICMP "fragmentation needed" (type 3, code 4) PTB response. * Called from process_packet() and tcp_base_recv_packet() BEFORE recv_packet() * so the hop is NOT marked as answered — probing continues with a smaller packet. * * seq — TCP seq or UDP dport value from the inner quoted packet * from — IP address of the router that sent the PTB */ void lft_pmtud_handle_ptb(lft_session_params *sess, const struct icmp *icmp, unsigned int seq, struct in_addr from) { struct trace_packet_info_s *tp = NULL; uint16_t next_mtu, new_mtu; int nhop; int hdr_size; /* Find the probe that triggered this PTB */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if (tp->seq == seq) break; } if (tp == NULL) return; /* not our probe */ nhop = tp->hopno; if (nhop < 0 || nhop >= 256) return; /* Determine the new MTU */ next_mtu = ntohs(icmp->icmp_nextmtu); if (next_mtu > 0 && next_mtu < sess->pmtud_current_mtu) { new_mtu = next_mtu; } else { /* Pre-RFC-1191 router or bogus value: step down via plateau table */ new_mtu = lft_pmtud_next_plateau(sess->pmtud_current_mtu); } /* Guard: must be a real reduction and >= minimum IP packet */ if (new_mtu >= sess->pmtud_current_mtu || new_mtu < 68) return; /* Record the MTU limit at the responding hop */ sess->hop_info[nhop].path_mtu = new_mtu; sess->pmtud_current_mtu = new_mtu; sess->pmtud_ptb_mtu = new_mtu; if (sess->noisy > 1 && !global_output_style) printf("RCVD PTB SRC=%s SEQ=%u NEXTMTU=%u NEWMTU=%u\n", inet_ntoa(from), seq, (unsigned)next_mtu, (unsigned)new_mtu); /* Resize the probe template — payload shrinks, IP length shrinks */ if (sess->protocol == 1) hdr_size = (int)(sizeof(struct ip) + sizeof(struct udphdr)); else hdr_size = (int)(sizeof(struct ip) + sizeof(struct tcphdr)); if (sess->trace_packet.lsrr.ipl_len > 0) hdr_size += sess->trace_packet.lsrr.ipl_len + 1; if ((int)new_mtu <= hdr_size) return; /* degenerate — can't shrink further */ sess->trace_packet.payload_len = (int)new_mtu - hdr_size; /* ip_len is stored in network byte order after open_sockets() finishes */ #ifdef SCREWED_IP_LEN sess->trace_packet.ip_hdr.ip_len = (u_short)new_mtu; #else sess->trace_packet.ip_hdr.ip_len = htons((u_short)new_mtu); #endif (void)from; /* recorded via tp->hopno; kept for future use */ } /* * Handle an ICMP PTB for ICMP probe mode (-p). * Same logic as lft_pmtud_handle_ptb() but resizes via sess->userlen (the * total desired packet size picked up by generateICMPPacket()) rather than * the TCP/UDP trace_packet template fields. * * seq — ICMP echo sequence number from the inner quoted echo request * from — IP address of the router that sent the PTB */ void lft_pmtud_icmp_handle_ptb(lft_session_params *sess, const struct icmp *icmp, unsigned int seq, struct in_addr from) { struct trace_packet_info_s *tp = NULL; uint16_t next_mtu, new_mtu; int nhop; /* ICMP probe header: IP header + ICMP echo header */ const int hdr_size = (int)(sizeof(struct ip) + sizeof(struct icmp_echo_header_s)); /* Find the probe that triggered this PTB */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if (tp->seq == seq) break; } if (tp == NULL) return; /* not our probe */ nhop = tp->hopno; if (nhop < 0 || nhop >= 256) return; /* Determine the new MTU */ next_mtu = ntohs(icmp->icmp_nextmtu); if (next_mtu > 0 && next_mtu < sess->pmtud_current_mtu) { new_mtu = next_mtu; } else { /* Pre-RFC-1191 router or bogus value: step down via plateau table */ new_mtu = lft_pmtud_next_plateau(sess->pmtud_current_mtu); } /* Guard: must be a real reduction and >= minimum IP packet */ if (new_mtu >= sess->pmtud_current_mtu || new_mtu < 68) return; /* Record the MTU limit at the responding hop */ sess->hop_info[nhop].path_mtu = new_mtu; sess->pmtud_current_mtu = new_mtu; sess->pmtud_ptb_mtu = new_mtu; if (sess->noisy > 1 && !global_output_style) printf("RCVD PTB SRC=%s SEQ=%u NEXTMTU=%u NEWMTU=%u\n", inet_ntoa(from), seq, (unsigned)next_mtu, (unsigned)new_mtu); if ((int)new_mtu <= hdr_size) return; /* degenerate — can't shrink further */ /* Resize ICMP probe via sess->userlen (picked up by generateICMPPacket) */ sess->userlen = (int)new_mtu; } /* * Enable active Path MTU Discovery. Called from lft.c when -K / --mtu is seen. * The actual probe size is set in open_sockets() once the interface is known. */ int LFTSetupPMTUD(lft_session_params *sess) { sess->pmtud = 1; sess->pmtud_current_mtu = 1500; /* updated in open_sockets() via SIOCGIFMTU */ sess->pmtud_initial_mtu = 1500; /* updated in open_sockets() via SIOCGIFMTU */ sess->pmtud_ptb_pending = 0; sess->pmtud_ptb_mtu = 0; return 0; } /*---------------------------------------------------------------------------*/ int LFTSetupRTTStats(lft_session_params *sess, int n) { if (n < 1) n = 1; sess->rtt_stats = n; return 0; } /*---------------------------------------------------------------------------*/ static void open_sockets (lft_session_params * sess) { struct timeval tforid; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) int optval = 1; DWORD dwBytesRet = 2048; int rmem = 120832; int wmem = 120832; BOOL blnFlag=TRUE; int ioctlret; #endif #ifdef IP_HDRINCL int on = 1; #endif int i; #if defined(sun) || defined(__CYGWIN__) || defined( WIN32 ) || defined(_WIN32) struct sockaddr_in local_bind; #endif #ifdef HAVE_NCURSES /* In watch mode, the raw socket was opened as root before privilege drop * and is reused across all sub-session cycles. Skip creation if already open. */ if (sess->send_sock <= 0) #endif { #if defined(sun) sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_IP); #elif defined(BSD_IP_STACK) switch(sess->protocol) { case 0: /*TCP*/ case 4: /*TCP basic*/ sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_TCP); break; case 1: /*UDP*/ sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_UDP); break; case 2: /*ICMP basic*/ case 3: /*RFC1393*/ sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); break; } #elif defined(__CYGWIN__) || defined( WIN32 ) || defined(_WIN32) switch(sess->protocol) { case 0: /*TCP*/ case 1: /*UDP*/ case 4: /*TCP basic*/ sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); break; case 2: /*ICMP basic*/ case 3: /*RFC1393*/ sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP); break; } sess->recv_sock = socket (PF_INET, SOCK_RAW, IPPROTO_IP); if (sess->recv_sock < 0) { LFTErrHandler(sess, ERR_RAW_SOCKET, NULL); return; } local_bind.sin_addr = sess->local_address; local_bind.sin_port = 0; local_bind.sin_family = AF_INET; if (bind(sess->recv_sock, (const struct sockaddr *)(const void *)&local_bind, sizeof(local_bind)) < 0) { LFTErrHandler(sess, ERR_SOCKET_BIND, NULL); return; } /* apparently the cygwin include files don't define this: */ #ifndef SIO_RCVALL # define SIO_RCVALL 0x98000001 #endif ioctlret = WSAIoctl(sess->recv_sock, SIO_RCVALL, &optval, sizeof(optval), NULL, 0, &dwBytesRet, NULL, NULL); if (ioctlret < 0) { LFTErrHandler(sess, WRN_WSAIOCTL, NULL); if(sess->exit_state<0) return; } #else switch(sess->protocol) { case 0: /*TCP*/ case 1: /*UDP*/ case 4: /*TCP basic*/ sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); break; case 2: /*ICMP basic*/ case 3: /*RFC1393*/ sess->send_sock = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP); break; } #endif if (sess->send_sock < 0) { LFTErrHandler(sess, ERR_RAW_SOCKET, NULL); if(sess->wsess) { free(sess->wsess); sess->wsess = NULL; } return; } #ifdef IP_HDRINCL if (setsockopt (sess->send_sock, IPPROTO_IP, IP_HDRINCL, (char *) &on, sizeof (on)) < 0) { LFTErrHandler(sess, ERR_IP_HDRINCL, NULL); return; } #endif #ifdef sun local_bind.sin_addr = sess->local_address; local_bind.sin_port = 0; local_bind.sin_family = AF_INET; if (bind (sess->send_sock, (const struct sockaddr *)&local_bind, sizeof (local_bind)) < 0) { LFTErrHandler(sess, ERR_SOCKET_BIND, NULL); return; } #endif } /* end if (sess->send_sock <= 0) */ #ifndef IPDEFTTL #define IPDEFTTL 200 #endif /* if(sess->protocol==4) { if(sess->userlen == 0) sess->userlen=def_payload_len; if(!(sess->payload = malloc(sess->userlen))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); return; } memset(sess->payload, 0,sess->userlen); sess->trace_packet.payload_len = sess->userlen; sess->payloadlen = sess->trace_packet.payload_len; sess->trace_packet.payload = sess->payload; } else */ if(sess->protocol==2 || sess->protocol==3) /*ICMP base or ICMP RFC 1393*/ { /* PMTUD: query interface MTU and set initial probe size */ if (sess->pmtud) { #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) if (sess->pcap_dev) { struct ifreq pmtu_ifr; int pmtu_fd = socket(AF_INET, SOCK_DGRAM, 0); if (pmtu_fd >= 0) { memset(&pmtu_ifr, 0, sizeof(pmtu_ifr)); strncpy(pmtu_ifr.ifr_name, sess->pcap_dev, IFNAMSIZ - 1); if (ioctl(pmtu_fd, SIOCGIFMTU, &pmtu_ifr) == 0 && pmtu_ifr.ifr_mtu >= 68 && pmtu_ifr.ifr_mtu <= 65535) { sess->pmtud_current_mtu = (uint16_t)pmtu_ifr.ifr_mtu; sess->pmtud_initial_mtu = sess->pmtud_current_mtu; } close(pmtu_fd); } } #endif sess->userlen = (int)sess->pmtud_current_mtu; } u_short ptr2id[sizeof tforid / sizeof (u_short)]; u_short icmpid=0; unsigned int ptridx; gettimeofday(&tforid,NULL); memcpy(ptr2id, &tforid, sizeof ptr2id); for(ptridx=0;ptridxicmp_packet, 1, ptr2id[0], 1); } else /*TCP and UDP*/ { /* PMTUD: query interface MTU and set initial probe size to fill the path */ if (sess->pmtud) { #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) if (sess->pcap_dev) { struct ifreq pmtu_ifr; int pmtu_fd = socket(AF_INET, SOCK_DGRAM, 0); if (pmtu_fd >= 0) { memset(&pmtu_ifr, 0, sizeof(pmtu_ifr)); strncpy(pmtu_ifr.ifr_name, sess->pcap_dev, IFNAMSIZ - 1); if (ioctl(pmtu_fd, SIOCGIFMTU, &pmtu_ifr) == 0 && pmtu_ifr.ifr_mtu >= 68 && pmtu_ifr.ifr_mtu <= 65535) { sess->pmtud_current_mtu = (uint16_t)pmtu_ifr.ifr_mtu; sess->pmtud_initial_mtu = sess->pmtud_current_mtu; } close(pmtu_fd); } } #endif /* Override userlen so payload construction sets probe size to MTU */ sess->userlen = (int)sess->pmtud_current_mtu; } /* Prepare the trace packet (probe) */ memset (&(sess->trace_packet), 0, sizeof (sess->trace_packet)); if (sess->userlen == 0 && (sess->protocol==0 || sess->protocol==4)) { /* If user doesn't supply length, default to zero-payload for TCP packets */ sess->trace_packet.payload_len = 0; sess->trace_packet.payload = NULL; } else if (sess->userlen == 0) { if(!(sess->payload = malloc(def_payload_len))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); return; } memset(sess->payload, 0, def_payload_len); sess->trace_packet.payload_len = def_payload_len; } else { if (sess->hostname_lsrr_size > 0) { if (sess->protocol==1) sess->userlen -= (sizeof (struct ip) + sizeof(struct udphdr) + sess->trace_packet.lsrr.ipl_len + 1); else sess->userlen -= (sizeof (struct ip) + sizeof(struct tcphdr) + sess->trace_packet.lsrr.ipl_len + 1); } else { if (sess->protocol==1) sess->userlen -= (sizeof (struct ip) + sizeof(struct udphdr)); else sess->userlen -= (sizeof (struct ip) + sizeof(struct tcphdr)); } if (!(sess->payload = malloc(sess->userlen+4))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); return; } memset(sess->payload, 0, sess->userlen+4); sess->trace_packet.payload_len = sess->userlen; } sess->payloadlen = sess->trace_packet.payload_len; sess->trace_packet.payload = sess->payload; /* set up initial ip headers, etc. */ if (sess->hostname_lsrr_size > 0) { for (i = 0; i < sess->hostname_lsrr_size; i++) { sess->trace_packet.lsrr.data[i] = get_address(sess, sess->hostname_lsrr[i]); if(sess->exit_state<0) { if(sess->payload) { free(sess->payload); sess->payload=NULL; } return; } } sess->trace_packet.lsrr.ipl_code = IPOPT_LSRR; sess->trace_packet.lsrr.ipl_len = sess->hostname_lsrr_size * 4 + 3; sess->trace_packet.lsrr.ipl_ptr = 4; } sess->trace_packet.ip_hdr.ip_v = 4; if (sess->hostname_lsrr_size > 0) { sess->trace_packet.ip_hdr.ip_hl = 6 + sess->hostname_lsrr_size; /* 5 + 3byte lsrr + addresses + padding */ if (sess->protocol==1) sess->trace_packet.ip_hdr.ip_len = sizeof (struct ip) + sizeof(struct udphdr) + sess->trace_packet.lsrr.ipl_len + 1 + sess->trace_packet.payload_len; else sess->trace_packet.ip_hdr.ip_len = sizeof (struct ip) + sizeof(struct tcphdr) + sess->trace_packet.lsrr.ipl_len + 1 + sess->trace_packet.payload_len; } else { sess->trace_packet.ip_hdr.ip_hl = 5; if (sess->protocol==1) sess->trace_packet.ip_hdr.ip_len = sizeof (struct ip) + sizeof(struct udphdr) + sess->trace_packet.payload_len; else sess->trace_packet.ip_hdr.ip_len = sizeof (struct ip) + sizeof(struct tcphdr) + sess->trace_packet.payload_len; } sess->trace_packet.ip_hdr.ip_off = IP_DF; #ifdef SCREWED_IP_LEN /* trace_packet.ip_hdr.ip_len = sizeof (struct ip) + sizeof(struct tcphdr); */ #else sess->trace_packet.ip_hdr.ip_len = htons (sess->trace_packet.ip_hdr.ip_len); sess->trace_packet.ip_hdr.ip_off = htons(sess->trace_packet.ip_hdr.ip_off); #endif sess->trace_packet.ip_hdr.ip_ttl = IPDEFTTL; if (sess->protocol==1) sess->trace_packet.ip_hdr.ip_p = IPPROTO_UDP; else { sess->trace_packet.ip_hdr.ip_p = IPPROTO_TCP; if (sess->set_tos) sess->trace_packet.ip_hdr.ip_tos = TOSMINDELAY; sess->trace_packet.tcp_hdr.th_win = htons (sess->win_len); sess->trace_packet.tcp_hdr.th_off = sizeof (struct tcphdr) / 4; } } /* * Init hop storage (array) * Init global packet info storage (SLIST) * Init per-hop packet info storage (SLIST) */ sess->hop_info = (struct hop_info_s *)malloc (sizeof(struct hop_info_s) * hop_info_size); for (i = 0; i < 256; i++) { memset(&(sess->hop_info[i]), 0, sizeof(struct hop_info_s)); sess->hop_info[i].state = 0; sess->hop_info[i].done_packet = NULL; SLIST_INIT(&(sess->hop_info[i].packets)); } SLIST_INIT(&(sess->trace_packets)); } /*---------------------------------------------------------------------------*/ static unsigned int send_packet (lft_session_params * sess, short nhop, unsigned short ttl, unsigned int seq, unsigned char flags) { struct sockaddr_in dest; unsigned int tseq=0; unsigned short tttl=0; u_short udp_ip_id=0; /* probe ID for UDP mode, embedded in IP ID field */ struct sumh sum; char *buf; char *bptr = NULL; int blen = 0; char *s; EvtSentPacketParam espparam; /* we'll use gettimeofday() from checktimeouts * instead of doing it again here */ /* gettimeofday (&now, NULL); */ struct trace_packet_info_s *pinfo = NULL; struct trace_packet_s *packet = NULL; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) buf=(char *)_alloca(maxpacklen+64); #else buf=(char *)alloca(maxpacklen+64); #endif if (!(pinfo = (struct trace_packet_info_s *)malloc(sizeof(struct trace_packet_info_s)))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); return 0; } memset(pinfo, 0, sizeof(struct trace_packet_info_s)); packet = &(pinfo->u.packet); memcpy(packet, &(sess->trace_packet), sizeof(struct trace_packet_s)); bptr = buf; dest.sin_family = AF_INET; dest.sin_addr = sess->remote_address; dest.sin_port = 0; if (seq == 0) tseq = new_seq(sess); else tseq = seq; if (nhop == -1) tttl = ttl; else tttl = nhop + 1; /*There is no place we use this variable*/ /*sess->num_sent++;*/ sess->ts_last_sent = sess->now; packet->ip_hdr.ip_ttl = tttl; packet->ip_hdr.ip_src = sess->local_address; packet->ip_hdr.ip_dst = sess->remote_address; /* UDP: encode probe sequence in the IP Identification field so each * probe can be matched to its TTL-exceeded reply via the echoed inner * IP header. The destination and source ports are kept constant for * all probes, giving every probe the same 5-tuple and therefore the * same ECMP hash. IP ID is not part of the ECMP hash, so varying * it here does not affect path selection. */ if (sess->protocol == 1) { /* Use the low 16 bits of tseq as a pseudorandom probe ID embedded in * the IP ID field. tseq starts from a random seq_start (same as the * TCP ISN mechanism) so the ID is unpredictable and session-unique. * Avoid ip_id=0: some kernels replace a zero IP ID in raw socket sends * with their own counter, which would break probe matching on reply. */ udp_ip_id = (u_short)(tseq & 0xFFFF); if (!udp_ip_id) udp_ip_id = 0xFFFF; packet->ip_hdr.ip_id = htons(udp_ip_id); } espparam.flags=flags; espparam.nhop=nhop; espparam.tseq=tseq; espparam.tttl=tttl; lft_o_probe_sent(); /* spinner: count in-flight probe */ if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_SENT_PACKET, &espparam); if(sess->exit_state<0) { free(pinfo); return 0; } } packet->ip_hdr.ip_sum = 0; #if !defined(SCREWED_IP_LEN) packet->ip_hdr.ip_sum = ip_cksum(&packet->ip_hdr); #endif memcpy(bptr, &(packet->ip_hdr), sizeof(struct ip)); bptr += sizeof(struct ip); if (packet->lsrr.ipl_len > 0) { memcpy(bptr, &(packet->lsrr), packet->lsrr.ipl_len + 1); bptr += (packet->lsrr.ipl_len + 1); /* PADDING !!! */ } /* Layer-4 preparation */ if (sess->protocol==1) { if (sess->noisy > 5) { LFTEvtHandler(sess,EVT_SHOW_PAYLOAD, packet); if(sess->exit_state<0) { free(pinfo); return 0; } } /* Construct UDP (with payload). * Both ports are fixed for the entire session so that all probes * share the same 5-tuple and are forwarded by load balancers on * the same path. The probe sequence is tracked via the IP ID * field (set above before ip_sum) rather than the destination port. */ packet->udp_hdr.uh_dport = htons(sess->dport); packet->udp_hdr.uh_sport = htons (sess->sport); packet->udp_hdr.uh_ulen = htons ((sizeof (struct udphdr)) + packet->payload_len); /* truncated-udplength 0 packet->udp_hdr.uh_ulen = 0; packet->udp_hdr.th_flags = flags; */ /* create pseudo-header for checksum calculation */ sum.src=sess->local_address.s_addr; sum.dst=sess->remote_address.s_addr; sum.fill=0; sum.protocol=IPPROTO_UDP; sum.len=htons(sizeof(struct udphdr) + packet->payload_len); if(!( s = (char *)malloc(sizeof(struct sumh)+sizeof(struct udphdr)+packet->payload_len))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); free(pinfo); return 0; } memset(s,0,(sizeof(struct sumh)+sizeof(struct udphdr)+packet->payload_len)); memcpy(s,&sum,sizeof(struct sumh)); memcpy(s+sizeof(struct sumh),&(packet->udp_hdr),sizeof(struct udphdr)); memcpy(s+sizeof(struct sumh)+sizeof(struct udphdr), packet->payload,packet->payload_len); packet->udp_hdr.uh_sum=udp_cksum((unsigned short *)(void *)s, sizeof(struct sumh)+sizeof(struct udphdr)+packet->payload_len); free(s); if (sess->noisy > 5) { LFTEvtHandler(sess,EVT_SHOW_UDP_CHECKSUM, packet); if(sess->exit_state<0) { free(pinfo); return 0; } } #if defined(SOLARIS_LENGTH_IN_CHECKSUM) packet->udp_hdr.uh_sum = htons (sizeof (struct udphdr) + packet->payload_len); #endif memcpy(bptr, &(packet->udp_hdr), sizeof(struct udphdr)); bptr += sizeof(packet->udp_hdr); memcpy(bptr, packet->payload, packet->payload_len); blen = sizeof(struct ip) + packet->lsrr.ipl_len + sizeof(struct udphdr) + packet->payload_len; } else { /* Construct TCP (no payload needed) */ if (sess->noisy > 5) { LFTEvtHandler(sess,EVT_SHOW_PAYLOAD, packet); if(sess->exit_state<0) { free(pinfo); return 0; } } packet->tcp_hdr.th_dport = htons (sess->dport); /* trace_packet.tcp_hdr.th_seq = htonl (seq_start + trace_packet_info_length); */ packet->tcp_hdr.th_seq = htonl (tseq); packet->tcp_hdr.th_sport = htons (sess->sport); packet->tcp_hdr.th_flags = flags; #if defined(SOLARIS_LENGTH_IN_CHECKSUM) packet->tcp_hdr.th_sum = htons (sizeof (struct tcphdr)) + packet->payload_len; #else packet->tcp_hdr.th_sum = 0; packet->tcp_hdr.th_sum = tcp_cksum(&packet->ip_hdr, &packet->tcp_hdr, packet->payload, packet->payload_len); #endif if (sess->noisy > 5) { LFTEvtHandler(sess,EVT_SHOW_TCP_CHECKSUM, packet); if(sess->exit_state<0) { free(pinfo); return 0; } } memcpy(bptr, &(packet->tcp_hdr), sizeof(struct tcphdr)); bptr += sizeof(packet->tcp_hdr); memcpy(bptr, packet->payload, packet->payload_len); blen = sizeof(struct ip) + packet->lsrr.ipl_len + sizeof(struct tcphdr) + packet->payload_len; } /* Packet is ready, fire away */ if (sendto (sess->send_sock, buf, blen, 0, (const struct sockaddr *)(const void *)&dest, sizeof (dest)) < 0) { LFTErrHandler(sess, ERR_RAW_TCP_DISABLED, NULL); free(pinfo); return 0; } pinfo->hopno = nhop; if (sess->protocol==1) pinfo->seq = udp_ip_id; /* matches ip->ip_id in the echoed inner header */ else if(!sess->protocol) pinfo->seq = tseq; pinfo->sent = sess->now; SLIST_INSERT_HEAD(&(sess->trace_packets), pinfo, next); sess->trace_packets_num++; if (nhop != -1) { SLIST_INSERT_HEAD(&(sess->hop_info[nhop].packets), pinfo, next_by_hop); sess->hop_info[nhop].num_sent++; sess->hop_info[nhop].all_sent++; sess->hop_info[nhop].ts_last_sent = sess->now; } return tseq; } /*---------------------------------------------------------------------------*/ static unsigned int send_hop (lft_session_params * sess, short nhop) { WrnBadHopStateParam wbhsp; struct hop_info_s *h = &(sess->hop_info[nhop]); if (!sess->adaptive) return send_packet (sess, nhop , 0, 0, sess->tcp_flags); if (h->state == HS_SEND_FIN) { return send_packet(sess, nhop, 0, 0, TH_FIN); } if (h->state == HS_SEND_SYN) { return send_packet(sess, nhop, 0, 0, TH_SYN); } if (h->state == HS_SEND_SYN_ACK) { return send_packet(sess, nhop, 0, 0, HS_SEND_SYN_ACK); } wbhsp.h=h; wbhsp.nhop=nhop; LFTErrHandler(sess, WRN_BAD_HOP_STATE, &wbhsp); return -1; } /*---------------------------------------------------------------------------*/ int hop_state_up (lft_session_params * sess, short nhop) { struct hop_info_s *h = &(sess->hop_info[nhop]); if (h->state == HS_MAX) return -1; /* 1st try FIN, then SYN_ACK, then SYN, then fail (set to MAX) */ if (h->state == HS_SEND_FIN) h->state = HS_SEND_SYN_ACK; else if (h->state == HS_SEND_SYN_ACK) h->state = HS_SEND_SYN; else h->state = HS_MAX; h->num_sent = 0; /* for this state that is */ return 0; } /*---------------------------------------------------------------------------*/ int hop_state_copy(lft_session_params * sess, short nhop) { int i; if (sess->noisy > 4) LFTEvtHandler(sess,EVT_SHOW_HOPS, &nhop); if(sess->exit_state>=0) { for (i = (nhop+1); i <= 255; i++) if (sess->hop_info[i].state < sess->hop_info[nhop].state) { sess->hop_info[i].state = sess->hop_info[nhop].state; sess->hop_info[i].num_sent = 0; } } return 0; } /*---------------------------------------------------------------------------*/ /* Returns 1 if every received packet at hop 'hopno' comes from an IP that was * already annotated at an earlier hop AND carries an ICMP dest-unreachable code * (icmp_type >= 0, meaning ICMP type 3 of some code). Returns 0 if any packet * is from a new/unseen IP, carries a normal TTL-exceeded response, or if no * packets were received at this hop at all. */ int hop_is_filtered_repeat (const lft_session_params *sess, int hopno) { const struct trace_packet_info_s *tp; int has_reply = 0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec || tp->recv.tv_usec) { int k, found = 0; has_reply = 1; for (k = 0; k < sess->n_shown_asn_ips; k++) { if (sess->shown_asn_ips[k].s_addr == tp->hopaddr.s_addr) { found = 1; break; } } if (!found || tp->icmp_type < 0) return 0; /* new IP or TTL-exceeded — not a filtered repeat */ } } return has_reply; } /* Return the address of the first received packet at hop 'hopno'. */ struct in_addr hop_first_rcvd_ip (const lft_session_params *sess, int hopno) { const struct trace_packet_info_s *tp; struct in_addr zero; zero.s_addr = 0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec || tp->recv.tv_usec) return tp->hopaddr; } return zero; } /* Returns the icmp_type of the first received packet at hopno. * Used to carry the ICMP name into the compressed filtered-run line. */ int hop_first_rcvd_icmp_type(const lft_session_params *sess, int hopno) { const struct trace_packet_info_s *tp; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec || tp->recv.tv_usec) return tp->icmp_type; } return 3; /* default: port unreachable */ } /* Emit the compressed filtered-run range line, mirroring EVT_RPT_NO_REPLY format. * icmp_type is trace_packet_info_s->icmp_type (0-15 = UNREACH code); * icmp_messages[icmp_type+1] gives the human-readable name. * Format: ** [filtered] A.B.C.D replied for TTL[s] N [through M] */ void print_filtered_run (int start_ttl, int end_ttl, struct in_addr ip, int icmp_type) { const char *name = (icmp_type >= 0 && icmp_type <= 15) ? icmp_messages[icmp_type + 1] : "unreachable"; if (start_ttl == end_ttl) printf("** [filtered] %s replied %s for TTL %d\n", inet_ntoa(ip), name, start_ttl); else printf("** [filtered] %s replied %s for TTLs %d through %d\n", inet_ntoa(ip), name, start_ttl, end_ttl); } /*---------------------------------------------------------------------------*/ static void finish (lft_session_params * sess) { int hopno; int maxhop; int reply, noreply; int as_for_hop = 0; struct trace_packet_info_s *tp; char *netname; /*int ocres;*/ char *myApp = (char *)malloc((strlen(version)+1 * sizeof(char)) + (strlen(appname) * sizeof(char))); struct ip_list_array *ipaslist = (struct ip_list_array *)malloc(sizeof(struct ip_list_array)); /* Variables for seam detection */ int icmpcode; int asseam_hopno=-1; struct in_addr asseam_hopaddr; int netseam_hopno=-1; struct in_addr netseam_hopaddr; struct in_addr classbmask; struct in_addr masked_target; int prevasn=-1; struct in_addr prevasn_hopaddr; int prevasn_hopno; int lastishole; int netreached=0; int isseam; /* ---------------------------- */ inet_aton("255.255.0.0", &classbmask); masked_target.s_addr=sess->remote_address.s_addr & classbmask.s_addr; EvtPacketInfoParam ehip; memset(ipaslist, 0, sizeof(struct ip_list_array)); memset(&tbuf, 0, sizeof(tbuf)); gettimeofday (&(sess->trace_done_time), NULL); /*if(sess->protocol==0) { ocres = open_check(sess, LFTErrHandler, LFTEvtHandler); LFTEvtHandler(sess,EVT_OPEN_CHECK_RESULT,&ocres); if(ocres==1) { sess->target_open=1; sess->target_filtered=0; } else { sess->target_open=0; if(ocres<0) sess->target_filtered=0; else sess->target_filtered=1; } }*/ if (sess->noisy > 3) { LFTEvtHandler(sess, EVT_SHOW_NUM_HOPS, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } if (sess->num_hops || (sess->hop_info[0].flags & HF_ENDPOINT)) { maxhop = sess->num_hops; /* display all packets we received from this host */ SLIST_FOREACH(tp, &(sess->trace_packets), next) if (tp->is_done) tp->hopno = maxhop; } else { maxhop = sess->hop_info_length - 1; } LFTEvtHandler(sess, EVT_TRACE_COMPLETED, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } /* if user wants ASN resolution from pwhois/cymru/riswhois, do it in bulk */ if (sess->do_aslookup || sess->do_netlookup) { if(sess->noisy > 1) { LFTEvtHandler(sess,EVT_ON_RESOLUTION, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } if (!sess->use_radb) { /* populate bulk ip_addr_list structure */ for (hopno = sess->ttl_min; hopno <= maxhop; hopno++) { SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_usec) { (*ipaslist).ipaddr[as_for_hop] = tp->hopaddr; as_for_hop++; (*ipaslist).numItems = (as_for_hop); break; } } } if (sess->use_cymru) { /* use cymru bulk service */ if (w_lookup_as_cymru_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } else if (sess->use_ris) { /* use RIPE NCC RIS service */ if (w_lookup_all_riswhois_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } else { /* use pwhois bulk service */ if ((strlen(version) * sizeof(char)) + 1 + (strlen(appname) * sizeof(char)) < 254) { *myApp = '\0'; strcat(myApp, appname); strcat(myApp, " "); strcat(myApp, version); strncpy((*ipaslist).application,myApp,511); } if (w_lookup_all_pwhois_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); /* Bulk pWhoIs omits org-name; fall back to individual queries * for any hop that has a valid ASN but no orgName yet. * This mirrors the per-hop query path used by watch mode. */ { int _fh; for (_fh = 0; _fh < (*ipaslist).numItems; _fh++) { if ((*ipaslist).asn[_fh] && !(*ipaslist).orgName[_fh][0]) { char _ipstr[INET_ADDRSTRLEN]; strncpy(_ipstr, inet_ntoa((*ipaslist).ipaddr[_fh]), sizeof(_ipstr) - 1); _ipstr[sizeof(_ipstr) - 1] = '\0'; sess->wsess->consolidated_orgname[0] = '?'; sess->wsess->consolidated_orgname[1] = '\0'; if (w_lookup_all_pwhois(sess->wsess, _ipstr) == 0 && sess->wsess->consolidated_orgname[0] != '?') strncpy((*ipaslist).orgName[_fh], sess->wsess->consolidated_orgname, sizeof((*ipaslist).orgName[_fh]) - 1); } } } } if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } } free(myApp); LFTEvtHandler(sess,EVT_TRACE_REPORT_START, &maxhop); if(sess->exit_state < 0){ free(ipaslist); return; } /* seam detection */ as_for_hop=0; for(hopno = sess->ttl_min; hopno < sess->hop_info_length; hopno++) { struct in_addr last_hop; icmpcode=-100; last_hop.s_addr = 0; if(sess->hop_info[hopno].all_rcvd) { lastishole=0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if(tp->recv.tv_sec) { if(hopno<=maxhop) icmpcode=tp->icmp_type; if(last_hop.s_addr != tp->hopaddr.s_addr) { if((tp->hopaddr.s_addr & classbmask.s_addr) == masked_target.s_addr) netreached=1; else { netseam_hopno=hopno; netseam_hopaddr=tp->hopaddr; } if(sess->do_aslookup || sess->do_netlookup) { if (sess->use_radb) { /* using RADB/IRR */ tp->asnumber = w_lookup_as(sess->wsess, inet_ntoa(tp->hopaddr)); } else { /* using pwhois by default */ tp->asnumber = (*ipaslist).asn[as_for_hop]; } if(prevasn==-1) { if(tp->asnumber) { prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } else { if(tp->asnumber) { if(tp->asnumber!=prevasn) { asseam_hopno=prevasn_hopno; asseam_hopaddr=prevasn_hopaddr; } prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } } last_hop=tp->hopaddr; } } } as_for_hop++; } else lastishole=1; if(icmpcode==-1) break; } if(!netreached) netseam_hopno=-1; if(lastishole) asseam_hopno=-1; /* -------------- */ noreply = 0; reply = 0; as_for_hop = 0; /* this correlates the hopno to the asn stored in ipaslist */ { int found_target = 0; /* suppress duplicate target hops from ahead-limit overshoot */ /* Filtered-run state: consecutive hops where the same already-seen IP * responds with an ICMP dest-unreachable code. Compressed into one line. */ int filt_run_start = 0; /* first TTL of active filtered run; 0 = none */ int filt_run_end = 0; /* last TTL of active filtered run */ int filt_run_icmp_type = 3; /* icmp_type of the filtering IP (default: port unreachable) */ struct in_addr filt_run_ip; filt_run_ip.s_addr = 0; for (hopno = sess->ttl_min; hopno <= maxhop; hopno++) { struct in_addr last_hop; /* Suppress duplicate target-hop output caused by ahead-limit overshoot */ if (found_target && (sess->hop_info[hopno].flags & HF_ENDPOINT)) { reply = 0; continue; } /* Detect filtered-repeat hops: same already-seen IP responding with ICMP * type-3 (dest unreachable / port blocked) at multiple TTL levels. * Compress into a single range line, mirroring the noreply treatment. */ { int _is_filt = (sess->hop_info[hopno].all_rcvd != 0) && hop_is_filtered_repeat(sess, hopno); if (!_is_filt && filt_run_start > 0) { print_filtered_run(filt_run_start, filt_run_end, filt_run_ip, filt_run_icmp_type); filt_run_start = 0; } if (_is_filt) { /* Flush any pending noreply range before extending filtered run */ if (noreply >= 1) { EvtNoReplyParam _nrp; _nrp.hopno = hopno; _nrp.noreply = noreply; LFTEvtHandler(sess, EVT_RPT_NO_REPLY, &_nrp); if (sess->exit_state < 0) { free(ipaslist); return; } } if (filt_run_start == 0) { filt_run_start = hopno; filt_run_ip = hop_first_rcvd_ip(sess, hopno); filt_run_icmp_type = hop_first_rcvd_icmp_type(sess, hopno); } filt_run_end = hopno; reply = 1; } else { /* !_is_filt — normal rendering path */ if (sess->hop_info[hopno].all_rcvd != 0) { if (noreply >= 1) { if (sess->pmtud && global_output_style == 0) { /* Per-hop output when PMTUD is active. * [ICMP filtered]: hop suppresses TTL-exceeded for large probes * but forwards them (confirmed because a downstream hop or the * target responded to the same probe size). * [MTU Limit]: hop did not respond to the DF probe and we cannot * confirm the packet was forwarded — likely a true dropper. * [neglected]: hop ignored probes of all sizes. */ int bh; for (bh = hopno - noreply; bh < hopno; bh++) { if (sess->hop_info[bh].pmtud_icmp_suppressor) printf("** [ICMP filtered] hop %d suppressed TTL-exceeded" " for DF probe (%u bytes) — packet confirmed" " forwarded\n", bh + 1, (unsigned)sess->hop_info[bh].path_mtu); else if (sess->hop_info[bh].pmtud_blackhole) printf("** [MTU Limit] hop %d did not respond to DF probe" " (%u bytes, no PTB received)\n", bh + 1, (unsigned)sess->hop_info[bh].path_mtu); else printf("** [neglected] no reply packets received" " from TTL %d\n", bh + 1); } } else { EvtNoReplyParam nrp; nrp.hopno=hopno; nrp.noreply=noreply; LFTEvtHandler(sess,EVT_RPT_NO_REPLY, &nrp); if(sess->exit_state < 0) { free(ipaslist); return; } } } } last_hop.s_addr = 0; if ((sess->hop_info[hopno].state == HS_SEND_FIN) && (sess->hop_info[hopno+1].state == HS_SEND_SYN) && (sess->hop_info[hopno+1].ts_last_recv.tv_sec)) { LFTEvtHandler(sess,EVT_RPT_FRW_INSPECT_PACKS, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if ((sess->hop_info[hopno].state != HS_SEND_SYN_ACK) && (sess->hop_info[hopno+1].state == HS_SEND_SYN_ACK) && (hopno == (sess->num_hops - 1))) { LFTEvtHandler(sess,EVT_RPT_FRW_STATE_FILTER, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if ((sess->hop_info[hopno].flags & HF_ENDPOINT) && (noreply >= ((maxhop - sess->ttl_min)/2)) && sess->num_hops > 3) { LFTEvtHandler(sess,EVT_RPT_BSD_BUG, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if (sess->hop_info[hopno].all_rcvd == 0) { reply = 0; } else { LFTEvtHandler(sess,EVT_RPT_HOP_INFO_START,&hopno); if(sess->exit_state < 0){ free(ipaslist); return; } SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec) { reply = 1; if (last_hop.s_addr != tp->hopaddr.s_addr) { ehip.asnumber = 0; /* init/clear the ASN */ if (sess->do_aslookup) { if (sess->use_radb) { /* using RADB/IRR */ ehip.asnumber = w_lookup_as(sess->wsess, inet_ntoa(tp->hopaddr)); } else { /* using pwhois by default */ ehip.asnumber = (*ipaslist).asn[as_for_hop]; } } tp->asnumber=ehip.asnumber; ehip.netname=NULL; if (sess->do_netlookup) { if (!sess->do_aslookup || (sess->do_aslookup && !sess->use_cymru && !sess->use_radb)) { netname = (*ipaslist).netName[as_for_hop]; } else { netname = w_lookup_netname(sess->wsess, inet_ntoa(tp->hopaddr)); } ehip.netname=netname; } if(ehip.netname) strncpy(tp->netname, ehip.netname, 511); else tp->netname[0]=0; /* Orgname — pwhois bulk only (not available via cymru/radb). * Fire whenever pwhois ran, which is do_aslookup OR do_netlookup. */ tp->orgname[0] = 0; if ((sess->do_aslookup || sess->do_netlookup) && !sess->use_cymru && !sess->use_radb) strncpy(tp->orgname, (*ipaslist).orgName[as_for_hop], sizeof(tp->orgname) - 1); } ehip.last_hop=last_hop; tp->last_hop=ehip.last_hop; last_hop = tp->hopaddr; } ehip.tp=tp; /* seam processing */ isseam=0; ehip.is_asseam=0; ehip.is_netseam=0; ehip.is_open=0; ehip.is_filtered=0; ehip.seam_traced=0; if(sess->check_seam && hopno==asseam_hopno && tp->hopaddr.s_addr==asseam_hopaddr.s_addr) { isseam=1; ehip.is_asseam=1; } if(sess->check_seam && hopno==netseam_hopno && tp->hopaddr.s_addr==netseam_hopaddr.s_addr) { isseam=1; ehip.is_netseam=1; } if(isseam) { if(sess->check_seam) { int curroutputstyle=global_output_style; char hostname[100]; ehip.seam_traced=1; global_output_style=2; lft_session_params * subsess=LFTSessionOpen(); strncpy(hostname, inet_ntoa(tp->hopaddr),100); subsess->senddevsel = sess->senddevsel; subsess->senddev = sess->senddev; subsess->auto_ports=0; subsess->dport=179; subsess->seq_start=30; subsess->retry_min=1; subsess->retry_max=1; subsess->resolve_names=0; subsess->ahead_limit=1; subsess->break_on_icmp = 0; subsess->is_graphviz_subquery=1; subsess->hostname=hostname; subsess->hostname_lsrr_size = 0; LFTExecute(subsess); ehip.is_open=subsess->target_open; ehip.is_filtered=subsess->target_filtered; LFTSessionClose(subsess); global_output_style=curroutputstyle; } } /* PMTUD annotations */ ehip.path_mtu = sess->hop_info[hopno].path_mtu; ehip.pmtud_blackhole = sess->hop_info[hopno].pmtud_blackhole; ehip.pmtud_icmp_suppressor = sess->hop_info[hopno].pmtud_icmp_suppressor; /* --------------- */ LFTEvtHandler(sess,EVT_RPT_PACKET_INFO,&ehip); if(sess->exit_state < 0){ free(ipaslist); return; } } LFTEvtHandler(sess,EVT_RPT_PACKET_LIST_END,NULL); if(sess->exit_state < 0){ free(ipaslist); return; } /* Record that we've printed the first target hop so the guard above * can suppress any duplicate endpoint hops from ahead-limit overshoot. */ if (sess->hop_info[hopno].flags & HF_ENDPOINT) found_target = 1; } } /* !_is_filt */ } /* filtered-repeat detection block */ if (reply) { noreply = 0; as_for_hop++; } else noreply++; reply = 0; } /* for(...) */ /* Flush any trailing filtered run (e.g. filtered hops at the end of the trace) */ if (filt_run_start > 0) print_filtered_run(filt_run_start, filt_run_end, filt_run_ip, filt_run_icmp_type); } /* found_target block */ /* num_hops==0 is ambiguous: it means both "not set" and "target replied at hopno=0 * (TTL=1)". Guard against the latter before reporting no hops found. */ if (!sess->num_hops && !(sess->hop_info[0].flags & HF_ENDPOINT)){ LFTEvtHandler(sess, EVT_RPT_NO_HOPS, &maxhop); } if (sess->timetrace){ LFTEvtHandler(sess, EVT_RPT_TIME_TRACE, NULL); } LFTEvtHandler(sess, EVT_ON_EXIT, NULL); free(ipaslist); return; } /*---------------------------------------------------------------------------*/ /* forward declaration — pcap_process_packet is defined later in this file */ #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) static void pcap_process_packet(u_char *, const struct pcap_pkthdr *, const u_char *); #endif /*---------------------------------------------------------------------------*/ /* * lft_pmtud_verify_blackholes — called just before finish() when -K is active. * * Every hop tentatively flagged pmtud_blackhole=1 could be either: * (a) a genuine PMTUD black hole: drops oversized DF probes silently, but * WILL respond to small probes with ICMP TTL-exceeded, or * (b) a plain non-responder that ignores TTL-exceeded entirely. * * We send one small (128-byte) probe at each suspect TTL and wait up to * timeout_ms for a reply. If a TTL-exceeded comes back the hop is a * confirmed black hole; if nothing comes back we clear the flag so the hop * is rendered as ordinary [neglected]. * * State cleanup: both ts_last_recv and all_rcvd are reset to zero after the * probe so finish() still treats the hop as no-reply regardless of outcome; * pmtud_blackhole is the discriminator used in output. */ #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) void lft_pmtud_verify_blackholes(lft_session_params *sess) { int nhop; const uint16_t verify_mtu = 128; /* small enough to pass any real link */ if (sess->protocol == 2 || sess->protocol == 3) { /* ICMP probe mode: resize via sess->userlen */ int save_userlen = sess->userlen; sess->userlen = (int)verify_mtu; for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { struct timeval deadline, now; int confirmed; if (!sess->hop_info[nhop].pmtud_blackhole) continue; /* Clear reply state so a fresh reply is detectable */ sess->hop_info[nhop].ts_last_recv.tv_sec = 0; sess->hop_info[nhop].ts_last_recv.tv_usec = 0; /* Set verifying flag before send so EVT_SENT_PACKET shows [PMTUD verify] */ sess->pmtud_verifying = 1; if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: sending verify probe (LEN=%u)\n", nhop + 1, (unsigned)verify_mtu); icmp_pmtud_send_verify_hop(sess, nhop); gettimeofday(&deadline, NULL); deadline.tv_usec += (long)sess->timeout_ms * 1000L; while (deadline.tv_usec >= 1000000L) { deadline.tv_sec++; deadline.tv_usec -= 1000000L; } confirmed = 0; do { pcap_dispatch(sess->pcapdescr, 1, pcap_icmp_process_packet, (u_char *)sess); if (sess->hop_info[nhop].ts_last_recv.tv_sec) { confirmed = 1; break; } gettimeofday(&now, NULL); } while (timercmp(&now, &deadline, <)); sess->pmtud_verifying = 0; sess->hop_info[nhop].ts_last_recv.tv_sec = 0; sess->hop_info[nhop].ts_last_recv.tv_usec = 0; sess->hop_info[nhop].all_rcvd = 0; if (!confirmed) { if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: verify timed out, reclassified as [neglected]\n", nhop + 1); sess->hop_info[nhop].pmtud_blackhole = 0; sess->hop_info[nhop].path_mtu = 0; } else { if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: verify confirmed DF black hole (path_mtu=%u)\n", nhop + 1, (unsigned)sess->hop_info[nhop].path_mtu); } } sess->userlen = save_userlen; /* Post-verification pass: if the target was reached by the oversized * probes (no PTB received, so pmtud_current_mtu never stepped down from * the initial value), then every confirmed black hole on the path * forwarded those probes and is actually suppressing ICMP TTL-exceeded * by size policy — not dropping the packets. Reclassify accordingly. */ if (sess->num_hops > 0 && sess->pmtud_ptb_mtu == 0) { for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (sess->hop_info[nhop].pmtud_blackhole) { sess->hop_info[nhop].pmtud_blackhole = 0; sess->hop_info[nhop].pmtud_icmp_suppressor = 1; if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: reclassified as [ICMP filtered]" " — oversized probe confirmed forwarded\n", nhop + 1); } } } return; } /* TCP / UDP probe mode: resize via trace_packet template */ { int hdr_size; int save_payload_len; u_short save_ip_len; if (sess->protocol == 1) hdr_size = (int)(sizeof(struct ip) + sizeof(struct udphdr)); else hdr_size = (int)(sizeof(struct ip) + sizeof(struct tcphdr)); if (sess->trace_packet.lsrr.ipl_len > 0) hdr_size += sess->trace_packet.lsrr.ipl_len + 1; if ((int)verify_mtu <= hdr_size) return; /* degenerate — skip */ save_payload_len = sess->trace_packet.payload_len; save_ip_len = sess->trace_packet.ip_hdr.ip_len; sess->trace_packet.payload_len = (int)verify_mtu - hdr_size; #ifdef SCREWED_IP_LEN sess->trace_packet.ip_hdr.ip_len = (u_short)verify_mtu; #else sess->trace_packet.ip_hdr.ip_len = htons((u_short)verify_mtu); #endif for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { struct timeval deadline, now; int confirmed; if (!sess->hop_info[nhop].pmtud_blackhole) continue; /* Clear reply state so a fresh reply is detectable */ sess->hop_info[nhop].ts_last_recv.tv_sec = 0; sess->hop_info[nhop].ts_last_recv.tv_usec = 0; /* Set verifying flag before send so EVT_SENT_PACKET shows [PMTUD verify] */ sess->pmtud_verifying = 1; if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: sending verify probe (LEN=%u)\n", nhop + 1, (unsigned)verify_mtu); send_hop(sess, nhop); gettimeofday(&deadline, NULL); deadline.tv_usec += (long)sess->timeout_ms * 1000L; while (deadline.tv_usec >= 1000000L) { deadline.tv_sec++; deadline.tv_usec -= 1000000L; } /* Guard: prevent the check_timeouts() call inside process_packet() * from re-entrantly calling finish() while we are still verifying. */ confirmed = 0; do { pcap_dispatch(sess->pcapdescr, 1, pcap_process_packet, (u_char *)sess); if (sess->hop_info[nhop].ts_last_recv.tv_sec) { confirmed = 1; break; } gettimeofday(&now, NULL); } while (timercmp(&now, &deadline, <)); sess->pmtud_verifying = 0; /* Always restore no-reply state: finish() must treat this as a hole. * pmtud_blackhole is the sole discriminator for [MTU Limit] vs [neglected]. */ sess->hop_info[nhop].ts_last_recv.tv_sec = 0; sess->hop_info[nhop].ts_last_recv.tv_usec = 0; sess->hop_info[nhop].all_rcvd = 0; if (!confirmed) { /* Small probe also timed out — plain non-responder, not PMTUD-related */ if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: verify timed out, reclassified as [neglected]\n", nhop + 1); sess->hop_info[nhop].pmtud_blackhole = 0; sess->hop_info[nhop].path_mtu = 0; } else { /* Confirmed PMTUD black hole — leave pmtud_blackhole=1, path_mtu set */ if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: verify confirmed DF black hole (path_mtu=%u)\n", nhop + 1, (unsigned)sess->hop_info[nhop].path_mtu); } } sess->trace_packet.payload_len = save_payload_len; sess->trace_packet.ip_hdr.ip_len = save_ip_len; /* Post-verification pass: reclassify confirmed black holes as ICMP * suppressors when the target was reached without any PTB step-down, * proving the oversized probes were forwarded through those hops. */ if (sess->num_hops > 0 && sess->pmtud_ptb_mtu == 0) { for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (sess->hop_info[nhop].pmtud_blackhole) { sess->hop_info[nhop].pmtud_blackhole = 0; sess->hop_info[nhop].pmtud_icmp_suppressor = 1; if (sess->noisy > 1 && !global_output_style) printf("PMTUD hop %d: reclassified as [ICMP filtered]" " — oversized probe confirmed forwarded\n", nhop + 1); } } } } } #endif /* !CYGWIN/WIN32 */ /*---------------------------------------------------------------------------*/ static int check_timeouts (lft_session_params * sess) { int nhop; int need_reply = 0; int no_reply = 0; int last_return = 0; gettimeofday (&(sess->now), NULL); if (timediff_ms (sess->ts_last_sent, sess->now) < sess->scatter_ms) return 0; /* not ready to send another packet yet */ for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (!sess->hop_info[nhop].num_sent) { send_hop(sess, nhop); return 0; } } for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (sess->hop_info[nhop].num_sent <= sess->retry_max && !sess->hop_info[nhop].ts_last_recv.tv_sec) { if (sess->noisy > 4) { LFTEvtHandler(sess,EVT_TTL_NO_REPLY,&nhop); if(sess->exit_state<0) return 0; } if (timediff_ms (sess->hop_info[nhop].ts_last_sent, sess->now) >= sess->timeout_ms) { /* we timed out waiting for this hop -- retry if we have any * more tries */ if (sess->hop_info[nhop].num_sent < sess->retry_max) { if (!sess->noisy && !sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_NO_REPLY,NULL); if (sess->noisy > 2) LFTEvtHandler(sess,EVT_TTL_TOUT_RESEND,&nhop); if(sess->exit_state<0) return 0; send_hop(sess, nhop); return 0; } else { if (!sess->adaptive || hop_state_up(sess, nhop)) { if (sess->noisy > 3) LFTEvtHandler(sess,EVT_TTL_TOUT_GIVINGUP,&nhop); if(sess->exit_state<0) return 0; /* PMTUD: DF probe timed out — silent black hole. * Store the probe size that was rejected so output * can report the exact byte count to the user. */ if (sess->pmtud) { sess->hop_info[nhop].pmtud_blackhole = 1; sess->hop_info[nhop].path_mtu = sess->pmtud_current_mtu; } no_reply++; } } } else { need_reply++; /* we have to wait for this one to timeout */ } } else { /* have reply */ last_return = nhop; } } if (sess->noisy > 4) { EvtDebugCheckpoint1Param edcp; edcp.last_return=last_return; edcp.need_reply=need_reply; edcp.no_reply=no_reply; LFTEvtHandler(sess,EVT_DBG_CHECKPOINT1,&edcp); if(sess->exit_state<0) return 0; } if (no_reply >= sess->ahead_limit) { /* we timed out. */ if ((last_return + 3) * 2 < sess->hop_info_length) { if (sess->pmtud_verifying) return 0; /* re-entrant call during verification — do nothing */ if ((need_reply < 3) && (sess->num_rcvd < 2)) LFTEvtHandler(sess,EVT_CANT_RELIABLY_RTRIP,NULL); if(sess->exit_state<0) return 0; #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) if (sess->pmtud) lft_pmtud_verify_blackholes(sess); #endif finish (sess); return 1; } } if ((!sess->num_hops || sess->hop_info_length < sess->num_hops || need_reply) && sess->hop_info_length < sess->ttl_limit) { if (sess->noisy > 4) LFTEvtHandler(sess,EVT_HAVE_UNANSWERRED_HOPS,NULL); if (need_reply >= sess->ahead_limit) { if (sess->noisy > 4) LFTEvtHandler(sess,EVT_TOO_FAR_AHEAD,NULL); return 0; /* wait for some replies before we go on */ } if(sess->exit_state<0) return 0; if (sess->num_hops > 0 && sess->hop_info_length >= sess->num_hops) { if (sess->noisy > 3) LFTEvtHandler(sess,EVT_HAVE_GAPS,NULL); return 0; /* we know how long the path is -- * wait to fill in the blanks */ } nhop = sess->hop_info_length++; send_hop(sess, nhop); } else { if (sess->noisy >= 4) { LFTEvtHandler(sess,EVT_EITHER_RESP_OR_TOUT,NULL); if(sess->exit_state<0) return 0; } for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (sess->hop_info[nhop].num_sent < sess->retry_min && sess->hop_info[nhop].num_sent <= sess->retry_max) { send_hop(sess, nhop); return 0; } } /* If we're adaptive and target appears closed, increment state and try again */ if ((sess->adaptive) && (sess->target_open < 1) && (sess->hop_info[nhop].state != HS_MAX)) { hop_state_up(sess, nhop); send_hop(sess, nhop); return 0; } if (sess->pmtud_verifying) return 0; /* re-entrant call during verification — do nothing */ #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) if (sess->pmtud) lft_pmtud_verify_blackholes(sess); #endif finish (sess); return 1; } return 0; } /*---------------------------------------------------------------------------*/ static void recv_packet (lft_session_params * sess, unsigned int seq, struct in_addr ipaddr, int icmp_type, const struct pcap_pkthdr *hdr) { double ms; struct trace_packet_info_s *tp = NULL; EvtNonSeqPacketParam ensp; /* Depending on the platform, we can use * the pcap header's timeval or we must call gettimeofday() for each packet */ #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) || defined( USE_GTOD ) (void)hdr; gettimeofday (&(sess->now), NULL); #else sess->now.tv_sec = hdr->ts.tv_sec; sess->now.tv_usec = hdr->ts.tv_usec; /* gettimeofday (&now, NULL); */ #endif /* First, search every probe to find an exact sequence match */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if (tp->seq == seq) { break; } } /* Next, if no probes have an exact sequence match, look for an unincremented ACK */ if (tp == NULL) { if (sess->noisy > 3) { LFTEvtHandler(sess,EVT_LOOKFOR_UNINC_ACK,NULL); if(sess->exit_state<0) return; } SLIST_FOREACH(tp, &(sess->trace_packets), next) { if (((tp->seq) == (seq +1)) && (icmp_type == -1)) break; } } /* Next, if no probes have an exact sequence match, look for an off-by-len */ if (tp == NULL) { if (sess->noisy > 3) { LFTEvtHandler(sess,EVT_LOOKFOR_OFF_BY_LEN,NULL); if(sess->exit_state<0) return; } SLIST_FOREACH(tp, &(sess->trace_packets), next) { if (((tp->seq) == (seq - sess->payloadlen)) && (icmp_type == -1)) break; } } /* Last resort. Catch any response from the target */ if (tp == NULL) { if (sess->noisy > 3) { LFTEvtHandler(sess,EVT_LOOKFOR_LAST_RESORT,NULL); if(sess->exit_state<0) return; } SLIST_FOREACH(tp, &(sess->trace_packets), next) { /* Special case: look for a response to our SYN_ACK */ if (tp->u.packet.tcp_hdr.th_flags == HS_SEND_SYN_ACK) { if (!tp->recv.tv_sec) { break; } } /* Truly the last resort: packet from the target with a wacky ACK sequence */ if ((ipaddr.s_addr == sess->remote_address.s_addr) && (tp->hopaddr.s_addr == 0) && (icmp_type == -1)) { sess->target_anomaly = 1; } } } /* This packet is not even close, drop it and move on */ if (!tp) { if (sess->noisy) LFTEvtHandler(sess,EVT_SKIP_PACKET,NULL); else if (!sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_SKIP_PACKET,NULL); return; } if (tp->seq != seq) { ensp.ipaddr=ipaddr; ensp.tp=tp; if (((tp->seq) == (seq + 1)) && (icmp_type == -1)) { if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_ACK_WAS_NOT_INC,&ensp); if(sess->exit_state<0) return; } /* return; */ } else if (((tp->seq) == (seq - sess->payloadlen)) && (icmp_type == -1)) { if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RST_REL_TO_ISN,&ensp); if(sess->exit_state<0) return; } /* return; */ } else if ((ipaddr.s_addr == sess->remote_address.s_addr) && (icmp_type == -1)) { if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_ACK_WAS_WAY_OFF,&ensp); if(sess->exit_state<0) return; } /* return; */ } } if (tp->recv.tv_sec) { if (sess->noisy) LFTEvtHandler(sess,EVT_DUPLICATE_PACKET, NULL); else if (!sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_DUPLICATE,NULL); return; } /* Set icmp_type before firing EVT_RECV_PACKET so the handler sees the * correct value (race-condition fix: it was being set 30 lines later). */ tp->icmp_type = icmp_type; if (sess->noisy > 1) { EvtRecvPacketParam erpp; erpp.ipaddr=ipaddr; erpp.seq=seq; erpp.tp=tp; LFTEvtHandler(sess,EVT_RECV_PACKET,&erpp); } else { if (!sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_OK,NULL); } if(sess->exit_state<0) return; /* increment received packet counter */ sess->num_rcvd++; tp->recv = sess->now; if (tp->hopno != -1) { sess->hop_info[tp->hopno].ts_last_recv = sess->now; sess->hop_info[tp->hopno].all_rcvd++; hop_state_copy(sess, tp->hopno); /* indicate this hop has a sequence anomaly */ if (icmp_type == -1) sess->hop_info[tp->hopno].flags |= HF_ENDPOINT; } tp->hopaddr = ipaddr; /* tp->icmp_type already set above, before EVT_RECV_PACKET */ #ifdef HAVE_CARES lft_ares_fire(sess, ipaddr); /* queue PTR lookup for this hop (deduped) */ #endif if (icmp_type != -2 && (!sess->num_hops || sess->num_hops > tp->hopno)) if ((sess->break_on_icmp && lft_icmp_stops_trace(icmp_type)) || (icmp_type == -1)) { if (tp->hopno != -1) { /* we set fake type -1 when we get actual * tcp packet in return - meaning destination */ sess->num_hops = tp->hopno; tp->is_done = 1; if (sess->noisy > 1 && sess->target_open < 1) LFTEvtHandler(sess,EVT_TCP_PORT_CLOSED,NULL); else if (sess->noisy > 1 && sess->target_open > 0) LFTEvtHandler(sess,EVT_TCP_PORT_OPEN,NULL); if(sess->exit_state<0) return; } } /* adjust scatter if we have fast reply times */ ms = timediff_ms (tp->sent, tp->recv); sess->scatter_ms = (sess->scatter_ms * (sess->ahead_limit - 1) + ms) / sess->ahead_limit; } /*---------------------------------------------------------------------------*/ void lft_printf(lft_session_params * sess, const char *templ, ...) { va_list ap; char buf[1024]; va_start (ap, templ); vsprintf(buf, templ, ap); va_end (ap); LFTEvtHandler(sess, EVT_DBG_LOG_MESSAGE, buf); } /*---------------------------------------------------------------------------*/ static void process_packet (lft_session_params * sess, const u_char *packet, const struct pcap_pkthdr *hdr) { const struct ether_header *eptr; const struct ip *ip, *orig_ip; const struct tcphdr *tcp; const struct udphdr *udp; const struct icmp *icmp; if (sess->noisy > 4) { LFTEvtHandler(sess,EVT_PROCESS_PACKET_START,NULL); if(sess->exit_state<0) return; } check_timeouts (sess); if(sess->exit_state<0) return; /* Test EtherType to adjust Ethernet header length 802.1q VLAN Ethernet frame (dot1q) */ eptr = (const struct ether_header *) packet; if ((sess->skip_header_len == sizeof (struct ether_header)) && (ntohs (eptr->ether_type) == ETHERTYPE_VLAN)) { sess->skip_header_len += 4; } packet += sess->skip_header_len; ip = (const void *)packet; packet += 4 * ip->ip_hl; switch (ip->ip_p) { case IPPROTO_ICMP: orig_ip = ip; icmp = (const void *)packet; if (icmp->icmp_type != ICMP_UNREACH && icmp->icmp_type != ICMP_TIMXCEED) { return; } ip = &icmp->icmp_ip; if (sess->protocol==1) { if (ip->ip_p != IPPROTO_UDP) return; /* not a response to our udp probe */ } else { if (ip->ip_p != IPPROTO_TCP) return; /* not a response to our tcp probe */ } packet = (const u_char *)ip; packet += 4 * ip->ip_hl; if (sess->protocol==1) { udp = (const void *)packet; if (ntohs (udp->uh_sport) != sess->sport || ip->ip_src.s_addr != sess->local_address.s_addr || ip->ip_dst.s_addr != sess->remote_address.s_addr) { LFTEvtHandler(sess,EVT_UDP_NOT_FOR_US,NULL); return; /* not for us */ } if (sess->noisy > 2) { EvtIncomingICMPUDPParam eiiup; eiiup.icmp=icmp; eiiup.ip=ip; eiiup.orig_ip=orig_ip; eiiup.udp=udp; LFTEvtHandler(sess,EVT_INCOMING_ICMP_UDP,&eiiup); if(sess->exit_state<0) return; } if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RCVD_ICMP_UDP,udp); if(sess->exit_state<0) return; } /* PMTUD: intercept PTB before marking the hop answered */ if (sess->pmtud && icmp->icmp_type == ICMP_UNREACH && icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { /* Match via IP ID embedded in the inner (echoed) IP header — * ip points to the inner IP after the ip = &icmp->icmp_ip * reassignment above; orig_ip is the outer router IP. */ lft_pmtud_handle_ptb(sess, icmp, (unsigned int)ntohs(ip->ip_id), orig_ip->ip_src); return; } /* Match the reply to its sent probe via the IP ID embedded in * the inner (echoed) IP header. ip was reassigned to the inner * header via ip = &icmp->icmp_ip; orig_ip is the outer router IP. */ recv_packet (sess, (unsigned int)ntohs(ip->ip_id), orig_ip->ip_src, (icmp->icmp_type == ICMP_TIMXCEED) ? -2 : icmp->icmp_code, hdr); if(sess->exit_state<0) return; } else { tcp = (const void *)packet; if (ntohs (tcp->th_dport) != sess->dport || ip->ip_src.s_addr != sess->local_address.s_addr || ip->ip_dst.s_addr != sess->remote_address.s_addr) return; /* not for us */ if (sess->noisy > 2) { EvtIncomingICMPTCPParam eiitp; eiitp.icmp=icmp; eiitp.ip=ip; eiitp.orig_ip=orig_ip; eiitp.tcp=tcp; LFTEvtHandler(sess,EVT_INCOMING_ICMP_TCP,&eiitp); if(sess->exit_state<0) return; } if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RCVD_ICMP_TCP,tcp); if(sess->exit_state<0) return; } /* PMTUD: intercept PTB before marking the hop answered */ if (sess->pmtud && icmp->icmp_type == ICMP_UNREACH && icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { lft_pmtud_handle_ptb(sess, icmp, ntohl(tcp->th_seq), orig_ip->ip_src); return; } recv_packet (sess, ntohl (tcp->th_seq) , orig_ip->ip_src, (icmp->icmp_type == ICMP_TIMXCEED) ? -2 : icmp->icmp_code, hdr); if(sess->exit_state<0) return; } return; case IPPROTO_TCP: /* check for RST reply */ tcp = (const void *)packet; if (!(tcp->th_flags & TH_RST) && !(tcp->th_flags & TH_ACK) && !(tcp->th_flags & TH_SYN)) return; /* not what we're looking for */ if (ntohs (tcp->th_sport) != sess->dport || ip->ip_src.s_addr != sess->remote_address.s_addr || ip->ip_dst.s_addr != sess->local_address.s_addr) { return; /* not the right connection */ } if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RCVD_TCP,tcp); if(sess->exit_state<0) return; } /*if (ntohl(tcp->th_ack) < seq_start || ntohl(tcp->th_ack) > seq_start + trace_packet_info_length + 1) return; * not for us */ /* Check for SYN,ACK in response to determine if target is listening */ if ((tcp->th_flags & TH_ACK) && (tcp->th_flags & TH_SYN) && !(tcp->th_flags & TH_RST)) { sess->target_open++; } /* It's wrong, because it can reset status of port after it already detected as open if ((tcp->th_flags & TH_ACK) && !(tcp->th_flags & TH_SYN) && (tcp->th_flags & TH_RST)) { sess->target_open = 0; } */ recv_packet (sess, ntohl (tcp->th_ack) - 1, ip->ip_src, -1, hdr); if(sess->exit_state<0) return; /*Could be nice to host and send a RESET here*/ /* send_packet(-1, IPDEFTTL, ntohl(tcp->th_ack) + 1, TH_RST); */ return; case IPPROTO_UDP: /* could be a probe we sent or something we don't support */ return; default: if (sess->noisy > 3) LFTEvtHandler(sess,EVT_RCVD_UNKNOWN,ip); } } /*---------------------------------------------------------------------------*/ #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) static void pcap_process_packet (u_char * user_data, const struct pcap_pkthdr *hdr, const u_char * packet) { lft_session_params * sess=(lft_session_params *)(void *)user_data; if(sess->exit_state<0) return; process_packet(sess, packet, hdr); } #endif /*---------------------------------------------------------------------------*/ #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) void cygwin_process(lft_session_params * sess) { fd_set fds; struct timeval tm; int wsaerr; tm.tv_sec = 0; tm.tv_usec = 100000; FD_ZERO(&fds); FD_SET(sess->recv_sock, &fds); if (select(sess->recv_sock+1, &fds, 0, 0, &tm) < 0) { wsaerr=WSAGetLastError(); LFTErrHandler(sess, ERR_WIN_SELECT, NULL); return; } if (FD_ISSET(sess->recv_sock, &fds)) { /* read packet */ char packetbuf[2048]; int nread; memset(packetbuf, 0, sizeof(packetbuf)); nread = recv(sess->recv_sock, packetbuf, sizeof(packetbuf), 0); if (nread <= 0) { LFTErrHandler(sess, ERR_WIN_RECV, NULL); return; } process_packet(sess, packetbuf, NULL); } } #endif /*---------------------------------------------------------------------------*/ /* Replacement for deprecated pcap_lookupdev */ /*---------------------------------------------------------------------------*/ char * lft_pcap_lookupdev(char *ebuf) { pcap_if_t *alldevs; char *dev = NULL; int res = pcap_findalldevs(&alldevs, ebuf); if (!res && alldevs != NULL) { dev = strdup(alldevs->name); pcap_freealldevs(alldevs); } return dev; } /*---------------------------------------------------------------------------*/ /* Main of lft */ /*---------------------------------------------------------------------------*/ void LFTExecute(lft_session_params * sess) { #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) char ebuf[PCAP_ERRBUF_SIZE]; /*static pcap_t *pd;*/ #endif sess->exit_state = 0; #ifdef HAVE_CARES /* Initialize c-ares channel now that all session options (including * async_dns_disabled) have been set by the caller. * TIMEOUTMS=500ms per attempt, TRIES=2 (one retry for dropped UDP): * total worst-case 1 000 ms per query — same as the alarm(1) ceiling * but queries run concurrently with probing so cost is largely hidden. * In watch mode LFTExecute is called once per cycle; destroy any channel * left open from the previous cycle before creating a fresh one. */ if (sess->ares_chan) { ares_destroy(sess->ares_chan); } sess->ares_chan = NULL; sess->ares_pending = 0; /* In watch mode the dns_cache is pre-seeded by watch_inject_cached_hostnames * before each LFTExecute call. Resetting it here would discard those entries * and force lft_ares_fire to re-query every IP every cycle. The existing * dedup check in lft_ares_fire() prevents duplicate entries, so the cache * only grows when genuinely new IPs appear (path changes). */ if (!sess->watch_mode) sess->dns_cache_count = 0; if (!sess->async_dns_disabled && sess->resolve_names) { struct ares_options aopts; int amask; memset(&aopts, 0, sizeof(aopts)); aopts.timeout = 500; /* ms per attempt */ aopts.tries = 2; /* total attempts */ aopts.sock_state_cb = lft_ares_sock_state_cb; aopts.sock_state_cb_data = sess; sess->ares_nfds = 0; memset(sess->ares_fd_table, 0, sizeof(sess->ares_fd_table)); amask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB; if (ares_init_options(&sess->ares_chan, &aopts, amask) != ARES_SUCCESS) sess->ares_chan = NULL; /* fall back to getnameinfo */ } #endif /* HAVE_CARES */ if(sess->auto_ports != 0) { do_auto_ports(sess, sess->hostname, sess->dport); if(sess->exit_state < 0){ /* if(sess->hostname != NULL) free(sess->hostname); */ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return; } } if ((sess->do_netlookup != 0) || (sess->do_aslookup != 0)) { sess->wsess = w_init(); /* initialize the whois framework */ sess->wsess->logprintfCookie = sess; /*Parameter for lft_printf*/ } /* if not given network interface, select one automatically */ if (!sess->userdevsel) { sess->pcap_dev = lft_getifforremote(sess, sess->hostname); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } /* if (sess->hostname != NULL) free(sess->hostname); */ return; } #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) if (sess->pcap_dev == NULL) sess->pcap_dev = lft_pcap_lookupdev (ebuf); if (sess->pcap_dev == NULL) { LFTErrHandler(sess, ERR_PCAP_ERROR, ebuf); if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } /* if (sess->hostname != NULL) free(sess->hostname); */ return; } #else if (sess->pcap_dev == NULL) { LFTErrHandler(sess, ERR_DISCOVER_INTERFACE, NULL); if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return; } #endif /* we have a receive device, set the source address */ sess->pcap_send_dev = sess->pcap_dev; sess->local_address.s_addr = lft_getifaddr(sess->pcap_dev); } else { struct in_addr addr; if (inet_aton(sess->userdev, &addr)) { /* specified by ip address -- look up device. */ sess->pcap_dev = lft_getifname(addr); if (sess->pcap_dev != 0) { LFTErrHandler(sess, ERR_UNKNOWN_INTERFACE, NULL); if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return; } /* we have a receive device, set the source address */ sess->local_address.s_addr = lft_getifaddr(sess->pcap_dev); } else sess->pcap_dev = sess->userdev; sess->pcap_send_dev = sess->userdev; sess->local_address.s_addr = lft_getifaddr(sess->pcap_dev); }; /* if user wants a different/spoof interface, facilitate */ if (sess->senddevsel > 0) { struct in_addr addr; if (inet_aton(sess->senddev, &addr)) { /* specified by ip address -- force using default device */ /*sess->pcap_send_dev = lft_getifname(addr);*/ sess->pcap_send_dev = lft_getifforremote(sess, sess->hostname); if (sess->pcap_send_dev == 0) { LFTErrHandler(sess, ERR_UNKNOWN_SEND_INTERFACE, NULL); return; } /* we have a send IP address, set the source address */ sess->local_address.s_addr = get_address(sess,sess->senddev); } else { sess->pcap_send_dev = sess->senddev; if (sess->pcap_send_dev == 0) { LFTErrHandler(sess, ERR_UNKNOWN_SEND_INTERFACE, NULL); return; } /* we have a send device, set the source address */ sess->local_address.s_addr = lft_getifaddr(sess->pcap_dev); } }; #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) #ifdef HAVE_NCURSES /* In watch mode the pcap handle is opened once (as root) and reused across * all sub-session cycles. Skip pcap_create/activate when already set. */ if (sess->pcapdescr == NULL) #endif { sess->pcapdescr = pcap_create(sess->pcap_dev, ebuf); if (sess->pcapdescr == NULL) { LFTErrHandler(sess, ERR_PCAP_DEV_UNAVAILABLE, ebuf); if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return; } pcap_set_snaplen(sess->pcapdescr, 1600); pcap_set_promisc(sess->pcapdescr, 0); pcap_set_timeout(sess->pcapdescr, 20); { int pcap_status = pcap_activate(sess->pcapdescr); if (pcap_status < 0) { LFTErrHandler(sess, ERR_PCAP_ACTIVATE_ERROR, pcap_geterr(sess->pcapdescr)); pcap_close(sess->pcapdescr); sess->pcapdescr = NULL; if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return; } else if (pcap_status > 0) { LFTErrHandler(sess, WRN_PCAP_ACTIVATE, pcap_geterr(sess->pcapdescr)); } } /* retain and inform data link type */ sess->pcap_datalink = pcap_datalink(sess->pcapdescr); #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) sess->skip_header_len = 0; #else /* use pcap datalink type to determine skip_header_len */ if (sess->pcap_datalink == DLT_RAW) sess->skip_header_len = 0; else if (sess->pcap_datalink == DLT_PPP) sess->skip_header_len += 4; else if (sess->pcap_datalink == DLT_NULL) sess->skip_header_len += 4; else if (sess->pcap_datalink == DLT_PPP_ETHER) sess->skip_header_len += (8 + (sizeof (struct ether_header))); else if (sess->pcap_datalink == DLT_LINUX_SLL) sess->skip_header_len += 16; else /* assume ethernet: linktype EN10MB */ sess->skip_header_len = sizeof (struct ether_header); /* if we're on what looks like a serial link, up the timeout (scatter will take care of itself) */ /* if ((sess->pcap_datalink == DLT_PPP || sess->pcap_datalink == DLT_LINUX_SLL) && (sess->timeout_ms == DEFAULT_TIMEOUT_MS)) sess->timeout_ms += 5000; */ #endif if (sess->noisy) { LFTEvtHandler(sess,EVT_DEVICE_SELECTED,NULL); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } return; } } #ifdef BSD_IP_STACK #ifndef NETBSD uint32_t bpfimmflag = 1; /* Instruct device to return packets immediately */ if (ioctl(pcap_fileno(sess->pcapdescr), BIOCIMMEDIATE, &bpfimmflag) < 0) { LFTErrHandler(sess, WRN_BIOCIMMEDIATE, pcap_strerror(errno)); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } if(sess->pcapdescr != 0) { pcap_close(sess->pcapdescr); sess->pcapdescr=0; } return; } } #endif #endif /* Set pcap non-blocking mode */ if (pcap_setnonblock(sess->pcapdescr, 1, ebuf) < 0) { LFTErrHandler(sess, ERR_PCAP_NONBLOCK_ERROR, ebuf); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } /* if(sess->hostname != NULL) free(sess->hostname); */ return; } } } /* end if (sess->pcapdescr == NULL) */ #endif if (sess->senddevsel > 0) init_address (sess, sess->hostname, sess->pcap_send_dev); else init_address (sess, sess->hostname, sess->pcap_dev); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } /* if(sess->hostname != NULL) free(sess->hostname); */ return; } if (!sess->seq_start) { sess->seq_start = rand(); /* pseudorandom ISN for TCP; probe-ID base for UDP */ } if (sess->noisy > 3 || (sess->noisy > 0 && sess->seq_start)) { LFTEvtHandler(sess, EVT_SHOW_INITIAL_SEQNUM, NULL); if(sess->exit_state < 0){ if(sess->wsess != NULL) { free(sess->wsess); sess->wsess = NULL; } /* if (sess->hostname != NULL) free(sess->hostname); */ return; } } open_sockets(sess); if(sess->exit_state < 0) { #ifdef HAVE_NCURSES /* In watch mode sub-sessions, send_sock and pcapdescr are borrowed from * proto_sess — never close them here; they will be reused next cycle. */ if (!sess->watch_mode) #endif { if(sess->send_sock > 0) { #if defined(WIN32) || defined(_WIN32) closesocket(sess->send_sock); #else close(sess->send_sock); #endif sess->send_sock = 0; } #if defined(WIN32) || defined(_WIN32) if(sess->recv_sock > 0) { closesocket(sess->recv_sock); sess->recv_sock = 0; } #else if(sess->pcapdescr != 0) { pcap_close(sess->pcapdescr); sess->pcapdescr=0; } #endif } /* end if (!sess->watch_mode) */ return; } #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) #ifndef LFT_DONT_USE_SAFE_UID if(!sess->check_seam && !sess->is_graphviz_subquery) setuid (getuid ()); #endif #endif if (sess->adaptive) { if (sess->retry_min < 2) sess->retry_min = 2; } if (sess->protocol==1) { if (sess->retry_min > 2) sess->retry_min = 2; } if (sess->retry_max < sess->retry_min) sess->retry_max = sess->retry_min; #ifdef HAVE_CARES /* Pre-fire PTR queries for the two addresses we already know about. * Hop addresses are queued as they are discovered in recv_packet(). */ lft_ares_fire(sess, sess->local_address); lft_ares_fire(sess, sess->remote_address); #endif gettimeofday (&(sess->begin_time), NULL); LFTEvtHandler(sess,EVT_TRACE_START,NULL); if(sess->exit_state<0) { if(sess->send_sock > 0) { #if defined(WIN32) || defined(_WIN32) closesocket(sess->send_sock); #else close(sess->send_sock); #endif sess->send_sock = 0; } #if defined(WIN32) || defined(_WIN32) if(sess->recv_sock > 0) { closesocket(sess->recv_sock); sess->recv_sock = 0; } #endif return; } if(sess->protocol<2) /*UDP or TCP*/ { #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) for (;;) { cygwin_process(sess); if(sess->exit_state<0) break; if(check_timeouts(sess)) break; if(sess->exit_state<0) break; } #else while (pcap_dispatch (sess->pcapdescr, -1, pcap_process_packet, (u_char *)sess) >= 0) { if(sess->exit_state<0) break; if (sess->noisy > 6) { LFTEvtHandler(sess,EVT_DBG_CHECKPOINT2,NULL); if(sess->exit_state < 0) break; } #ifdef HAVE_CARES lft_ares_poll(sess); /* harvest any completed PTR replies */ #endif if(check_timeouts (sess)) break; if(sess->exit_state < 0) break; } #ifdef HAVE_CARES lft_ares_wait(sess); /* drain remaining queries before display */ #endif #endif } else { if(sess->protocol<4) icmp_trace_main_loop(sess, LFTErrHandler, LFTEvtHandler); /*ICMP traces*/ else tcp_base_trace_main_loop(sess, LFTErrHandler, LFTEvtHandler); #ifdef HAVE_CARES lft_ares_wait(sess); /* drain remaining PTR queries before display */ #endif } /*{ int i; printf("\n"); for(i=0;idebugmapidx;i++) { if(sess->debugmap[i].type) { printf("%3d <<<", i); printf("%3d %5d phop=%2d %d.%d.%d.%d\n", sess->debugmap[i].hop, sess->debugmap[i].port, sess->debugmap[i].phop, (int)sess->debugmap[i].ip.S_un.S_un_b.s_b1, (int)sess->debugmap[i].ip.S_un.S_un_b.s_b2, (int)sess->debugmap[i].ip.S_un.S_un_b.s_b3, (int)sess->debugmap[i].ip.S_un.S_un_b.s_b4); } else { printf("%3d -->", i); printf("%3d %5d\n", sess->debugmap[i].hop, sess->debugmap[i].port); } } }*/ #ifdef HAVE_NCURSES /* In watch mode sub-sessions, the socket is borrowed from proto_sess and * must not be closed here — it is reused across all watch cycles. */ if (!sess->watch_mode) #endif { if(sess->send_sock > 0) { #if defined(WIN32) || defined(_WIN32) closesocket(sess->send_sock); #else close(sess->send_sock); #endif sess->send_sock = 0; } #if defined(WIN32) || defined(_WIN32) if(sess->recv_sock > 0) { closesocket(sess->recv_sock); sess->recv_sock = 0; } #endif } /* end if (!sess->watch_mode) */ } /*---------------------------------------------------------------------------*/ void setOutputStyle(int nstyle) { #ifdef HAVE_NCURSES if(nstyle<0 || nstyle>4) #else if(nstyle<0 || nstyle>3) #endif global_output_style=0; else global_output_style=nstyle; } int getOutputStyle(void) { return global_output_style; } int outputStyleIsXML(void) { if(global_output_style==1) return 1; return 0; } int outputStyleIsGraphViz(void) { if(global_output_style==2) return 1; return 0; } int outputStyleIsASCII(void) { if(global_output_style==3) return 1; return 0; } /*---------------------------------------------------------------------------*/ #define INCSIZE 500 static char * addtostringbuf(char * buf, size_t * bufsz, const char * addstr) { if(!buf || !(*bufsz)) { buf=malloc(INCSIZE); *bufsz=INCSIZE; buf[0]=0; } if(strlen(buf)+strlen(addstr)+1>(*bufsz)) { buf=realloc(buf, (*bufsz)+INCSIZE); *bufsz+=INCSIZE; } strncat(buf,addstr,(*bufsz) - strlen(buf) - 1); return buf; } /*---------------------------------------------------------------------------*/ static char * addNodeToRouteMap(char * routemap, size_t * routemapsz, char * prevlevelnodes, char ** currlevelnodes, size_t * currlevelnodessz, char * node) { char * pnode, * pnodeend; char pathbuff[100]; if((*currlevelnodes) && (*currlevelnodessz)) *currlevelnodes=addtostringbuf(*currlevelnodes, currlevelnodessz, ";"); *currlevelnodes=addtostringbuf(*currlevelnodes, currlevelnodessz, node); pnode=prevlevelnodes; pnodeend=pnode; while(pnodeend) { pnodeend=strchr(pnode,';'); if(pnodeend) *pnodeend=0; snprintf(pathbuff,100,"\t%s -> %s;\n",pnode,node); routemap=addtostringbuf(routemap, routemapsz, pathbuff); if(pnodeend) { *pnodeend=';'; pnode=pnodeend+1; } } return routemap; } /*---------------------------------------------------------------------------*/ static void UpdateLatency(const struct trace_packet_info_s *tp, double * minlat, double * maxlat, int * latinitialized) { double currlat=timediff_ms(tp->sent, tp->recv); if(!(*latinitialized)) { *minlat=currlat; *maxlat=currlat; *latinitialized=1; } else { if(*minlat > currlat) *minlat=currlat; if(*maxlat < currlat) *maxlat=currlat; } } /*---------------------------------------------------------------------------*/ static void PrintPacketInfoForGraphViz(lft_session_params * sess, const struct trace_packet_info_s *tp) { if(tp->recv.tv_sec) { if (tp->last_hop.s_addr != tp->hopaddr.s_addr) { if (sess->do_aslookup) { if (tp->asnumber) printf(" [%d]", tp->asnumber); /* suppress [AS?] in GraphViz output */ } if (sess->do_netlookup) { if((uintptr_t)tp->netname && strlen(tp->netname)>0 && strcmp(tp->netname,"NULL")!=0) printf(" [%s]", tp->netname); /* suppress unresolved net name in GraphViz output */ } if (tp->icmp_type < -2 || tp->icmp_type > 17) printf (" [icmp code %d]", tp->icmp_type); else { if (tp->icmp_type >= 0) printf (" [%s]", icmp_messages[tp->icmp_type + 1]); } printf(" "); print_host (sess, tp->hopaddr); if (tp->icmp_type == -1 && (sess->protocol<2 || sess->protocol>3)) printf(":%d",sess->dport); } } } /*---------------------------------------------------------------------------*/ void GraphVizOutput(lft_session_params * sess) { int reply,noreply,neglstart,neglend,anomtype,hopno,maxhop,icmpcode,holecount=0; char * rankstring=NULL; size_t rankstringsz=0; struct trace_packet_info_s *tp; char netnamecopy[512]; char * prevlevelnodes=NULL; size_t prevlevelnodessz=0; char * currlevelnodes=NULL; size_t currlevelnodessz=0; char * routemap=NULL; size_t routemapsz; char nodenamebuff[200]; double minlat, maxlat; char latencybuf[100]; int latinitialized; int asseam_hopno=-1; struct in_addr asseam_hopaddr; int netseam_hopno=-1; struct in_addr netseam_hopaddr; struct in_addr classbmask; struct in_addr masked_target; int prevasn=-1; struct in_addr prevasn_hopaddr; int prevasn_hopno; int lastishole; int netreached=0; int isseam; char cpath[1024]; int lastpos; if(sess->is_graphviz_subquery) return; if(sess->graphviz_icon_path) strncpy(cpath,sess->graphviz_icon_path,1022); else { if(!getcwd(cpath,1022)) { cpath[0]='.'; cpath[1]=DIRECTORY_SPLITTER; cpath[2]=0; } } lastpos=strlen(cpath)-1; if(cpath[lastpos]!=DIRECTORY_SPLITTER) { cpath[lastpos+1]=DIRECTORY_SPLITTER; cpath[lastpos+2]=0; } inet_aton("255.255.0.0", &classbmask); masked_target.s_addr=sess->remote_address.s_addr & classbmask.s_addr; printf("digraph %s {\n",GVGRAPHNAME); printf("\trankdir=TB;\n\tnode [fontname=%s,fontsize=%s];\n",GVFONTNAME,GVFONTSIZE); printf("\tSRC[%s, label=<",GVHOPSTYLE_SOURCE); prevlevelnodes=addtostringbuf(prevlevelnodes, &prevlevelnodessz, "SRC"); rankstring=addtostringbuf(rankstring, &rankstringsz, "Source"); printf("%s%s%s%s",GVNTBEG,cpath,GVNIMG_SOURCE,GVNTMID); print_host (sess, sess->local_address); if(sess->protocol==2 || sess->protocol==3) printf("%s>];\n",GVNTEND); else { if (sess->random_source) printf (":%d (pseudo-random)%s>];\n", sess->sport, GVNTEND); else printf (":%d%s>];\n", sess->sport, GVNTEND); } if (sess->num_hops) maxhop = sess->num_hops; else maxhop = sess->hop_info_length - 1; noreply = 0; reply = 0; neglstart=-1; neglend=-1; /* Find SEAMs at first*/ for(hopno = sess->ttl_min; hopno < sess->hop_info_length; hopno++) { icmpcode=-100; if(sess->hop_info[hopno].all_rcvd) { lastishole=0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if(tp->recv.tv_sec) { if(hopno<=maxhop) icmpcode=tp->icmp_type; if(tp->last_hop.s_addr != tp->hopaddr.s_addr) { if((tp->hopaddr.s_addr & classbmask.s_addr) == masked_target.s_addr) netreached=1; else { netseam_hopno=hopno; netseam_hopaddr=tp->hopaddr; } if(sess->do_aslookup || sess->do_netlookup) { if(prevasn==-1) { if(tp->asnumber) { prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } else { if(tp->asnumber) { if(tp->asnumber!=prevasn) { asseam_hopno=prevasn_hopno; asseam_hopaddr=prevasn_hopaddr; } prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } } } } } } else lastishole=1; if(icmpcode==-1) break; } if(!netreached) netseam_hopno=-1; if(lastishole) asseam_hopno=-1; for(hopno = sess->ttl_min; hopno < sess->hop_info_length; hopno++) { icmpcode=-100; latinitialized=0; anomtype=0; if((sess->hop_info[hopno].state == HS_SEND_FIN) && (sess->hop_info[hopno+1].state == HS_SEND_SYN) && (sess->hop_info[hopno+1].ts_last_recv.tv_sec)) anomtype=1; if((sess->hop_info[hopno].state != HS_SEND_SYN_ACK) && (sess->hop_info[hopno+1].state == HS_SEND_SYN_ACK) && (hopno == (sess->num_hops - 1))) anomtype=2; if((sess->hop_info[hopno].flags & HF_ENDPOINT) && (noreply >= ((maxhop - sess->ttl_min)/2)) && sess->num_hops > 3) anomtype=3; if(sess->hop_info[hopno].all_rcvd == 0) { reply=0; if(neglstart==-1) neglstart=hopno; neglend=hopno; } else { if(neglstart!=-1) { holecount++; printf("\tHOLE%d[%s, label=<",holecount,GVHOPSTYLE_HOLE); printf("%s%s%s%s",GVNTBEG,cpath,GVNIMG_HOLE,GVNTMID); snprintf(nodenamebuff,200,"HOLE%d",holecount); printf("Potentially Cloaked Device%s>];\n",GVNTEND); routemap=addNodeToRouteMap(routemap, &routemapsz, prevlevelnodes, &currlevelnodes, &currlevelnodessz, nodenamebuff); free(prevlevelnodes); prevlevelnodes=currlevelnodes; prevlevelnodessz=currlevelnodessz; currlevelnodes=NULL; currlevelnodessz=0; if(neglstart == neglend) snprintf(nodenamebuff,200,"\"No reply received from TTL %d\"", neglstart+1); else snprintf(nodenamebuff,200,"\"No reply received from TTLs %d through %d\"", neglstart+1, neglend+1); rankstring=addtostringbuf(rankstring, &rankstringsz, " -> "); rankstring=addtostringbuf(rankstring, &rankstringsz, nodenamebuff); neglstart=-1; neglend=-1; } int cnt=0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if(tp->recv.tv_sec) { const char * img=GVNIMG_REGULAR; cnt++; if(hopno<=maxhop) icmpcode=tp->icmp_type; reply = 1; strncpy(netnamecopy, tp->netname, 511); if(tp->last_hop.s_addr != tp->hopaddr.s_addr) { if(icmpcode==-1) { printf("\tTRG["); snprintf(nodenamebuff,200,"TRG"); if(sess->target_open > 0) { printf("%s, label=<%s%s", GVHOPSTYLE_TARGET_OPEN,GVNTBEG,cpath); img=GVNIMG_TRGOPEN; } else { if(sess->target_filtered) { printf("%s, label=<%s%s", GVHOPSTYLE_TARGET_FILTERED,GVNTBEG,cpath); img=GVNIMG_TRGFILTERED; } else { printf("%s, label=<%s%s", GVHOPSTYLE_TARGET_CLOSED, GVNTBEG, cpath); img=GVNIMG_TRGCLOSED; } } } else { printf("\tHOP%d_%d[",hopno+1,cnt); snprintf(nodenamebuff,200,"HOP%d_%d",hopno+1,cnt); switch (anomtype) { case 1: printf("%s, label=<%s%s", GVHOPSTYLE_ANOMALY1, GVNTBEG, cpath); img=GVNIMG_ANOMALY1; break; case 2: printf("%s, label=<%s%s", GVHOPSTYLE_ANOMALY2, GVNTBEG, cpath); img=GVNIMG_ANOMALY2; break; case 3: printf("%s, label=<%s%s", GVHOPSTYLE_ANOMALY3, GVNTBEG, cpath); img=GVNIMG_ANOMALY3; break; default: printf("%s, label=<%s%s", GVHOPSTYLE_BASE, GVNTBEG, cpath); img=GVNIMG_REGULAR; break; } } if(img==GVNIMG_REGULAR && ((hopno==asseam_hopno && tp->hopaddr.s_addr==asseam_hopaddr.s_addr) || (hopno==netseam_hopno && tp->hopaddr.s_addr==netseam_hopaddr.s_addr))) img=GVNIMG_SEAM; routemap=addNodeToRouteMap(routemap, &routemapsz, prevlevelnodes, &currlevelnodes, &currlevelnodessz, nodenamebuff); UpdateLatency(tp,&minlat,&maxlat,&latinitialized); printf("%s%s",img,GVNTMID); PrintPacketInfoForGraphViz(sess,tp); isseam=0; if(hopno==asseam_hopno && tp->hopaddr.s_addr==asseam_hopaddr.s_addr) { printf("(AS-Method Seam"); isseam=1; } if(hopno==netseam_hopno && tp->hopaddr.s_addr==netseam_hopaddr.s_addr) { printf("(Network-Method Seam"); isseam=1; } if(isseam) { if(sess->check_seam) { printf(": "); char hostname[100]; lft_session_params * subsess=LFTSessionOpen(); strncpy(hostname, inet_ntoa(tp->hopaddr),100); subsess->senddevsel = 1; subsess->senddev = sess->pcap_send_dev; subsess->userdevsel = 1; subsess->userdev = sess->pcap_dev; subsess->auto_ports=0; subsess->dport=179; subsess->seq_start=30; subsess->retry_min=1; subsess->retry_max=1; subsess->resolve_names=0; subsess->ahead_limit=1; subsess->break_on_icmp = 0; subsess->is_graphviz_subquery=1; subsess->hostname=hostname; subsess->hostname_lsrr_size = 0; LFTExecute(subsess); if(subsess->target_open > 0) printf("Vulnerable/Handshake"); else { if(subsess->target_filtered) printf("Protected/Filtered"); else printf("Vulnerable/Shun"); } LFTSessionClose(subsess); } printf(")"); } printf("%s>];\n",GVNTEND); } else UpdateLatency(tp,&minlat,&maxlat,&latinitialized); } } if(minlat==maxlat) snprintf(latencybuf,100,": %.1fms",minlat); else snprintf(latencybuf,100,": %.1f - %.1fms",minlat,maxlat); if(icmpcode==-1) { rankstring=addtostringbuf(rankstring, &rankstringsz, " -> \"Destination"); if (sess->protocol==0 || sess->protocol==4) { rankstring=addtostringbuf(rankstring, &rankstringsz, " [target "); if (sess->target_open > 0) rankstring=addtostringbuf(rankstring, &rankstringsz, "open]"); else { if(sess->target_filtered > 0) rankstring=addtostringbuf(rankstring, &rankstringsz, "filtered]"); else rankstring=addtostringbuf(rankstring, &rankstringsz, "closed]"); } } rankstring=addtostringbuf(rankstring, &rankstringsz, latencybuf); } else { snprintf(nodenamebuff,200,"\"%d", hopno+1); rankstring=addtostringbuf(rankstring, &rankstringsz, " -> "); rankstring=addtostringbuf(rankstring, &rankstringsz, nodenamebuff); rankstring=addtostringbuf(rankstring, &rankstringsz, latencybuf); } switch (anomtype) { case 1: rankstring=addtostringbuf(rankstring, &rankstringsz, "\\n"); rankstring=addtostringbuf(rankstring, &rankstringsz, GV_ANOMALY1_TEXT); break; case 2: rankstring=addtostringbuf(rankstring, &rankstringsz, "\\n"); rankstring=addtostringbuf(rankstring, &rankstringsz, GV_ANOMALY2_TEXT); break; case 3: rankstring=addtostringbuf(rankstring, &rankstringsz, "\\n"); rankstring=addtostringbuf(rankstring, &rankstringsz, GV_ANOMALY3_TEXT); break; default: break; } rankstring=addtostringbuf(rankstring, &rankstringsz, "\""); free(prevlevelnodes); prevlevelnodes=currlevelnodes; prevlevelnodessz=currlevelnodessz; currlevelnodes=NULL; currlevelnodessz=0; } if(icmpcode==-1) break; } if(neglstart!=-1) { holecount++; printf("\tHOLE%d[%s, label=<",holecount,GVHOPSTYLE_HOLE); printf("%s%s%s%s",GVNTBEG,cpath,GVNIMG_HOLE,GVNTMID); snprintf(nodenamebuff,200,"HOLE%d",holecount); printf("No Response%s>];\n",GVNTEND); routemap=addNodeToRouteMap(routemap, &routemapsz, prevlevelnodes, &currlevelnodes, &currlevelnodessz, nodenamebuff); free(prevlevelnodes); prevlevelnodes=currlevelnodes; prevlevelnodessz=currlevelnodessz; currlevelnodes=NULL; currlevelnodessz=0; snprintf(nodenamebuff,200,"\"No reply received after TTL %d\"", neglstart+1); rankstring=addtostringbuf(rankstring, &rankstringsz, " -> "); rankstring=addtostringbuf(rankstring, &rankstringsz, nodenamebuff); neglstart=-1; neglend=-1; } free(prevlevelnodes); printf("\tranksep=equally;\n\t{\n\t\tnode [shape=plaintext];\n\t\t%s;\n\t}\n",rankstring); free(rankstring); printf("%s",routemap); free(routemap); printf("}\n"); } /*---------------------------------------------------------------------------*/ /* * ASCIIOutput() -- horizontal hop diagram for -o * * Layout: each hop occupies a column of width W_i = max of all per-hop * string widths. Every line pads every column to exactly W_i chars and * every separator slot to exactly 4 chars (" -> " on the hostname row, * " " on all other rows). Columns are grouped into rows that fit * within the terminal width (TIOCGWINSZ, $COLUMNS, or 80 fallback). * * Per-column strings built: * hdr : "[ HOP N ]" (or "[ SOURCE ]" / "[ TARGET ]") * host : result of format_host() — hostname (ip) or just ip * sub : ip line when host has a hostname prefix (may be empty) * asn : "[AS NNNNN]" or "[AS?]" or "" (when -A used) * rtt : "N.Nms" or "" (holes have no rtt) * ann : annotation: "*SEAM*", "*FW?*", "[open]", "[closed]", etc. * * Terminal capability detection (runs once, result stored in locals): * use_unicode : box-drawing chars ┌─ HOP N ─┐ vs [- HOP N -] * use_color : ANSI color codes */ /* helper: write 'n' copies of char 'c' to stdout */ static void ascii_repeat(char c, int n) { int i; for (i = 0; i < n; i++) putchar(c); } /* helper: display column count for a UTF-8 string. * UTF-8 continuation bytes (0x80-0xBF) are not counted; every other byte * (ASCII or a multi-byte leading byte) counts as one display column. * Correct for all BMP characters that occupy a single terminal cell, * which covers every glyph emitted by ASCIIOutput(). */ static int utf8_display_width(const char *s) { int w = 0; while (*s) { unsigned char b = (unsigned char)*s++; if ((b & 0xC0) != 0x80) /* not a continuation byte */ w++; } return w; } /* Truncate string s in-place so its display width is ≤ max_w columns. * Backs up over UTF-8 continuation bytes to avoid splitting a code point. */ static void ascii_utf8_truncate_to(char *s, int max_w) { int i = (int)strlen(s); while (i > 0 && utf8_display_width(s) > max_w) { i--; while (i > 0 && ((unsigned char)s[i] & 0xC0) == 0x80) i--; s[i] = '\0'; } } /* helper: center string 's' within field of width w (display columns), * padding with spaces. Uses utf8_display_width() so multi-byte glyphs * are measured in columns, not bytes. */ static void ascii_center(const char *s, int w) { int sl = utf8_display_width(s); int left = (w - sl) / 2; int right = w - sl - left; ascii_repeat(' ', left); fputs(s, stdout); ascii_repeat(' ', right); } /* helper: build the host string into buf (like print_host but into a buffer) */ static void format_host_buf(lft_session_params *sess, struct in_addr addr, char *hostbuf, int hostbufsz, char *ipbuf, int ipbufsz) { char hbuf[NI_MAXHOST]; const char *ip = inet_ntoa(addr); int resolved = 0; hostbuf[0] = '\0'; ipbuf[0] = '\0'; #ifdef HAVE_CARES if (sess->ares_chan && !sess->async_dns_disabled) { int i; for (i = 0; i < sess->dns_cache_count; i++) { if (sess->dns_cache[i].addr.s_addr == addr.s_addr && sess->dns_cache[i].name[0]) { snprintf(hostbuf, hostbufsz, "%s", sess->dns_cache[i].name); if (!sess->hostnames_only) snprintf(ipbuf, ipbufsz, "%s", ip); resolved = 1; break; } } if (!resolved) snprintf(hostbuf, hostbufsz, "%s", ip); return; } #endif if (!sess->resolve_names) { snprintf(hostbuf, hostbufsz, "%s", ip); return; } /* synchronous reverse DNS */ { struct sockaddr_in sa; int rc; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr = addr; #ifndef WIN32 lft_revdns_timed_out = 0; signal(SIGALRM, lft_sigalrm); alarm(LFT_REVDNS_TIMEOUT); rc = getnameinfo((struct sockaddr *)&sa, sizeof(sa), hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD); alarm(0); signal(SIGALRM, SIG_DFL); if (lft_revdns_timed_out) rc = EAI_AGAIN; #else rc = lft_getnameinfo_timed((struct sockaddr *)&sa, sizeof(sa), hbuf, sizeof(hbuf), NI_NAMEREQD); #endif if (rc == 0) { snprintf(hostbuf, hostbufsz, "%s", hbuf); if (!sess->hostnames_only) snprintf(ipbuf, ipbufsz, "%s", ip); } else { snprintf(hostbuf, hostbufsz, "%s", ip); } } } /* ANSI color codes */ #define AC_RESET "\033[0m" #define AC_BOLD "\033[1m" #define AC_DIM "\033[2m" #define AC_CYAN "\033[36m" #define AC_YELLOW "\033[33m" #define AC_GREEN "\033[32m" #define AC_BGREEN "\033[92m" #define AC_RED "\033[31m" #define AC_BRED "\033[91m" #define AC_ORANGE "\033[38;5;208m" /* xterm-256 bright orange */ #define AC_LABEL "\033[38;5;147m" /* xterm-256 light steel blue — labels */ #define AC_WHITE "\033[97m" #define AC_BBLUE "\033[1;34m" #define AC_BLUE "\033[34m" /* dark blue — outline/frame chars */ /* RTT chart constants */ #define CHART_HEIGHT 8 /* character rows in the chart body */ #define CHART_SEP " " /* 4-space separator between columns */ #define JITTER_WARN_MS 25.0 /* jitter alert threshold (ms) */ /* Unicode box-drawing strings */ #define UC_TL "\xe2\x94\x8c" /* ┌ */ #define UC_TR "\xe2\x94\x90" /* ┐ */ #define UC_BL "\xe2\x94\x94" /* └ */ #define UC_BR "\xe2\x94\x98" /* ┘ */ #define UC_H "\xe2\x94\x80" /* ─ */ #define UC_LS "[" /* ASCII fallback left */ #define UC_RS "]" /* ASCII fallback right */ /* Maximum hops we'll handle in one pass */ #define ASCII_MAX_HOPS 256 #define ASCII_MAX_COL_W 17 /* max column width; fields wider than this are truncated */ /* Per-hop column data */ struct ascii_col { char hdr[64]; /* "[ HOP N ]" */ char host[256]; /* hostname or ip */ char sub[128]; /* "(ip)" when host has name; else "" */ char asn[32]; /* "[AS 12345]" or "" */ char net[64]; /* "[net-name]" or "" (when -A used) */ char org[64]; /* "[org-name]" or "" (when -A used) */ char rtt[32]; /* "12.3ms" or "10/12/15/2ms" (stats) or "" */ char ann[64]; /* "*SEAM*", "*FW?*", "[open]", "⚡ jitter Nms ⚡" etc. */ char mtu[32]; /* "[MTU: 1492]" or "[MTU Limit]" when -K active */ hop_rtt_stats_t stats; /* per-hop RTT stats (when rtt_stats active; n==0 if none) */ char jitter_ann[32]; /* "⚡ΔNms" for chart label row (alert column only) */ int jitter_warn; /* 1 = this col has max jitter > JITTER_WARN_MS */ int is_hole; /* 1 if no reply */ int is_target; int is_source; int anomtype; /* 0=normal,1=fw stateful,2=fw flag,3=bsd bug */ int w; /* column width = max of all string lengths */ }; /* Render the ┌─ HOP N ─┐ header row for a slice of columns. * Shared between the chart context (renders above the chart) and the * hop-diagram context (renders above hostname/RTT/annotation rows). */ static void render_header_row(const struct ascii_col *cols, int start, int end, int use_color, int use_unicode, int flip_corners) { int i; for (i = start; i <= end; i++) { const struct ascii_col *c = &cols[i]; int hlen = (int)strlen(c->hdr); if (use_unicode) { /* build "┌─ HOP N ─┐" (or └─ … ─┘ when flip_corners). * Frame chars (└┘┌┐─) in blue, label text in cyan. */ int inner = c->w - 2; int dashes_l, dashes_r, label_len; char label[64]; if (hlen >= 4) { snprintf(label, sizeof(label), "%.*s", hlen - 4, c->hdr + 2); /* e.g. "HOP 1" */ label_len = (int)strlen(label); } else { snprintf(label, sizeof(label), "%s", c->hdr); label_len = hlen; } dashes_l = (inner - label_len - 2) / 2; dashes_r = inner - label_len - 2 - dashes_l; if (dashes_l < 0) dashes_l = 0; if (dashes_r < 0) dashes_r = 0; if (use_color) fputs(AC_BLUE, stdout); fputs(flip_corners ? UC_BL : UC_TL, stdout); { int d; for (d=0;dw - 2; int label_len; char label[64]; if (hlen >= 4) { snprintf(label, sizeof(label), "%.*s", hlen - 4, c->hdr + 2); label_len = (int)strlen(label); } else { snprintf(label, sizeof(label), "%s", c->hdr); label_len = hlen; } { int dashes_l = (inner - label_len - 2) / 2; int dashes_r = inner - label_len - 2 - dashes_l; if (dashes_l < 0) dashes_l = 0; if (dashes_r < 0) dashes_r = 0; putchar(flip_corners ? '{' : '['); ascii_repeat('-', dashes_l); fprintf(stdout, " %s ", label); ascii_repeat('-', dashes_r); putchar(flip_corners ? '}' : ']'); } } if (use_color) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } /* Render the RTT candlestick chart above one screen-slice of hop columns. * g_min / g_max are the global RTT bounds across the entire trace (for uniform scale). * Rows: header | jitter-alert label | max-RTT label | chart body (CHART_HEIGHT rows) | x-axis label. */ static void ascii_chart_render(const struct ascii_col *cols, int start, int end, double g_min, double g_max, int use_color, int use_unicode, const char *info_str) { int i, r; double range = (g_max > g_min + 0.1) ? (g_max - g_min) : 1.0; /* --- Y-axis geometry --- * yval_w: display width of the widest RTT label string e.g. "75ms" = 4. * Each chart row is prefixed with "NNms ┤ " (labeled, even rows) or * " │ " (unlabeled, odd rows). * Non-chart rows (header, labels) get the same width as blank spaces. * Total prefix display width = yval_w + 3. */ char _ymax_buf[16]; snprintf(_ymax_buf, sizeof(_ymax_buf), "%dms", (int)ceil(g_max > 0 ? g_max : 1.0)); int yval_w = (int)strlen(_ymax_buf); /* --- header row (repeated above chart for spatial context) --- */ if (use_color) fputs(AC_DIM, stdout); printf("%*s ", yval_w, ""); if (use_color) fputs(AC_RESET, stdout); render_header_row(cols, start, end, use_color, use_unicode, 0); /* --- max RTT label row --- */ { int any = 0; for (i = start; i <= end; i++) if (cols[i].stats.n > 0 || cols[i].is_hole) { any = 1; break; } if (any) { if (use_color) fputs(AC_DIM, stdout); printf("%*s ", yval_w, ""); if (use_color) fputs(AC_RESET, stdout); for (i = start; i <= end; i++) { const struct ascii_col *c = &cols[i]; if (c->stats.n > 0) { char lbl[24]; snprintf(lbl, sizeof(lbl), "%dms max", (int)round(c->stats.rtt_max)); if (use_color) fputs(c->jitter_warn ? AC_YELLOW : AC_GREEN, stdout); ascii_center(lbl, c->w); if (use_color) fputs(AC_RESET, stdout); } else if (c->is_hole) { /* Cloaked column — ░ padded with one space on each side */ if (use_color) fputs(AC_DIM, stdout); if (c->w >= 2) { int d; putchar(' '); for (d = 0; d < c->w - 2; d++) fputs(use_unicode ? "\xe2\x96\x91" : ":", stdout); putchar(' '); } else { int d; for (d = 0; d < c->w; d++) fputs(use_unicode ? "\xe2\x96\x91" : ":", stdout); } if (use_color) fputs(AC_RESET, stdout); } else { int d; for (d = 0; d < c->w; d++) putchar(' '); } if (i < end) fputs(CHART_SEP, stdout); } putchar('\n'); } } /* --- precompute per-column row indices --- */ /* Row 0 = top (highest RTT = g_max), Row CHART_HEIGHT-1 = bottom (g_min) */ int col_max_row[ASCII_MAX_HOPS]; int col_avg_row[ASCII_MAX_HOPS]; int col_min_row[ASCII_MAX_HOPS]; for (i = start; i <= end; i++) { const struct ascii_col *c = &cols[i]; if (c->stats.n > 0) { col_max_row[i] = (int)round((g_max - c->stats.rtt_max) / range * (CHART_HEIGHT - 1)); col_avg_row[i] = (int)round((g_max - c->stats.rtt_avg) / range * (CHART_HEIGHT - 1)); col_min_row[i] = (int)round((g_max - c->stats.rtt_min) / range * (CHART_HEIGHT - 1)); /* clamp */ if (col_max_row[i] < 0) col_max_row[i] = 0; if (col_max_row[i] >= CHART_HEIGHT) col_max_row[i] = CHART_HEIGHT - 1; if (col_avg_row[i] < 0) col_avg_row[i] = 0; if (col_avg_row[i] >= CHART_HEIGHT) col_avg_row[i] = CHART_HEIGHT - 1; if (col_min_row[i] < 0) col_min_row[i] = 0; if (col_min_row[i] >= CHART_HEIGHT) col_min_row[i] = CHART_HEIGHT - 1; /* ensure max_row <= avg_row <= min_row */ if (col_avg_row[i] < col_max_row[i]) col_avg_row[i] = col_max_row[i]; if (col_avg_row[i] > col_min_row[i]) col_avg_row[i] = col_min_row[i]; /* force minimum 1-row visual spread when rtt_min != rtt_max */ if (c->stats.rtt_min != c->stats.rtt_max && col_max_row[i] == col_min_row[i]) { if (col_min_row[i] < CHART_HEIGHT - 1) col_min_row[i]++; else if (col_max_row[i] > 0) col_max_row[i]--; /* re-clamp avg */ if (col_avg_row[i] < col_max_row[i]) col_avg_row[i] = col_max_row[i]; if (col_avg_row[i] > col_min_row[i]) col_avg_row[i] = col_min_row[i]; } } else { col_max_row[i] = col_avg_row[i] = col_min_row[i] = 0; } } /* --- chart body rows --- */ for (r = 0; r < CHART_HEIGHT; r++) { /* Y-axis prefix: labeled tick at even rows, bare pipe at odd rows */ if (use_color) fputs(AC_DIM, stdout); if (r % 2 == 0) { double row_rtt = g_max - (double)r / (CHART_HEIGHT - 1) * range; printf("%*dms ", yval_w - 2, (int)round(row_rtt)); fputs(use_unicode ? "\xe2\x94\xa4 " : "| ", stdout); /* ┤ */ } else { printf("%*s ", yval_w, ""); fputs(use_unicode ? "\xe2\x94\x82 " : "| ", stdout); /* │ */ } if (use_color) fputs(AC_RESET, stdout); for (i = start; i <= end; i++) { const struct ascii_col *c = &cols[i]; int cw = c->w; if (c->stats.n == 0) { if (c->is_hole) { /* Cloaked column — ░ across full chart height, padded * with one space on each side so the column doesn't * butt up against its neighbours. */ if (use_color) fputs(AC_DIM, stdout); if (cw >= 2) { int d; putchar(' '); for (d = 0; d < cw - 2; d++) fputs(use_unicode ? "\xe2\x96\x91" : ":", stdout); putchar(' '); } else { int d; for (d = 0; d < cw; d++) fputs(use_unicode ? "\xe2\x96\x91" : ":", stdout); } if (use_color) fputs(AC_RESET, stdout); } else { int d; for (d = 0; d < cw; d++) putchar(' '); } } else { int mr = col_max_row[i]; int ar = col_avg_row[i]; int mir = col_min_row[i]; int collapsed = (mr == mir); /* rtt_min == rtt_max exactly */ int in_shaft = (r >= mr && r <= mir); int is_max = (r == mr); int is_min = (r == mir); int is_avg = (r == ar); int d, mid; /* Color: avg block takes priority; then whisker caps white; * shaft/wick is dim. Applied before drawing, reset after. */ if (use_color && in_shaft) { if (is_avg) fputs(c->jitter_warn ? AC_YELLOW : AC_GREEN, stdout); else if (is_max || is_min) fputs(AC_WHITE, stdout); else fputs(AC_DIM, stdout); } /* Bars are 1-col thinner on each side (bw = cw-2) so they * sit inside the ┌─ HOP N ─┐ brackets above/below. */ int bw = (cw >= 2) ? (cw - 2) : cw; int pad = (cw >= 2) ? 1 : 0; if (!in_shaft) { /* blank above max whisker or below min whisker */ for (d = 0; d < cw; d++) putchar(' '); } else if (collapsed || is_avg) { /* avg body (or sole row when rtt_min==rtt_max): █ */ for (d = 0; d < pad; d++) putchar(' '); for (d = 0; d < bw; d++) fputs(use_unicode ? "\xe2\x96\x88" : "#", stdout); /* █ */ for (d = 0; d < pad; d++) putchar(' '); } else if (is_max) { /* max whisker cap: ──────┬────── */ for (d = 0; d < pad; d++) putchar(' '); mid = (bw - 1) / 2; for (d = 0; d < bw; d++) fputs(d == mid ? (use_unicode ? "\xe2\x94\xac" : "+") /* ┬ */ : (use_unicode ? "\xe2\x94\x80" : "-"), /* ─ */ stdout); for (d = 0; d < pad; d++) putchar(' '); } else if (is_min) { /* min whisker cap: ──────┴────── */ for (d = 0; d < pad; d++) putchar(' '); mid = (bw - 1) / 2; for (d = 0; d < bw; d++) fputs(d == mid ? (use_unicode ? "\xe2\x94\xb4" : "+") /* ┴ */ : (use_unicode ? "\xe2\x94\x80" : "-"), /* ─ */ stdout); for (d = 0; d < pad; d++) putchar(' '); } else { /* shaft / wick between avg and whisker: single │ centered */ ascii_center(use_unicode ? "\xe2\x94\x82" : "|", cw); /* │ */ } if (use_color) fputs(AC_RESET, stdout); } /* inter-column separator */ if (i < end) fputs(CHART_SEP, stdout); } putchar('\n'); } /* --- min RTT label row (mirrors the max label row above the chart) --- */ { int any = 0; for (i = start; i <= end; i++) if (cols[i].stats.n > 0 || cols[i].is_hole) { any = 1; break; } if (any) { if (use_color) fputs(AC_DIM, stdout); printf("%*s ", yval_w, ""); if (use_color) fputs(AC_RESET, stdout); for (i = start; i <= end; i++) { const struct ascii_col *c = &cols[i]; if (c->stats.n > 0) { char lbl[24]; snprintf(lbl, sizeof(lbl), "%dms min", (int)round(c->stats.rtt_min)); if (use_color) fputs(c->jitter_warn ? AC_YELLOW : AC_GREEN, stdout); ascii_center(lbl, c->w); if (use_color) fputs(AC_RESET, stdout); } else if (c->is_hole) { /* Cloaked column — ░ padded one space each side */ if (use_color) fputs(AC_DIM, stdout); if (c->w >= 2) { int d; putchar(' '); for (d = 0; d < c->w - 2; d++) fputs(use_unicode ? "\xe2\x96\x91" : ":", stdout); putchar(' '); } else { int d; for (d = 0; d < c->w; d++) fputs(use_unicode ? "\xe2\x96\x91" : ":", stdout); } if (use_color) fputs(AC_RESET, stdout); } else { int d; for (d = 0; d < c->w; d++) putchar(' '); } if (i < end) fputs(CHART_SEP, stdout); } putchar('\n'); } } /* --- X-axis: └─────┤ RTT Nmin–Nmax ms, N hops to ├───── --- * Suppressed entirely when info_str is NULL or empty. */ { int any = 0; for (i = start; i <= end; i++) if (cols[i].stats.n > 0) { any = 1; break; } if (any && info_str && info_str[0]) { int total_w = 0; for (i = start; i <= end; i++) { total_w += cols[i].w; if (i < end) total_w += 4; } /* Build the bracketed label: * Unicode: "┤ End-to-End RTT 3–81ms, 14 hops distance to 1.2.3.4 ├" * ASCII: "| End-to-End RTT 3-81ms, 14 hops distance to 1.2.3.4 |" * info_str is pre-formatted by ASCIIOutput() from the target column. */ char range_lbl[256]; if (use_unicode) snprintf(range_lbl, sizeof(range_lbl), "\xe2\x94\xa4 %s \xe2\x94\x9c", /* ┤ … ├ */ info_str ? info_str : ""); else snprintf(range_lbl, sizeof(range_lbl), "| %s |", info_str ? info_str : ""); /* Display width (not byte length) for correct dash math */ int label_disp = (int)utf8_display_width(range_lbl); /* Horizontal run = total_w minus the 2 display cols "└─" already emitted */ int dash_total = total_w - 2 - label_disp; int dash_l = dash_total / 2; int dash_r = dash_total - dash_l; if (dash_l < 0) dash_l = 0; if (dash_r < 0) dash_r = 0; /* Frame chars (└─┤├) in blue; label text inside ┤…├ in cyan. */ printf("%*s ", yval_w, ""); /* Y-axis blank margin */ if (use_unicode) { int d; if (use_color) fputs(AC_BLUE, stdout); fputs("\xe2\x94\x94\xe2\x94\x80", stdout); /* └─ */ for (d = 0; d < dash_l; d++) fputs("\xe2\x94\x80", stdout); /* range_lbl = "┤ info ├" — outline text in OS_SENT */ fputs("\xe2\x94\xa4", stdout); /* ┤ */ if (use_color) fputs(OS_SENT, stdout); fputs(" ", stdout); fputs(info_str ? info_str : "", stdout); fputs(" ", stdout); if (use_color) fputs(AC_BLUE, stdout); fputs("\xe2\x94\x9c", stdout); /* ├ */ for (d = 0; d < dash_r; d++) fputs("\xe2\x94\x80", stdout); if (use_color) fputs(AC_RESET, stdout); } else { if (use_color) fputs(AC_BLUE, stdout); fputs("+-", stdout); ascii_repeat('-', dash_l); fputs("|", stdout); if (use_color) fputs(OS_SENT, stdout); fputs(" ", stdout); fputs(info_str ? info_str : "", stdout); fputs(" ", stdout); if (use_color) fputs(AC_BLUE, stdout); fputs("|", stdout); ascii_repeat('-', dash_r); if (use_color) fputs(AC_RESET, stdout); } putchar('\n'); (void)range_lbl; /* kept for width math earlier, not emitted raw */ } } } void ASCIIOutput(lft_session_params *sess) { #ifndef WIN32 lft_o_spinner_stop(); /* erase spinner line before printing output */ #endif struct ascii_col cols[ASCII_MAX_HOPS]; int ncols = 0; int hopno, maxhop; int use_color = 0; int use_unicode = 0; int term_width = 80; int reply, noreply, neglstart, neglend; int prevasn = -1; struct in_addr classbmask, masked_target, prevasn_hopaddr; int prevasn_hopno = 0; int netreached = 0; int asseam_hopno = -1, netseam_hopno = -1; struct in_addr asseam_hopaddr, netseam_hopaddr; int lastishole = 0; struct trace_packet_info_s *tp; /* --- terminal capability detection --- */ #if !defined(WIN32) && !defined(_WIN32) { const char *term = getenv("TERM"); const char *colorterm = getenv("COLORTERM"); if (isatty(fileno(stdout)) && term && strncmp(term, "dumb", 4) != 0 && strncmp(term, "unknown", 7) != 0) { use_unicode = 1; if (!getenv("NO_COLOR") && (colorterm || strncmp(term, "xterm", 5) == 0 || strncmp(term, "screen", 6) == 0 || strncmp(term, "tmux", 4) == 0 || strncmp(term, "rxvt", 4) == 0)) use_color = 1; } /* terminal width */ { const char *cols_env = getenv("COLUMNS"); if (cols_env && atoi(cols_env) > 0) term_width = atoi(cols_env); #ifdef TIOCGWINSZ else { struct winsize ws; if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0) term_width = ws.ws_col; } #endif } } #endif /* --- determine SEAM positions (same logic as GraphVizOutput) --- */ inet_aton("255.255.0.0", &classbmask); masked_target.s_addr = sess->remote_address.s_addr & classbmask.s_addr; if (sess->num_hops) maxhop = sess->num_hops; else maxhop = sess->hop_info_length - 1; reply = noreply = 0; neglstart = neglend = -1; for (hopno = sess->ttl_min; hopno < sess->hop_info_length; hopno++) { int icmpcode = -100; if (sess->hop_info[hopno].all_rcvd) { lastishole = 0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec) { if (hopno <= maxhop) icmpcode = tp->icmp_type; if (tp->last_hop.s_addr != tp->hopaddr.s_addr) { if ((tp->hopaddr.s_addr & classbmask.s_addr) == masked_target.s_addr) netreached = 1; else { netseam_hopno = hopno; netseam_hopaddr = tp->hopaddr; } if (sess->do_aslookup || sess->do_netlookup) { if (prevasn == -1) { if (tp->asnumber) { prevasn = tp->asnumber; prevasn_hopno = hopno; prevasn_hopaddr = tp->hopaddr; } } else if (tp->asnumber && tp->asnumber != prevasn) { asseam_hopno = prevasn_hopno; asseam_hopaddr = prevasn_hopaddr; prevasn = tp->asnumber; prevasn_hopno = hopno; prevasn_hopaddr = tp->hopaddr; } else if (tp->asnumber && tp->asnumber == prevasn) { /* same AS — keep prevasn_hopno pointing at the * most recent hop in this AS so the seam is * attributed to the last hop before the boundary, * not the first (matches GraphViz behaviour) */ prevasn_hopno = hopno; prevasn_hopaddr = tp->hopaddr; } } } } } } else { lastishole = 1; } if (icmpcode == -1) break; } if (!netreached) netseam_hopno = -1; if (lastishole) asseam_hopno = -1; (void)asseam_hopaddr; (void)netseam_hopaddr; /* --- build SOURCE column --- */ { struct ascii_col *c = &cols[ncols++]; char hostbuf[256], ipbuf[128]; memset(c, 0, sizeof(*c)); c->is_source = 1; snprintf(c->hdr, sizeof(c->hdr), "[ SOURCE ]"); format_host_buf(sess, sess->local_address, hostbuf, sizeof(hostbuf), ipbuf, sizeof(ipbuf)); /* -o default: numeric only; -h overrides to show hostname */ if (!sess->hostnames_only) { snprintf(hostbuf, sizeof(hostbuf), "%s", inet_ntoa(sess->local_address)); ipbuf[0] = '\0'; } snprintf(c->host, sizeof(c->host), "%s", hostbuf); snprintf(c->sub, sizeof(c->sub), "%s", ipbuf); if (sess->protocol < 2 || sess->protocol > 3) { size_t hlen = strlen(c->host); snprintf(c->host + hlen, sizeof(c->host) - hlen, ":%d", sess->sport); } /* width (use display columns, not bytes, for correct Unicode accounting) */ c->w = utf8_display_width(c->hdr); if (utf8_display_width(c->host) > c->w) c->w = utf8_display_width(c->host); if (utf8_display_width(c->sub) > c->w) c->w = utf8_display_width(c->sub); if (utf8_display_width(c->rtt) > c->w) c->w = utf8_display_width(c->rtt); } /* --- build per-hop columns --- */ neglstart = neglend = -1; reply = noreply = 0; int found_target = 0; for (hopno = sess->ttl_min; hopno < sess->hop_info_length; hopno++) { int anomtype = 0; int icmpcode = -100; struct ascii_col *c; if (ncols >= ASCII_MAX_HOPS) break; if ((sess->hop_info[hopno].state == HS_SEND_FIN) && (sess->hop_info[hopno+1].state == HS_SEND_SYN) && (sess->hop_info[hopno+1].ts_last_recv.tv_sec)) anomtype = 1; if ((sess->hop_info[hopno].state != HS_SEND_SYN_ACK) && (sess->hop_info[hopno+1].state == HS_SEND_SYN_ACK) && (hopno == (sess->num_hops - 1))) anomtype = 2; if ((sess->hop_info[hopno].flags & HF_ENDPOINT) && (noreply >= ((maxhop - sess->ttl_min) / 2)) && sess->num_hops > 3) anomtype = 3; if (sess->hop_info[hopno].all_rcvd == 0) { reply = 0; if (neglstart == -1) neglstart = hopno; neglend = hopno; continue; /* collect hole range, emit as one column below */ } /* emit pending hole column before this reply hop */ if (neglstart != -1) { c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->is_hole = 1; if (neglstart == neglend) snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", neglstart + 1); else snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d-%d ]", neglstart + 1, neglend + 1); if (use_unicode) snprintf(c->host, sizeof(c->host), "cloaked"); else snprintf(c->host, sizeof(c->host), "?cloaked?"); /* TTL shown in the RTT row (dim color) — avoids a separate sub line */ snprintf(c->rtt, sizeof(c->rtt), neglstart == neglend ? "TTL %d" : "TTLs %d-%d", neglstart + 1, neglend + 1); c->w = utf8_display_width(c->hdr); if (utf8_display_width(c->host) > c->w) c->w = utf8_display_width(c->host); if (utf8_display_width(c->rtt) > c->w) c->w = utf8_display_width(c->rtt); neglstart = neglend = -1; if (ncols >= ASCII_MAX_HOPS) break; } /* reply hop */ SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (!tp->recv.tv_sec) continue; if (hopno <= maxhop) icmpcode = tp->icmp_type; if (tp->last_hop.s_addr == tp->hopaddr.s_addr) continue; c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->anomtype = anomtype; /* is this the target? */ if (icmpcode == -1) { c->is_target = 1; found_target = 1; snprintf(c->hdr, sizeof(c->hdr), "[ TARGET ]"); if (sess->target_open > 0) snprintf(c->ann, sizeof(c->ann), "[open]"); else if (sess->target_filtered) snprintf(c->ann, sizeof(c->ann), "[filtered]"); else if (sess->protocol == 2 || sess->protocol == 3) snprintf(c->ann, sizeof(c->ann), "[replied]"); else snprintf(c->ann, sizeof(c->ann), "[closed]"); } else { snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", hopno + 1); /* annotations — Unicode glyphs when terminal supports them */ if (anomtype == 1) { if (use_unicode) /* ⇶ = rightwards arrows: packets flowing through inspector */ snprintf(c->ann, sizeof(c->ann), "\xe2\x87\xb6 Stateful FW"); else snprintf(c->ann, sizeof(c->ann), "[Stateful FW]"); } else if (anomtype == 2) { if (use_unicode) /* ⚑ = black flag: flag-based filter */ snprintf(c->ann, sizeof(c->ann), "\xe2\x9a\x91 Flag FW"); else snprintf(c->ann, sizeof(c->ann), "[Flag FW]"); } else if (anomtype == 3) { if (use_unicode) /* ⇄ = right-over-left arrows: half-open / reversed state anomaly */ snprintf(c->ann, sizeof(c->ann), "\xe2\x87\x84 BSD Bug"); else snprintf(c->ann, sizeof(c->ann), "[BSD Bug]"); } else if (hopno == asseam_hopno) { if (use_unicode) /* ╳ = diagonal cross: boundary / crossing point */ snprintf(c->ann, sizeof(c->ann), "\xe2\x95\xb3 ASN Seam"); else snprintf(c->ann, sizeof(c->ann), "x-AS-Seam-x"); } else if (hopno == netseam_hopno) { if (use_unicode) snprintf(c->ann, sizeof(c->ann), "\xe2\x95\xb3 NET Seam"); else snprintf(c->ann, sizeof(c->ann), "x-Net-Seam-x"); } } /* hostname / address */ { char hostbuf[256], ipbuf[128]; format_host_buf(sess, tp->hopaddr, hostbuf, sizeof(hostbuf), ipbuf, sizeof(ipbuf)); /* -o default: numeric only; -h overrides to show hostname */ if (!sess->hostnames_only) { snprintf(hostbuf, sizeof(hostbuf), "%s", inet_ntoa(tp->hopaddr)); ipbuf[0] = '\0'; } snprintf(c->host, sizeof(c->host), "%s", hostbuf); snprintf(c->sub, sizeof(c->sub), "%s", ipbuf); } /* append :dport to target address */ if (c->is_target && (sess->protocol < 2 || sess->protocol > 3)) { size_t hlen = strlen(c->host); snprintf(c->host + hlen, sizeof(c->host) - hlen, ":%d", sess->dport); } /* ASN — suppress unresolved in -o output */ if (sess->do_aslookup && tp->asnumber) snprintf(c->asn, sizeof(c->asn), "AS%d", tp->asnumber); /* Net name (short route name) and org name — suppress blank/NULL. * Data is populated from pwhois bulk when do_netlookup or do_aslookup * is set; check the data itself so -N alone still shows netname. */ if (tp->netname[0] && strcmp(tp->netname, "NULL") != 0) { snprintf(c->net, sizeof(c->net), "%s", tp->netname); ascii_utf8_truncate_to(c->net, ASCII_MAX_COL_W); } if (tp->orgname[0] && strcmp(tp->orgname, "NULL") != 0) { snprintf(c->org, sizeof(c->org), "%s", tp->orgname); ascii_utf8_truncate_to(c->org, ASCII_MAX_COL_W); } /* IANA fallback: private / special-purpose addresses are never in * pWhoIs, so substitute RFC labels when the fields are still blank. * asn → RFC tag (e.g. "RFC6598"), only when -A requested * net → descriptive name (e.g. "Shared Address Space"), * only when -N requested (suppressed by default) * org → "IANA", always — gives the org row something to show * without -N and fills in cleanly alongside netname with -N */ if (sess->do_aslookup || sess->do_netlookup) { char _irfc[16], _iname[64]; if (lft_iana_v4_lookup(tp->hopaddr, _irfc, sizeof(_irfc), _iname, sizeof(_iname))) { if (!c->asn[0] && sess->do_aslookup) snprintf(c->asn, sizeof(c->asn), "%s", _irfc); if (!c->net[0] && sess->do_netlookup) { snprintf(c->net, sizeof(c->net), "%s", _iname); ascii_utf8_truncate_to(c->net, ASCII_MAX_COL_W); } if (!c->org[0]) snprintf(c->org, sizeof(c->org), "IANA"); } } /* RTT */ if (sess->rtt_stats) { c->stats = compute_hop_rtt_stats(sess, hopno); if (c->stats.n > 0) snprintf(c->rtt, sizeof(c->rtt), "%d/%d/%d/%dms", (int)round(c->stats.rtt_min), (int)round(c->stats.rtt_avg), (int)round(c->stats.rtt_max), (int)round(c->stats.rtt_stddev)); } else if (tp->recv.tv_sec) { snprintf(c->rtt, sizeof(c->rtt), "%.1fms", timediff_ms(tp->sent, tp->recv)); } /* PMTUD annotation — only show discovered MTU for replied hops. * Black holes (no-reply) are not in this column; they appear in * the neglected-hop hole columns and the post-trace summary. */ if (sess->pmtud && sess->hop_info[hopno].path_mtu > 0) snprintf(c->mtu, sizeof(c->mtu), "[MTU: %u]", (unsigned)sess->hop_info[hopno].path_mtu); /* column width = widest of all strings (display columns, not bytes). * host/sub can be long; cap them at ASCII_MAX_COL_W so wide hostnames * don't blow out the chart layout. asn/net/org are already capped. */ c->w = utf8_display_width(c->hdr); { int hw = utf8_display_width(c->host); if (hw > ASCII_MAX_COL_W) hw = ASCII_MAX_COL_W; if (hw > c->w) c->w = hw; } if (utf8_display_width(c->sub) > c->w) c->w = utf8_display_width(c->sub); if (utf8_display_width(c->asn) > c->w) c->w = utf8_display_width(c->asn); if (utf8_display_width(c->net) > c->w) c->w = utf8_display_width(c->net); if (utf8_display_width(c->org) > c->w) c->w = utf8_display_width(c->org); if (utf8_display_width(c->rtt) > c->w) c->w = utf8_display_width(c->rtt); if (utf8_display_width(c->ann) > c->w) c->w = utf8_display_width(c->ann); if (utf8_display_width(c->mtu) > c->w) c->w = utf8_display_width(c->mtu); reply = 1; noreply = 0; if (found_target) break; if (ncols >= ASCII_MAX_HOPS) break; } if (found_target) break; } /* trailing hole (path ended without target reply) */ if (neglstart != -1 && ncols < ASCII_MAX_HOPS) { struct ascii_col *c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->is_hole = 1; if (neglstart == neglend) snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", neglstart + 1); else snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d-%d ]", neglstart + 1, neglend + 1); if (use_unicode) snprintf(c->host, sizeof(c->host), "cloaked"); else snprintf(c->host, sizeof(c->host), "?cloaked?"); /* TTL shown in the RTT row (dim color) — avoids a separate sub line */ snprintf(c->rtt, sizeof(c->rtt), neglstart == neglend ? "TTL %d" : "TTLs %d-%d", neglstart + 1, neglend + 1); c->w = utf8_display_width(c->hdr); if (utf8_display_width(c->host) > c->w) c->w = utf8_display_width(c->host); if (utf8_display_width(c->rtt) > c->w) c->w = utf8_display_width(c->rtt); } /* --- RTT stats: global scale + jitter alert detection --- */ double g_rtt_min = 0.0, g_rtt_max = 0.0; if (sess->rtt_stats && !sess->no_ascii_chart) { int g_found = 0; int gi; double max_jitter = 0.0; int jitter_col = -1; for (gi = 0; gi < ncols; gi++) { if (cols[gi].stats.n > 0) { if (!g_found || cols[gi].stats.rtt_min < g_rtt_min) g_rtt_min = cols[gi].stats.rtt_min; if (!g_found || cols[gi].stats.rtt_max > g_rtt_max) g_rtt_max = cols[gi].stats.rtt_max; g_found = 1; { double jitter = cols[gi].stats.rtt_max - cols[gi].stats.rtt_min; if (jitter > max_jitter) { max_jitter = jitter; jitter_col = gi; } } } } if (jitter_col >= 0 && max_jitter > JITTER_WARN_MS) { int jms = (int)round(max_jitter); cols[jitter_col].jitter_warn = 1; snprintf(cols[jitter_col].jitter_ann, sizeof(cols[jitter_col].jitter_ann), use_unicode ? "\xe2\x9a\xa1 %dms \xce\x94" : "! %dms D", jms); /* diagram annotation row (only if no existing anomaly annotation) */ if (cols[jitter_col].ann[0] == '\0') snprintf(cols[jitter_col].ann, sizeof(cols[jitter_col].ann), use_unicode ? "\xe2\x9a\xa1 jitter %dms" : "! jitter %dms", jms); /* update column width for new strings */ if (utf8_display_width(cols[jitter_col].jitter_ann) > cols[jitter_col].w) cols[jitter_col].w = utf8_display_width(cols[jitter_col].jitter_ann); if (utf8_display_width(cols[jitter_col].ann) > cols[jitter_col].w) cols[jitter_col].w = utf8_display_width(cols[jitter_col].ann); } } /* --- render rows --- */ /* A row is a slice of cols[] that fits within term_width. * Arrow " -> " is 4 chars; we account for that between columns. * When the RTT chart is active its Y-axis prefix eats y_prefix_w columns, * so diagram rows get the same indentation to keep everything aligned. */ /* Y-axis prefix width (display cols): same formula as inside ascii_chart_render */ int y_prefix_w = 0; char _xa_info[256] = ""; /* x-axis inner label, pre-formatted */ /* Build target URL and info string — always used for the top header. * When rtt_stats is active we add the E2E RTT range; otherwise just * show the hop count and target so the header is always informative. */ { int n_hops = ncols - 1; /* columns shown minus source */ const char *tgt_name = inet_ntoa(sess->remote_address); /* Build "proto://IP[:port]" target URL */ char tgt_url[80]; switch (sess->protocol) { case 1: /* UDP */ snprintf(tgt_url, sizeof(tgt_url), "udp://%s:%d", tgt_name, sess->dport); break; case 2: /* ICMP */ case 3: /* ICMP RFC 1393 */ snprintf(tgt_url, sizeof(tgt_url), "icmp://%s", tgt_name); break; default: /* 0=TCP, 4=TCP basic */ snprintf(tgt_url, sizeof(tgt_url), "tcp://%s:%d", tgt_name, sess->dport); break; } if (sess->rtt_stats && !sess->no_ascii_chart) { /* Y-axis prefix width (only needed when chart is rendered) */ char _yb[16]; snprintf(_yb, sizeof(_yb), "%dms", (int)ceil(g_rtt_max > 0 ? g_rtt_max : 1.0)); y_prefix_w = (int)strlen(_yb) + 3; /* label + " ┤ " (3 display cols) */ /* Find target column for end-to-end RTT range */ double tgt_rtt_min = 0.0, tgt_rtt_max = 0.0; int found_tgt = 0; { int gi; for (gi = 0; gi < ncols; gi++) { if (cols[gi].is_target && cols[gi].stats.n > 0) { tgt_rtt_min = cols[gi].stats.rtt_min; tgt_rtt_max = cols[gi].stats.rtt_max; found_tgt = 1; break; } } } if (found_tgt) { /* Match --watch's format exactly: en-dash between min/max, * "hops to" (no "distance"), URL-style target. */ snprintf(_xa_info, sizeof(_xa_info), "End-to-End RTT %d\xe2\x80\x93%dms, %d hops to %s", (int)round(tgt_rtt_min), (int)round(tgt_rtt_max), n_hops, tgt_url); } else { /* Match --watch's no-RTT fallback: "target N hops" */ snprintf(_xa_info, sizeof(_xa_info), "%s %d hops", tgt_url, n_hops); } } else { /* No per-hop RTT stats — same fallback as --watch */ snprintf(_xa_info, sizeof(_xa_info), "%s %d hops", tgt_url, n_hops); } } int effective_width = term_width - y_prefix_w; if (effective_width < 20) effective_width = 20; /* ---- Top header: ┌──┤ LFT_LABEL ├[dl]┤ RTT_INFO ├[dr]┐ * Starts at col 0; spans full term_width. * LFT label is left-aligned (short fixed indent); RTT info is centered. * Always printed in -o mode; RTT range only present when -j is active. */ { const char *lft_label = "Layer Four Traceroute (LFT)"; if (use_unicode) { /* lft_end = ┌──┤ space label space ├ = lft_dw + 7 * rtt_section = ┤ space info space ├ = xa_dw + 4 * Center RTT on full term_width: * dash_l = (term_width - rtt_section) / 2 - lft_end */ int lft_dw = (int)utf8_display_width(lft_label); int xa_dw = _xa_info[0] ? (int)utf8_display_width(_xa_info) : 0; int lft_end = lft_dw + 7; int rtt_sec = xa_dw + 4; int total_dashes = term_width - lft_end - rtt_sec - 1; /* -1 for ┐ */ if (total_dashes < 0) total_dashes = 0; int dash_l = (term_width - rtt_sec) / 2 - lft_end; if (dash_l < 0) dash_l = 0; if (dash_l > total_dashes) dash_l = total_dashes; int dash_r = total_dashes - dash_l; int d; /* Frame chars in dark blue, outline text labels in medium slate * blue (xterm 75) — matches --watch's colour convention. */ if (use_color) fputs(AC_BLUE, stdout); fputs("\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80", stdout); /* ┌── */ fputs("\xe2\x94\xa4", stdout); /* ┤ */ if (use_color) fputs(OS_SENT, stdout); fputs(" ", stdout); fputs(lft_label, stdout); fputs(" ", stdout); if (use_color) fputs(AC_BLUE, stdout); fputs("\xe2\x94\x9c", stdout); /* ├ */ for (d = 0; d < dash_l; d++) fputs("\xe2\x94\x80", stdout); if (_xa_info[0]) { fputs("\xe2\x94\xa4", stdout); /* ┤ */ if (use_color) fputs(OS_SENT, stdout); fputs(" ", stdout); fputs(_xa_info, stdout); fputs(" ", stdout); if (use_color) fputs(AC_BLUE, stdout); fputs("\xe2\x94\x9c", stdout); /* ├ */ } for (d = 0; d < dash_r; d++) fputs("\xe2\x94\x80", stdout); fputs("\xe2\x94\x90", stdout); /* ┐ */ if (use_color) fputs(AC_RESET, stdout); putchar('\n'); } else { /* ASCII fallback — same centering logic */ int lft_dw = (int)strlen(lft_label); int xa_dw = _xa_info[0] ? (int)strlen(_xa_info) : 0; int lft_end = lft_dw + 7; int rtt_sec = xa_dw + 4; int total_dashes = term_width - lft_end - rtt_sec - 1; if (total_dashes < 0) total_dashes = 0; int dash_l = (term_width - rtt_sec) / 2 - lft_end; if (dash_l < 0) dash_l = 0; if (dash_l > total_dashes) dash_l = total_dashes; int dash_r = total_dashes - dash_l; int d; fputs("+--", stdout); fputs("|", stdout); fputs(" ", stdout); fputs(lft_label, stdout); fputs(" ", stdout); fputs("|", stdout); for (d = 0; d < dash_l; d++) putchar('-'); if (_xa_info[0]) { fputs("|", stdout); fputs(" ", stdout); fputs(_xa_info, stdout); fputs(" ", stdout); fputs("|", stdout); } for (d = 0; d < dash_r; d++) putchar('-'); fputs("+", stdout); putchar('\n'); } } { int start = 0; while (start < ncols) { int end = start; int used = cols[start].w; /* greedily add columns while they fit within the narrowed width */ while (end + 1 < ncols) { int needed = used + 4 + cols[end + 1].w; if (needed > effective_width) break; used = needed; end++; } /* ---- RTT chart (above header, only when rtt_stats active) ---- */ if (sess->rtt_stats && !sess->no_ascii_chart) { putchar('\n'); ascii_chart_render(cols, start, end, g_rtt_min, g_rtt_max, use_color, use_unicode, NULL); /* x-axis line suppressed; info in top header */ } /* ---- row header line (hdr strings) * └┘ when chart hangs above (rtt_stats), ┌┐ when no chart above. * Skip the leading \n when a chart was just drawn — the chart's * min-RTT row already ended with \n, so an extra \n here was * producing a redundant blank line between the chart and the * first hop-label row. */ if (!(sess->rtt_stats && !sess->no_ascii_chart)) putchar('\n'); if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); render_header_row(cols, start, end, use_color, use_unicode, (sess->rtt_stats && !sess->no_ascii_chart) ? 1 : 0); /* ---- hostname line (host strings, sep = " -> ") ---- */ { int i; if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; const char *col_color = AC_RESET; if (use_color) { if (c->is_hole) col_color = AC_DIM; else if (c->is_target) col_color = AC_BOLD; else if (c->anomtype > 0) col_color = AC_RED; else if (c->is_source) col_color = AC_BOLD; } if (use_color) fputs(col_color, stdout); ascii_center(c->host, c->w); if (use_color) fputs(AC_RESET, stdout); if (i < end) fputs(" -> ", stdout); } } putchar('\n'); /* ---- sub line (ip in parens, or TTL N for holes) ---- */ { int i, any = 0; for (i = start; i <= end; i++) if (cols[i].sub[0]) { any = 1; break; } if (any) { if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; if (use_color && c->is_hole) fputs(AC_DIM, stdout); ascii_center(c->sub, c->w); if (use_color && c->is_hole) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } } /* ---- RTT line ---- */ { int i, any = 0; for (i = start; i <= end; i++) if (cols[i].rtt[0]) { any = 1; break; } if (any) { if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; if (use_color && c->rtt[0]) fputs(c->is_hole ? AC_DIM : AC_GREEN, stdout); ascii_center(c->rtt, c->w); if (use_color && c->rtt[0]) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } } /* ---- ASN line (when -A used) ---- */ { int i, any = 0; for (i = start; i <= end; i++) if (cols[i].asn[0]) { any = 1; break; } if (any) { if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; if (use_color && c->asn[0]) fputs(c->is_hole ? AC_DIM : AC_YELLOW, stdout); ascii_center(c->asn, c->w); if (use_color && c->asn[0]) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } } /* ---- net-name line (when -A used) ---- */ { int i, any = 0; for (i = start; i <= end; i++) if (cols[i].net[0]) { any = 1; break; } if (any) { if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; if (use_color && c->net[0]) fputs(AC_YELLOW, stdout); ascii_center(c->net, c->w); if (use_color && c->net[0]) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } } /* ---- org-name line (when -A used, pwhois only) ---- */ { int i, any = 0; for (i = start; i <= end; i++) if (cols[i].org[0]) { any = 1; break; } if (any) { if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; if (use_color && c->org[0]) fputs(AC_YELLOW, stdout); ascii_center(c->org, c->w); if (use_color && c->org[0]) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } } /* ---- annotation line (*SEAM*, *FW?*, [OPEN], etc.) ---- */ { int i, any = 0; for (i = start; i <= end; i++) if (cols[i].ann[0]) { any = 1; break; } if (any) { if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; const char *ac = NULL; if (use_color && c->ann[0]) { if (c->is_target) { if (sess->target_open > 0) ac = AC_BGREEN; else if (sess->target_filtered) ac = AC_YELLOW; else if (sess->protocol == 2 || sess->protocol == 3) ac = AC_BGREEN; else ac = AC_BRED; } else if (c->anomtype > 0) ac = AC_RED; else ac = AC_ORANGE; } if (ac) fputs(ac, stdout); ascii_center(c->ann, c->w); if (ac) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } } /* ---- MTU line (when -K active and any hop has MTU info) ---- */ if (sess->pmtud) { int i, any = 0; for (i = start; i <= end; i++) if (cols[i].mtu[0]) { any = 1; break; } if (any) { if (y_prefix_w > 0) printf("%*s", y_prefix_w, ""); for (i = start; i <= end; i++) { struct ascii_col *c = &cols[i]; if (use_color && c->mtu[0]) fputs(AC_YELLOW, stdout); ascii_center(c->mtu, c->w); if (use_color && c->mtu[0]) fputs(AC_RESET, stdout); if (i < end) fputs(" ", stdout); } putchar('\n'); } } start = end + 1; } } putchar('\n'); } /*---------------------------------------------------------------------------*/ lft-3.98/INSTALL000644 000765 000024 00000030341 15163651453 013231 0ustar00vicstaff000000 000000 ####################################################### ## Installation Instructions for Layer Four Traceroute ####################################################### Instructions are provided for different platforms below. ####################################################### On WINDOWS: ####################################################### Building this program on Windows is possible without UNIX-like compatibility environments. To compile the program, use the Microsoft Visual Studio Express edition which is free and may be downloaded from http://www.microsoft.com You must also download and install the Windows Platform SDK which is available from the same web site. The platform SDK provides the necessary Winsock header files. Please read the documentation related to fully installing the Windows Platform SDK for use with Visual Studio Express or your alternative compiler to ensure all necessary files are placed in the appropriate locations, specifically the header files. Using VS Express, you may build LFT by following these instructions: Open a Command Prompt window (cmd.exe), change directories to the "lft" folder you downloaded, and type the following commands (from inside the LFT folder): "%VS80COMNTOOLS%vsvars32.bat" nmake -f makefile.vc This will create lft.exe and whob.exe in the current folder. Of course, you may move the files wherever you choose. Depending on the Windows platform, you may or may not have TCP raw sockets functionality, causing TCP traces to fail. However, in this case ICMP and UDP traces should still work. You may also have to explicitly allow lft.exe to use the network by creating a rule in Windows (or your 3rd-party) Firewall. ####################################################### On UNIX-like operating systems: ####################################################### Optional Dependencies ===================== LFT requires libpcap (mandatory) and optionally uses c-ares for asynchronous DNS resolution. c-ares eliminates the post-trace serial hostname lookup delay (typically 180-260ms per trace) and is strongly recommended. It is auto-detected by ./configure; install it before running ./configure to enable it: Debian/Ubuntu: apt-get install libc-ares-dev RHEL/Fedora: dnf install c-ares-devel macOS Homebrew: brew install c-ares FreeBSD ports: dns/c-ares MacPorts: port install c-ares ./configure searches the standard system paths plus common third-party prefix directories (/opt/homebrew, /usr/local, /opt/local, /opt/pkg) automatically, so no manual LDFLAGS or CPPFLAGS are needed on most systems. Some special options to consider when running './configure' are: 1. Using '--enable-gtod' is useful on platforms where BPF timestamps are not precise. This forces LFT to call gettimeofday() on each packet instead of relying on the timestamp in the packet's pcap header. 2. Using '--enable-universal' will automatically compile universal binaries on macOS for arm64 (Apple Silicon) and x86_64 (Intel) architectures. 3. Using '--disable-async-dns' disables c-ares support at compile time even if the library is installed. 4. Using '--with-cares=PATH' specifies the installation prefix for c-ares when it is installed in a non-standard location (e.g. --with-cares=/opt/cares). 5. Using '--with-pcap=PATH' specifies the installation prefix for libpcap when it is installed in a non-standard location. Below are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `config/configure.ac' is used to create `configure' by a program called `autoconf'. You only need `config/configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. To regenerate `configure' from the project root, run: autoconf config/configure.ac > configure The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' (as root or with sudo) to install the programs and any data files and documentation. On Linux, the installer will use setcap(8) to grant lft the raw socket and packet capture capabilities it needs without installing it setuid root. On FreeBSD and other platforms, lft is installed setuid root. In both cases root access is required to run `make install'. On macOS, lft requires setuid root. This is because lft uses two separate privilege paths: libpcap (via /dev/bpf*) for capturing response packets, and a raw socket (SOCK_RAW) for sending probe packets. BPF device permissions (e.g., ChmodBPF / access_bpf group) only cover the capture side -- they do not grant the ability to create raw sockets, which on macOS requires root. Unlike Linux, macOS has no setcap equivalent to selectively grant raw socket capability to a binary. `make install' performs this step automatically when run as root. If you are installing manually: sudo chown root lft && sudo chmod u+s lft This is standard practice for network diagnostic tools on macOS -- ping and traceroute ship setuid root for the same reason. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. lft-3.98/whois.c000644 000765 000024 00000277155 15174131552 013510 0ustar00vicstaff000000 000000 /* * Handle communication with whois servers. * * This file is part of the Prefix WhoIs Project. * See http://pwhois.org * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * * This software includes: * - simplified access to regular expressions * - tokenizer * - lookup functions for AS, NETNAME, ORGNAME * - works with the following sources: * ARIN, RIPE, APNIC, RADB, CYMRU, PWHOIS, RISWHOIS * - will do recursive lookups * - convenient framework for further whois digging * * To compile the standalone client: * cc -o whob whois.c -DSTANDALONE * * * Portions (c) Victor Oppleman (lft@oppleman.com) * Portions (c) 2011 Markus Gothe * Portions (c) 2002 Ugen Antsilevitch (ugen@xonix.com) * */ #include "lft_lib.h" #define PORT_WHOIS 43 #if defined(WIN32) || defined(_WIN32) #define read(a, b, c) recv(a, b, c, 0) #define write(a, b, c) send(a, b, c, 0) #define close(s) closesocket(s) #define snprintf _snprintf #endif #include "whois.h" #ifndef WIN32 #include #endif #if defined(BSD_IP_STACK) #define pcap_snprintf snprintf #endif /*#define ASSERT(x) if (!(x)) { fprintf(stderr, "Assertion ("#x") failed\n"); exit(EXIT_FAILURE); }*/ /* OPTIONS and variable initialization */ static char pwhois_server[] = "whois.pwhois.org"; static char myaddress_server[] = "myaddress.today"; static char radb_server[] = "whois.ra.net"; static char cymru_server[] = "whois.cymru.com"; static char arin_server[] = "whois.arin.net"; static char apnic_server[] = "whois.apnic.net"; static char ripe_server[] = "whois.ripe.net"; static char ripe_ris_server[] = "riswhois.ripe.net"; #ifdef STANDALONE const char *version = "3.98"; /* set version string for library and client */ const char *appname = "WhoB"; /* set application name */ static int go_interactive = 0; /* We'll wait on STDIN unless args suggest otherwise */ static int use_cymru = 0; /* Don't use Cymru by default */ static int use_riswhois = 0; /* Don't use RIPE NCC RIS by default */ static int display_orgname = 1; /* Display orgname by default */ static int display_aspath = 0; /* Don't display AS-PATH by default */ static int display_netname = 0; /* Don't display netname by default */ static int display_radb_as = 0; /* Don't display RADB Origin-AS by default */ static int show_routes_byasn = 0; /* Don't show all advertisements by default */ static int show_routes_bytransitasn = 0; /* Don't show all routes that transit ASN by default */ static int show_networks_byasn = 0; /* Don't show all networks by default */ static int show_contacts_byasn = 0; /* Don't show all contact info by default */ static int show_routes_byprefix = 0; /* Don't show all routes by prefix by default */ static int show_server_status = 0; /* Don't show pwhois server status by default */ static int show_cache_date = 0; /* Don't show pwhois cache date by default */ static int read_from_file = 0; /* Don't read input from file by default */ static int riswhoisfromfile = 0; /* Don't use riswhois by default for readfromfile */ static int cymrufromfile = 0; /* Don't use Cymru by default for readfromfile */ static int use_gigo = 1; /* Use GIGO feature by default */ static int use_stdin = 0; /* Don't use STDIN for bulk file input by default */ static const unsigned int max_hostname_input = 200; /* Maximum length of input from user */ static const int max_lines = 2500; /* Maximum lines to read from bulk file per query */ static const int line_size = 256; /* Maximum line length */ static char hostname[256]; #endif /* END of OPTIONS and variable initialization */ #if defined(WIN32) || defined(_WIN32) char * index(char *s, char c) { char *t; if (!s) return NULL; for (t = s; *t; t++) if (*t == c) { return t; } /* Return terminating \0 if specifically requested */ if (c == '\0') return t; return NULL; } #endif #if defined(WIN32) || defined(_WIN32) int inet_aton(const char *cp, struct in_addr *pin) { if (!pin) return -1; pin->s_addr = inet_addr(cp); return (pin->s_addr != -1) ? 1 : 0; } #endif typedef struct token_s { char *ptr; } token_t; static token_t * tokens(char *buf, const char *sep) { char *c, *c1; int size, cur; token_t *rt; if (!buf || !sep) return NULL; size = 1; for (c = buf; *c ; c++) if (index(sep, *c)) { size++; while (*c && index(sep, *c)) c++; } size++; /* for the NULL */ if (!(rt = (token_t *)malloc(size * sizeof(token_t)))) return NULL; memset(rt, 0, size * sizeof(token_t)); rt[0].ptr = buf; cur = 0; for (c = buf; *c ; c++) { if (index(sep, *c)) { c1 = c; while (*c && index(sep, *c)) c++; if (*c) rt[++cur].ptr = c; *c1 = '\0'; } } rt[++cur].ptr = NULL; return rt; } typedef struct ip_blk_s { unsigned int start; unsigned int end; } ip_blk_t; static ip_blk_t * w_blk2range(char *s_start, char *s_end) { struct in_addr in; unsigned int s, e; ip_blk_t *r; if (!s_start || !s_end) return NULL; if (!inet_aton(s_start, &in)) return NULL; s = ntohl(in.s_addr); if (!inet_aton(s_end, &in)) return NULL; e = ntohl(in.s_addr); if (!(r = malloc(sizeof(ip_blk_t)))) return NULL; r->start = s; r->end = e; return r; } static ip_blk_t * w_mask2range(char *addr, char *mask) { struct in_addr in; unsigned int s, m; ip_blk_t *r; if (!addr || !mask) return NULL; m = (unsigned int)strtoul(mask, (char **)NULL, 10); if (m > 32) return NULL; if (!inet_aton(addr, &in)) return NULL; s = ntohl(in.s_addr); if (!(r = malloc(sizeof(ip_blk_t)))) return NULL; r->start = s &~ (((unsigned)0xffffffff) >> m); r->end = s | (((unsigned)0xffffffff) >> m); return r; } static int rm_spaces(char* str) { /* Remove spaces (isspace()) from anywhere within a string ONLY operates on a null-terminated (\0) string! */ int j = -1; unsigned int i; if (!str) return 0; for (i=0; i<=strlen(str); i++) if (!(isspace(*(str+i)))) *(str+(++j)) = *(str+i); else continue; return 1; } static char *match_prefix(const char *prefix, const char *target) { /* Target will be something like "origin: AS22773" and prefix will be "origin:" and * we return a pointer to "AS22773" */ while (*prefix) { if (tolower(*prefix) != tolower(*target)) return NULL; prefix++; target++; } while (isspace(*target)) target++; /* strip out the leading AS from the number */ if (strncmp(target,"AS",2) == 0) target += 2; return strdup(target); } static ip_blk_t *match_iprange(char *target) { /* matches something like "1.2.3.4-5.6.7.8" */ char *pos, *dash, *beforedash; /* ip_blk_t *out; */ while (isspace(*target)) target++; pos = target; while (*pos && !isspace(*pos)) pos++; beforedash = strdup(target); beforedash[pos-target] = 0; dash = strchr(target, '-'); if (!dash) return NULL; dash++; while (isspace(*dash)) dash++; return w_blk2range(beforedash, dash); } static ip_blk_t *match_ipprefix(char *target) { /* matches something like 1.2.3.0/24 */ char *slash, *pos; char *beforeslash; /* ip_blk_t *out; */ while (isspace(*target)) target++; pos = target; while (*pos && !isspace(*pos) && *pos != '/') pos++; beforeslash = strdup(target); beforeslash[pos - target] = 0; slash = strchr(target, '/'); if (!slash) return NULL; slash++; return w_mask2range(beforeslash, slash); } static char *match_inparens(char *target) { /* matches something like " (HELLO)" and returns "HELLO" */ char *end, *res; target = strchr(target, '('); if (!target) return NULL; target++; end = strchr(target, ')'); if (!end) return NULL; res = strdup(target); res[end - target] = 0; return res; } static char *match_afterparens(char *target) { /* matches something like " (HELLO) xxx" and returns a pointer to "xxx" */ target = strchr(target, '('); if (!target) return NULL; target = strchr(target, ')'); if (!target) return NULL; target++; while(*target && isspace(*target)) target++; if (*target) return strdup(target); else return NULL; } static int stricontains(const char *haystack, const char *needle) { /* Search for a substring without a string library */ int i, j, match; i = 0, j = 0; while (haystack[i] != '\0') { while (tolower(haystack[i]) != tolower(needle[0]) && haystack[i] != '\0') i++; if (haystack[i] == '\0') return (-1); match = i; while (tolower(haystack[i]) == tolower(needle[j]) && haystack[i] != '\0' && needle[j] != '\0') { i++; j++; } if (needle[j] == '\0') return (match); if (haystack[i] == '\0') return (-1); i = match + 1; j = 0; } return 0; } #ifdef HAVE_CARES /* fd-state tracking for w_prefetch()'s private ares_channel */ #define W_ARES_MAX_FDS 4 struct w_ares_state { struct { ares_socket_t fd; unsigned int events; /* mask of ares_fd_eventflag_t */ } fd_table[W_ARES_MAX_FDS]; int nfds; }; static void w_ares_sock_state_cb(void *data, ares_socket_t fd, int readable, int writable) { struct w_ares_state *st = (struct w_ares_state *)data; int i; for (i = 0; i < st->nfds; i++) { if (st->fd_table[i].fd == fd) { if (!readable && !writable) { st->fd_table[i] = st->fd_table[--st->nfds]; } else { st->fd_table[i].events = (readable ? (unsigned)ARES_FD_EVENT_READ : 0u) | (writable ? (unsigned)ARES_FD_EVENT_WRITE : 0u); } return; } } if (!readable && !writable) return; if (st->nfds >= W_ARES_MAX_FDS) return; st->fd_table[st->nfds].fd = fd; st->fd_table[st->nfds].events = (readable ? (unsigned)ARES_FD_EVENT_READ : 0u) | (writable ? (unsigned)ARES_FD_EVENT_WRITE : 0u); st->nfds++; } /* Callback for w_prefetch() forward (A-record) lookups. */ static void w_ares_callback(void *arg, int status, int timeouts, struct ares_addrinfo *ai) { struct w_dns_entry *entry = (struct w_dns_entry *)arg; (void)timeouts; if (status == ARES_SUCCESS && ai && ai->nodes) { struct sockaddr_in *sin = (struct sockaddr_in *)ai->nodes->ai_addr; memcpy(&entry->addr, &sin->sin_addr, sizeof(struct in_addr)); } if (ai) ares_freeaddrinfo(ai); entry->resolved = 1; /* mark done whether succeeded or failed */ } /* Resolve a single whois server hostname on first use. * Finds the matching cache slot, fires one c-ares A-record lookup via * ares_getaddrinfo(), and drains via ares_process_fds() until resolved or * the 1-second deadline expires. No-op if already resolved or the server * is not in the known-server list. */ static void w_prefetch(whois_session_params *wsess, const char *serv) { int i; struct w_dns_entry *entry = NULL; ares_channel wchan; struct ares_options opts; struct w_ares_state st; struct ares_addrinfo_hints hints; if (!wsess || !serv) return; /* find the cache slot for this server */ for (i = 0; i < wsess->w_dns_cache_count; i++) { if (wsess->w_dns_cache[i].host && strcmp(wsess->w_dns_cache[i].host, serv) == 0) { entry = &wsess->w_dns_cache[i]; break; } } if (!entry) return; /* not a known whois server; getaddrinfo handles it */ if (entry->resolved) return; /* already resolved on a previous call */ /* fire a single lookup and drain with a 1-second hard deadline */ memset(&opts, 0, sizeof(opts)); memset(&st, 0, sizeof(st)); opts.timeout = 500; opts.tries = 2; opts.sock_state_cb = w_ares_sock_state_cb; opts.sock_state_cb_data = &st; if (ares_init_options(&wchan, &opts, ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB) != ARES_SUCCESS) return; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; ares_getaddrinfo(wchan, serv, NULL, &hints, w_ares_callback, entry); { struct timeval deadline, now; gettimeofday(&deadline, NULL); deadline.tv_sec += 1; for (;;) { struct timeval tv, remaining; ares_fd_events_t events[W_ARES_MAX_FDS]; size_t nevents = 0; int j, nfds = 0; fd_set rfd, wfd; if (entry->resolved) break; gettimeofday(&now, NULL); remaining.tv_sec = deadline.tv_sec - now.tv_sec; remaining.tv_usec = deadline.tv_usec - now.tv_usec; if (remaining.tv_usec < 0) { remaining.tv_sec--; remaining.tv_usec += 1000000; } if (remaining.tv_sec < 0) break; if (st.nfds == 0) { /* No fds yet — drive timeout processing only, then stop */ ares_process_fds(wchan, NULL, 0, 0); break; } FD_ZERO(&rfd); FD_ZERO(&wfd); for (j = 0; j < st.nfds; j++) { if (st.fd_table[j].events & ARES_FD_EVENT_READ) FD_SET(st.fd_table[j].fd, &rfd); if (st.fd_table[j].events & ARES_FD_EVENT_WRITE) FD_SET(st.fd_table[j].fd, &wfd); if ((int)st.fd_table[j].fd + 1 > nfds) nfds = (int)st.fd_table[j].fd + 1; } tv.tv_sec = 0; tv.tv_usec = (remaining.tv_sec == 0 && remaining.tv_usec < 10000) ? remaining.tv_usec : 10000; if (nfds > 0) select(nfds, &rfd, &wfd, NULL, &tv); for (j = 0; j < st.nfds; j++) { unsigned int ev = ARES_FD_EVENT_NONE; if (FD_ISSET(st.fd_table[j].fd, &rfd)) ev |= ARES_FD_EVENT_READ; if (FD_ISSET(st.fd_table[j].fd, &wfd)) ev |= ARES_FD_EVENT_WRITE; if (ev != ARES_FD_EVENT_NONE) { events[nevents].fd = st.fd_table[j].fd; events[nevents].events = ev; nevents++; } } ares_process_fds(wchan, events, nevents, 0); } } ares_destroy(wchan); } #endif /* HAVE_CARES */ /*---------------------------------------------------------------------------*/ /* Timed getaddrinfo() wrapper for whois server hostname resolution. * On Windows: spawns a thread and waits up to 1 s; kills if it doesn't finish. * On POSIX: arms SIGALRM for 1 s, restores on return. * Keeps the call-sites in w_ask() clean and platform-independent. */ #if defined(WIN32) || defined(_WIN32) struct w_gai_args { const char *serv; struct addrinfo hints; /* copy — thread must not touch caller's stack */ struct addrinfo **res; int rc; }; static DWORD WINAPI w_gai_thread(LPVOID arg) { struct w_gai_args *a = (struct w_gai_args *)arg; a->rc = getaddrinfo(a->serv, NULL, &a->hints, a->res); return 0; } static int w_getaddrinfo_timed(const char *serv, const struct addrinfo *hints, struct addrinfo **res) { struct w_gai_args a; HANDLE th; DWORD tid; a.serv = serv; a.hints = *hints; /* copy the hints struct into our local */ a.res = res; a.rc = EAI_AGAIN; th = CreateThread(NULL, 0, w_gai_thread, &a, 0, &tid); if (!th) return EAI_AGAIN; if (WaitForSingleObject(th, 1000) == WAIT_TIMEOUT) TerminateThread(th, 0); CloseHandle(th); return a.rc; } #else /* POSIX */ static volatile sig_atomic_t w_dns_timed_out; static void w_sigalrm(int sig) { (void)sig; w_dns_timed_out = 1; } static int w_getaddrinfo_timed(const char *serv, const struct addrinfo *hints, struct addrinfo **res) { int rc; w_dns_timed_out = 0; signal(SIGALRM, w_sigalrm); alarm(1); rc = getaddrinfo(serv, NULL, hints, res); alarm(0); signal(SIGALRM, SIG_DFL); if (w_dns_timed_out && rc != 0) rc = EAI_AGAIN; return rc; } #endif /* WIN32 */ /*---------------------------------------------------------------------------*/ whois_session_params *w_init(void) { /* int e; */ whois_session_params *wsess = (whois_session_params *)malloc(sizeof(whois_session_params)); wsess->w_noisy = 0; /* Don't show debug msgs by default */ memset(&(wsess->pw_serv), 0, sizeof(wsess->pw_serv)); wsess->consolidated_asn[0] = wsess->consolidated_asp[0] = wsess->consolidated_route[0] = wsess->consolidated_orgname[0] = wsess->consolidated_netname[0] ='?'; wsess->consolidated_asn[1] = wsess->consolidated_asp[1] = wsess->consolidated_route[1] = wsess->consolidated_orgname[1] = wsess->consolidated_netname[1] =0; memset(&wsess->tbuf, 0, sizeof(wsess->tbuf)); wsess->logprintfCookie = 0; #ifdef HAVE_CARES { /* Register all known whois server hostnames in the DNS cache so * w_prefetch() can find the right slot when w_ask() is first called * for each server. No lookups are fired here; resolution happens * lazily on first use via w_prefetch(). */ static const char *servers[] = { pwhois_server, myaddress_server, radb_server, cymru_server, arin_server, apnic_server, ripe_server, ripe_ris_server }; int ns = (int)(sizeof(servers) / sizeof(servers[0])); int i; memset(wsess->w_dns_cache, 0, sizeof(wsess->w_dns_cache)); wsess->w_dns_cache_count = 0; for (i = 0; i < ns && i < 8; i++) { wsess->w_dns_cache[i].host = servers[i]; wsess->w_dns_cache[i].resolved = 0; memset(&wsess->w_dns_cache[i].addr, 0, sizeof(wsess->w_dns_cache[i].addr)); } wsess->w_dns_cache_count = (ns < 8) ? ns : 8; } #endif /* HAVE_CARES */ return wsess; } whois_session_params * w_reinit(whois_session_params * wsess) { /* int e; */ wsess->w_noisy = 0; /* Don't show debug msgs by default */ memset(&(wsess->pw_serv), 0, sizeof(wsess->pw_serv)); wsess->consolidated_asn[0] = wsess->consolidated_asp[0] = wsess->consolidated_route[0] = wsess->consolidated_orgname[0] = wsess->consolidated_netname[0] = '?'; wsess->consolidated_asn[1] = wsess->consolidated_asp[1] = wsess->consolidated_route[1] = wsess->consolidated_orgname[1] = wsess->consolidated_netname[1] = 0; memset(&wsess->tbuf, 0, sizeof(wsess->tbuf)); wsess->logprintfCookie = 0; return wsess; } __inline__ void w_close(whois_session_params * wsess) { free(wsess); } static char * w_ask(whois_session_params *wsess, const char *serv, const char *q, const char *port) { int s; struct sockaddr_in sin4; struct addrinfo ai_hints, *ai_res; char *br; int q_s, br_s, cur, n, myport; char buf[128], *sendbuf; #ifdef USE_WHOIS_TIMEOUT #if defined(WIN32) || defined(_WIN32) int whreadtimeout = 20000; #else struct timeval whreadtimeout; whreadtimeout.tv_sec = 20; whreadtimeout.tv_usec = 0; #endif #endif if (!serv || !q) return NULL; memset(&sin4, 0, sizeof(sin4)); sin4.sin_family = AF_INET; #ifdef HAVE_CARES { /* Resolve this server on first use (no-op if already cached). */ w_prefetch(wsess, serv); /* Check DNS cache to avoid a blocking getaddrinfo() call; * falls through to getaddrinfo() for unknown/custom servers. */ int _i, _found = 0; for (_i = 0; wsess && _i < wsess->w_dns_cache_count; _i++) { if (wsess->w_dns_cache[_i].host && strcmp(wsess->w_dns_cache[_i].host, serv) == 0 && wsess->w_dns_cache[_i].resolved && wsess->w_dns_cache[_i].addr.s_addr != 0) { memcpy(&sin4.sin_addr, &wsess->w_dns_cache[_i].addr, sizeof(sin4.sin_addr)); _found = 1; break; } } if (!_found) { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (w_getaddrinfo_timed(serv, &ai_hints, &ai_res) != 0) return NULL; memcpy(&sin4.sin_addr, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(sin4.sin_addr)); freeaddrinfo(ai_res); } } #else memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (w_getaddrinfo_timed(serv, &ai_hints, &ai_res) != 0) return NULL; memcpy(&sin4.sin_addr, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(sin4.sin_addr)); freeaddrinfo(ai_res); #endif /* HAVE_CARES */ if (port) { if (!(myport = (int)strtol(port, (char **)NULL, 10))) return NULL; sin4.sin_port = htons(myport); } else { sin4.sin_port = htons(PORT_WHOIS); } if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) return NULL; #ifdef USE_WHOIS_TIMEOUT setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &whreadtimeout, sizeof(whreadtimeout)); #endif if (connect(s, (const struct sockaddr *)(const void *)&sin4, sizeof (sin4)) < 0) return NULL; br_s = 512; if (!(br = (char *)malloc(br_s))) return NULL; q_s = strlen(q); sendbuf = (char *)malloc(q_s+2); if(q[q_s-1]=='\r' || q[q_s-1]=='\n') { strncpy(sendbuf, q, q_s+1); } else { strncpy(sendbuf, q, q_s+1); sendbuf[q_s]='\n'; q_s++; sendbuf[q_s]=0; } if (write(s, sendbuf, q_s) != q_s) /* || write(s, "\r\n", 2) != 2)*/ return NULL; cur = 0; while ((n = read(s, buf, sizeof(buf))) > 0) { if ((cur + n) >= br_s) { br_s = br_s * 2; if (!(br = realloc(br, br_s))) return NULL; } strncpy((char *)&br[cur], buf, n); cur += n; } br[cur] = 0; close(s); return br; } int w_lookup_all_pwhois(whois_session_params * wsess, char *addr) { token_t *ls; char *serv, *reply; const char *format; int i; if (!addr) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; reply = w_ask(wsess, serv, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",serv); else fprintf(stderr,"No reply from %s.\n",serv); } return -1; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value = NULL; if ((value = match_prefix("origin-as:", ls[i].ptr))) { if (strncmp(wsess->consolidated_asn,"?",1) == 0) strncpy(wsess->consolidated_asn,value,255); } else if ((value = match_prefix("prefix:", ls[i].ptr))) { if (strncmp(wsess->consolidated_route,"?",1) == 0) strncpy(wsess->consolidated_route,value,255); } else if ((value = match_prefix("as-path:", ls[i].ptr))) { if (strncmp(wsess->consolidated_asp,"?",1) == 0) strncpy(wsess->consolidated_asp,value,255); } else if ((value = match_prefix("org-name:", ls[i].ptr))) { if (strncmp(wsess->consolidated_orgname,"?",1) == 0) strncpy(wsess->consolidated_orgname,value,255); } else if ((value = match_prefix("net-name:", ls[i].ptr))) { if (strncmp(wsess->consolidated_netname,"?",1) == 0) strncpy(wsess->consolidated_netname,value,255); } else if ((value = match_prefix("cache-date:", ls[i].ptr))) { if ((wsess->tval = atol(value)) != 0) { format = "%d-%b-%y %H:%M:%S %Z"; (void)strftime(wsess->tbuf, sizeof(wsess->tbuf), format, localtime(&wsess->tval)); } } if(value) free(value); } free(ls); free(reply); return 0; } int w_lookup_all_riswhois(whois_session_params * wsess, char *addr) { token_t *ls=NULL; char *serv=NULL, *reply=NULL; const char *risopts = "-1 -M "; /* 1 object/prefix, Most specific */ char *risquery = malloc((strlen(risopts)* sizeof(char)) + (strlen(addr) * sizeof(char)) + 1); unsigned int i; if (!addr) return -1; /* prepare the text-string-based query */ risquery[0]=0; strcat(risquery,risopts); strcat(risquery,addr); reply = w_ask(wsess, ripe_ris_server, risquery, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",serv); else fprintf(stderr,"No reply from %s.\n",serv); } return -1; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value = NULL; if ((value = match_prefix("origin:", ls[i].ptr))) { if (strncmp(wsess->consolidated_asn,"?",1) == 0) strncpy(wsess->consolidated_asn,value,255); } else if ((value = match_prefix("route:", ls[i].ptr))) { if (strncmp(wsess->consolidated_route,"?",1) == 0) strncpy(wsess->consolidated_route,value,255); } else if ((value = match_prefix("descr:", ls[i].ptr))) { if (strncmp(wsess->consolidated_orgname,"?",1) == 0) strncpy(wsess->consolidated_orgname,value,255); if (strncmp(wsess->consolidated_netname,"?",1) == 0) strncpy(wsess->consolidated_netname,value,255); } if(value) free(value); } free(ls); free(reply); free(risquery); return 0; } int w_lookup_as_pwhois(whois_session_params * wsess, char *addr) { token_t *ls; char *reply = NULL, *value = NULL; unsigned int i; int ans = 0; if (!addr) return 0; reply = w_ask(wsess, pwhois_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",pwhois_server); else fprintf(stderr,"No reply from %s.\n",pwhois_server); } return 0; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { if ((value = match_prefix("origin-as:", ls[i].ptr))) break; } free(ls); free(reply); if (!value) return 0; rm_spaces(value); for (i = 0; i < strlen(value); i++) { if (!isdigit(value[i])) { free(value); return 0; } } ans = strtol(value, (char **)NULL, 10); free(value); return ans; } int w_lookup_as_riswhois(whois_session_params * wsess, char *addr) { token_t *ls; char *reply = NULL, *value = NULL; const char *risopts = "-1 -M "; /* 1 object/prefix, Most specific */ char *risquery = malloc((strlen(risopts) * sizeof(char)) + (strlen(addr) * sizeof(char)) + 1); unsigned int i; int ans = 0; if (!addr) return 0; /* prepare the text-string-based query */ risquery[0]=0; strcat(risquery,risopts); strcat(risquery,addr); reply = w_ask(wsess, ripe_ris_server, risquery, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",ripe_ris_server); else fprintf(stderr,"No reply from %s.\n",ripe_ris_server); } return 0; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { if ((value = match_prefix("origin:", ls[i].ptr))) break; } free(ls); free(reply); free(risquery); if (!value) return 0; rm_spaces(value); for (i = 0; i < strlen(value); i++) { if (!isdigit(value[i])) { free(value); return 0; } } ans = atoi(value); free(value); return ans; } int w_lookup_all_riswhois_bulk(whois_session_params * wsess, struct ip_list_array *iplist) { token_t *responses=0; char *reply=0; const char *bulk_begin = "-k -1 -M\n"; /* Keepalive, 1 object/prefix, Most specific */ const char *bulk_end = "-k"; char *bulk_ip_query = malloc((strlen(bulk_begin) * sizeof(char)) + ((strlen(bulk_end)+1) * sizeof(char)) + (16 * (*iplist).numItems)); int i = 0; unsigned int j = 0; int k = 0; int entity_id = 0; unsigned int until = 0; char *value = NULL; bulk_ip_query[0]=0; if (!iplist) return -1; /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) { (*iplist).asn[(i)] = 0; memset((*iplist).netName[i],0,sizeof((*iplist).netName[i])); memset((*iplist).orgName[i],0,sizeof((*iplist).orgName[i])); } /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query,inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query,"\n"); } strcat(bulk_ip_query,bulk_end); reply = w_ask(wsess, ripe_ris_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",ripe_ris_server); else fprintf(stderr,"No reply from %s.\n",ripe_ris_server); } /* clean up the response data set in case the caller doesn't */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; return -1; } responses = tokens(reply, "\n"); for (i = 0; responses[i].ptr; i++) { value = NULL; if ((value = match_prefix("origin:", responses[i].ptr)) != NULL) { if (k > 0) { entity_id++; k = 0; } rm_spaces(value); /* strip out any spaces from the ASN */ for (j = 0; j < strlen(value); j++) { if (!isdigit(value[j])) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); else printf("Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); } break; } } if(strtol(value, (char **) NULL, 10)) { (*iplist).asn[(entity_id)] = strtol(value, (char **)NULL, 10); k++; } else if (wsess->w_noisy > 2) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Skipping additional object for same query.\n"); else fprintf(stderr,"Skipping additional object for same query.\n"); } } else if ((value = match_prefix("descr:", responses[i].ptr))) { strncpy((*iplist).orgName[entity_id],value,100); /* yes, this is duplicated. riswhois adds a netname attribute here, so we reuse 'descr' */ for (until = 0; until < strlen(value); until++) { if (isspace(value[until])) break; } strncpy((*iplist).netName[entity_id],value,(until)); k++; } else if ((value = match_prefix("% ", responses[i].ptr)) != NULL) { if (i > 5 && k < 1) { /* Weed out up to 5 leading lines from RIPE NCC RIS */ if (wsess->w_noisy > 2) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"%% MATCHED on '%s'\n",responses[i].ptr); else printf("%% MATCHED on '%s'\n",responses[i].ptr); } /* (*iplist).asn[(entity_id)] = 0; */ k++; } /* else printf("'%s'\n",responses[i].ptr); */ } /* else printf("'%s'\n",responses[i].ptr); */ if(value) free(value); if ((entity_id) >= (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0; } int w_lookup_as(whois_session_params * wsess, char *addr) { token_t *ls; ip_blk_t *a = NULL, *b = NULL; /* char *sa, *sb; */ char *reply, *value = NULL; unsigned int i; int ans = 0; int use_this = 1; if (!addr) return 0; reply = w_ask(wsess, radb_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",radb_server); else fprintf(stderr,"No reply from %s.\n",radb_server); } return 0; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { value = NULL; if ((value = match_prefix("local-as:", ls[i].ptr)) != NULL) { break; } else if ((value = match_prefix("route:", ls[i].ptr)) != NULL) { a = match_ipprefix(value); if (b) { if (((b->end - b->start) > (a->end - a->start))) { use_this = 1; free(b); b = a; a = NULL; } else { use_this = 0; free(a); a = NULL; } } else { use_this = 1; b = a; a = NULL; } } else if (use_this && (value = match_prefix("origin:", ls[i].ptr))) { break; } if(value != NULL) free(value); } free(ls); free(reply); if(b != NULL) free(b); if (!value) return 0; rm_spaces(value); for (i = 0; i < strlen(value); i++) { if (!isdigit(value[i])) { return 0; } } ans = strtol(value, (char **)NULL, 10); free(value); return ans; } int w_lookup_as_cymru(whois_session_params * wsess, char *addr) { /* * Look up the ASN at the prefix-based Cymru whois server */ token_t *ls; char *reply; unsigned int i; char value[6]; memset(&value, 0, sizeof(value)); if (!addr) return 0; reply = w_ask(wsess, cymru_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",cymru_server); else fprintf(stderr,"No reply from %s.\n",cymru_server); } return 0; } ls = tokens(reply, "\n"); /* Set i to 1 to skip the first/header line of reply from cymru */ strncpy(value,ls[1].ptr,5); rm_spaces(value); /* strip out any spaces from the ASN */ for (i = 0; i < strlen(value); i++) { if (!isdigit(value[i])) { return 0; } } free(ls); free(reply); return (strtol(value, (char **)NULL, 10)); } int w_lookup_as_cymru_bulk(whois_session_params * wsess, struct ip_list_array *iplist) { token_t *responses; char *reply; const char *bulk_begin = "begin\n"; const char *bulk_end = "end\n"; char *bulk_ip_query = malloc((strlen(bulk_begin) * sizeof(char)) + (strlen(bulk_end)* sizeof(char)) + (16 * (*iplist).numItems)); int i; unsigned int j; char value[6]; memset(&value, 0, sizeof(value)); bulk_ip_query[0]=0; if (!iplist) return -1; /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query,inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query,"\n"); } strcat(bulk_ip_query,bulk_end); reply = w_ask(wsess, cymru_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",cymru_server); else fprintf(stderr,"No reply from %s.\n",cymru_server); } return -1; } responses = tokens(reply, "\n"); /* Set i to 1 to skip the first/header line of reply from cymru */ for (i = 1; responses[i].ptr; i++) { strncpy(value,responses[i].ptr,5); rm_spaces(value); /* strip out any spaces from the ASN */ for (j = 0; j < strlen(value); j++) { if (!isdigit(value[j])) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); else fprintf(stderr,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); } break; } } if(strtol(value, (char **)NULL, 10)) { (*iplist).asn[(i-1)] = strtol(value, (char **)NULL, 10); } else { (*iplist).asn[(i-1)] = 0; } if ((i+1) > (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0; } #ifdef STANDALONE static int w_display_rvbyasn_pwhois(whois_session_params * wsess, char *asn) { char *reply; const char *query_begin = "routeview source-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc( (strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); free(whob_query); return 0; } static int w_display_rvbytransitasn_pwhois(whois_session_params * wsess, char *asn) { char *reply; const char *query_begin = "routeview transit-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc( (strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); free(whob_query); return 0; } static int w_display_contactsbyasn_pwhois(whois_session_params * wsess, char *asn) { char *reply; const char *query_begin = "registry source-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc( ((strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char)) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); free(whob_query); return 0; } static int w_display_networksbyasn_pwhois(whois_session_params * wsess, char *asn) { char *reply; const char *query_begin = "netblock source-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query =(char *)malloc(((strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char)) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); free(whob_query); return 0; } static int w_display_rvbyprefix_pwhois(whois_session_params * wsess, char *prefix) { char *reply; const char *query_begin = "routeview prefix="; char *whob_query = NULL; char *serv; if (!prefix) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc(((strlen(appname) * sizeof(char))+10) + (strlen(version) * sizeof(char)) + (strlen(query_begin) * sizeof(char)) + (strlen(prefix))* sizeof(char)); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,prefix); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); free(whob_query); return 0; } static int w_display_bulkfromfile_pwhois(whois_session_params * wsess, char *filespec) { const char *query_begin = "begin\n"; const char *query_end = "end\n"; const char *appname_extras = "BULK_FILE"; const char *format_instructions = "type=cymru\n"; char *reply; char *serv; FILE *bulkFile; int num_lines = 0, i = 0; char *lines = malloc(line_size * max_lines); char *this_line = malloc(line_size); size_t whob_query_len = ((strlen(appname) * sizeof(char) +10) + (strlen(appname_extras) * sizeof(char)) + (strlen(version) * sizeof(char))) + (strlen(query_begin) * sizeof(char)) + (strlen(format_instructions) * sizeof(char)) + (strlen(query_end) * sizeof(char)) + (line_size * max_lines); char *whob_query = (char *)malloc(whob_query_len); reply = NULL; *whob_query = '\0'; if (!filespec) { fprintf(stderr,"You must specify a file to use (or '-' for stdin) for bulk query input.\n"); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (!strncmp(filespec,"-",1) || use_stdin == 1) { bulkFile = stdin; } else bulkFile = fopen(filespec, "r"); if (!bulkFile) { fprintf(stderr,"%s: Unable to open \'%s\' for reading.\n",appname,filespec); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; while (!feof(bulkFile)) { if (num_lines >= max_lines && wsess->w_noisy >= 2) fprintf(stderr,"Processing next batch of %d beginning at line %d.\n",max_lines,(num_lines+1)); memset(lines, 0, line_size * max_lines); memset(whob_query, 0, whob_query_len); for (i = 0; i < max_lines; i++) { if (fgets(this_line,line_size - 1,bulkFile)) { if (strncmp(this_line, "#", 1) && strncmp(this_line, ";", 1)) { strcat(lines,this_line); num_lines++; if (wsess->w_noisy >= 4) fprintf(stderr,"Line %d: %s",num_lines,this_line); } } else if (feof(bulkFile)) { if (wsess->w_noisy >= 2) fprintf(stderr,"End of file reached after reading %d lines.\n",num_lines); break; } else if (ferror(bulkFile)) { if (wsess->w_noisy >= 1) fprintf(stderr,"Error in stream on line %d.\n",num_lines+1); break; } } /* prepare the text-string-based query */ strcat(whob_query, query_begin); strcat(whob_query, "app=\""); strcat(whob_query, appname); strcat(whob_query, " "); strcat(whob_query, version); strcat(whob_query, " "); strcat(whob_query, appname_extras); strcat(whob_query, "\"\n"); if (use_cymru) strcat(whob_query, format_instructions); strcat(whob_query, lines); strcat(whob_query, query_end); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); free(this_line); free(lines); free(whob_query); free(reply); return -1; } printf("%s",reply); } free(this_line); free(lines); free(whob_query); free(reply); fclose(bulkFile); return 0; } static int w_display_bulkfromfile_riswhois(whois_session_params * wsess, char *filespec) { const char *query_begin = "-k -1 -M\n"; /* Keepalive, 1 object/prefix, Most specific */ const char *query_end = "-k"; char *reply; char *serv; FILE *bulkFile; int num_lines = 0, i = 0; char *lines = (char *)malloc(line_size * max_lines); char *this_line = (char *)malloc(line_size); size_t whob_query_len = 10 + (strlen(query_begin) * sizeof(char)) + (strlen(query_end) * sizeof(char)) + (line_size * max_lines); char *whob_query = (char *)malloc(whob_query_len); reply = NULL; *whob_query = '\0'; if (!filespec) { fprintf(stderr,"You must specify a file to use (or '-' for stdin) for bulk query input.\n"); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (!strncmp(filespec,"-",1) || use_stdin == 1) { bulkFile = stdin; } else bulkFile = fopen(filespec, "r"); if (!bulkFile) { fprintf(stderr,"%s: Unable to open \'%s\' for reading.\n",appname,filespec); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = ripe_ris_server; while (!feof(bulkFile)) { if (num_lines >= max_lines && wsess->w_noisy >= 2) fprintf(stderr,"Processing next batch of %d beginning at line %d.\n",max_lines,(num_lines+1)); memset(lines, 0, line_size * max_lines); memset(whob_query, 0, whob_query_len); for (i = 0; i < max_lines; i++) { if (fgets(this_line,line_size - 1,bulkFile)) { if (strncmp(this_line, "#", 1) && strncmp(this_line, ";", 1)) { strcat(lines,this_line); num_lines++; if (wsess->w_noisy >= 4) fprintf(stderr,"Line %d: %s",num_lines,this_line); } } else if(feof(bulkFile)) { if (wsess->w_noisy >= 2) fprintf(stderr,"End of file reached after reading %d lines.\n",num_lines); break; } else if (ferror(bulkFile)) { if (wsess->w_noisy >= 1) fprintf(stderr,"Error in stream on line %d.\n",num_lines+1); break; } } /* prepare the text-string-based query */ strcat(whob_query, query_begin); strcat(whob_query, lines); strcat(whob_query, query_end); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); free(this_line); free(lines); free(whob_query); free(reply); return -1; } printf("%s",reply); } free(this_line); free(lines); free(whob_query); free(reply); fclose(bulkFile); return 0; } static int w_display_bulkfromfile_cymru(whois_session_params * wsess, char *filespec) { const char *query_begin = "begin\n"; const char *query_end = "end"; char *reply; char *serv; FILE *bulkFile; int num_lines = 0, i = 0; char *lines = (char *)malloc(line_size * max_lines); char *this_line = (char *)malloc(line_size); size_t whob_query_len = 10 + (strlen(query_begin) * sizeof(char)) + (strlen(query_end) * sizeof(char)) + (line_size * max_lines); char *whob_query = (char *)malloc(whob_query_len); reply = NULL; *whob_query = '\0'; if (!filespec) { fprintf(stderr,"You must specify a file to use (or '-' for stdin) for bulk query input.\n"); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (!strncmp(filespec,"-",1) || use_stdin == 1) { bulkFile = stdin; } else bulkFile = fopen(filespec, "r"); if (!bulkFile) { fprintf(stderr,"%s: Unable to open \'%s\' for reading.\n",appname,filespec); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = cymru_server; while (!feof(bulkFile)) { if (num_lines >= max_lines && wsess->w_noisy >= 2) fprintf(stderr,"Processing next batch of %d beginning at line %d.\n",max_lines,(num_lines+1)); memset(lines, 0, line_size * max_lines); memset(whob_query, 0, whob_query_len); for (i = 0; i < max_lines; i++) { if (fgets(this_line, line_size - 1, bulkFile)) { if (strncmp(this_line, "#", 1) && strncmp(this_line, ";", 1)) { strcat(lines, this_line); num_lines++; if (wsess->w_noisy >= 4) fprintf(stderr,"Line %d: %s",num_lines,this_line); } } else if(feof(bulkFile)) { if (wsess->w_noisy >= 2) fprintf(stderr,"End of file reached after reading %d lines.\n",num_lines); break; } else if(ferror(bulkFile)) { if (wsess->w_noisy >= 1) fprintf(stderr,"Error in stream on line %d.\n",num_lines+1); break; } } /* prepare the text-string-based query */ strcat(whob_query, query_begin); strcat(whob_query, lines); strcat(whob_query, query_end); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); free(this_line); free(lines); free(whob_query); free(reply); return -1; } printf("%s",reply); } free(this_line); free(lines); free(whob_query); free(reply); fclose(bulkFile); return 0; } static int w_display_pwhois_version(whois_session_params * wsess) { char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; if (wsess->w_noisy) fprintf(stderr,"Querying '%s' for version/status.\n",serv); reply = w_ask(wsess, serv, "version", NULL); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); return 0; } static int w_display_myaddress_http(whois_session_params * wsess) { char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = myaddress_server; if (wsess->w_noisy) fprintf(stderr,"Querying '%s' for my public address.\n",serv); reply = w_ask(wsess, serv, "Accept:*/*\nHost: myaddress.today\nUser-Agent: WhoB\nGET /\n\n", "80"); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); return 0; } static int w_display_myaddress(whois_session_params * wsess) { char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; if (wsess->w_noisy) fprintf(stderr,"Querying '%s' for my public address.\n",serv); reply = w_ask(wsess, serv, "whoami", NULL); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); return 0; } static int w_display_pwhois_gigo(whois_session_params * wsess, char *user_query) { char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; if (wsess->w_noisy) fprintf(stderr,"Querying '%s' for: '%s'\n",serv,user_query); reply = w_ask(wsess, serv, user_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); return 0; } #endif int w_lookup_all_pwhois_bulk(whois_session_params * wsess, struct ip_list_array *iplist) { token_t *responses; char *reply; const char *bulk_begin = "begin\n"; const char *bulk_end = "end\n"; char *bulk_ip_query = NULL; int i = 0, k = 0, entity_id = 0; unsigned int j = 0; char *value = NULL; if (!iplist) return -1; if ((uintptr_t)(*iplist).application) { bulk_ip_query = (char *)malloc(((strlen((*iplist).application) * sizeof(char)) +10) + ((strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); } else bulk_ip_query = (char *)malloc(((strlen(appname) * sizeof(char)) +10) + ((strlen(version) + strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); *bulk_ip_query = '\0'; /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) { (*iplist).asn[(i)] = 0; memset((*iplist).netName[i],0,sizeof((*iplist).netName[i])); memset((*iplist).orgName[i],0,sizeof((*iplist).orgName[i])); } /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); if ((uintptr_t)(*iplist).application) { strcat(bulk_ip_query, "app=\""); strcat(bulk_ip_query, (*iplist).application); strcat(bulk_ip_query, "\"\n"); } else { strcat(bulk_ip_query, "app=\""); strcat(bulk_ip_query, appname); strcat(bulk_ip_query, " "); strcat(bulk_ip_query, version); strcat(bulk_ip_query, "\"\n"); } for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query, inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query, "\n"); } strcat(bulk_ip_query, bulk_end); reply = w_ask(wsess, pwhois_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",pwhois_server); else fprintf(stderr,"No reply from %s.\n",pwhois_server); } /* clean up the response data set in case the caller doesn't */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; return -1; } responses = tokens(reply, "\n"); for(i = 0; responses[i].ptr; i++){ value = NULL; // printf("LINE %d: '%s'\n",i, responses[i].ptr); if((value = match_prefix("IP:", responses[i].ptr)) != NULL){ /* if any keys matched, increment the id of the array */ if(k > 0){ entity_id++; k = 0; } } else if((value = match_prefix("origin-as:", responses[i].ptr)) != NULL) { rm_spaces(value); /* strip out any spaces from the ASN */ for(j = 0; j < strlen(value); j++) { if(!isdigit(value[j])){ if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); else fprintf(stderr,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); } break; } } if ((int)strtol(value, (char **)NULL, 10)) { (*iplist).asn[(entity_id)] = strtol(value, (char **)NULL, 10); k++; } else { (*iplist).asn[(entity_id)] = 0; k++; } } else if ((value = match_prefix("org-name:", responses[i].ptr))) { strncpy((*iplist).orgName[entity_id],value,100); k++; } else if ((value = match_prefix("net-name:", responses[i].ptr))) { strncpy((*iplist).netName[entity_id],value,32); k++; } if(value) free(value); if ((entity_id+1) > (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0; } int w_lookup_all_pwhois_bulk_ext(whois_session_params * wsess, struct ext_ip_list_array *iplist) { token_t *responses; char *reply; const char *bulk_begin = "begin\n"; const char *bulk_end = "end\n"; char *bulk_ip_query = NULL; int i=0; unsigned int j=0; int k=0; int pntcnt; int entity_id = 0; char *value = NULL; if (!iplist) return -1; iplist->geoavailable=0; if ((uintptr_t)(*iplist).application) { bulk_ip_query = malloc(((strlen((*iplist).application) * sizeof(char)) +10) + ((strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); } else bulk_ip_query = malloc(((strlen(appname) * sizeof(char)) +10) + ((strlen(version) + strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); bulk_ip_query[0]=0; /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) { (*iplist).asn[(i)] = 0; memset((*iplist).netName[i],0,sizeof((*iplist).netName[i])); memset((*iplist).orgName[i],0,sizeof((*iplist).orgName[i])); } /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); if ((uintptr_t)(*iplist).application) { strcat(bulk_ip_query,"app=\""); strcat(bulk_ip_query,(*iplist).application); strcat(bulk_ip_query,"\"\n"); } else { strcat(bulk_ip_query,"app=\""); strcat(bulk_ip_query,appname); strcat(bulk_ip_query," "); strcat(bulk_ip_query,version); strcat(bulk_ip_query,"\"\n"); } for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query,inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query,"\n"); } strcat(bulk_ip_query,bulk_end); reply = w_ask(wsess, pwhois_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",pwhois_server); else fprintf(stderr,"No reply from %s.\n",pwhois_server); } /* clean up the response data set in case the caller doesn't */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; return -1; } responses = tokens(reply, "\n"); for (i = 0; responses[i].ptr; i++) { value = NULL; // printf("LINE %d: '%s'\n",i, responses[i].ptr); if ((value = match_prefix("IP:", responses[i].ptr)) != NULL) { /* if any keys matched, increment the id of the array */ if (k > 0) { entity_id++; k = 0; } } else if ((value = match_prefix("origin-as:", responses[i].ptr)) != NULL) { rm_spaces(value); /* strip out any spaces from the ASN */ for (j = 0; j < strlen(value); j++) { if (!isdigit(value[j])) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); else fprintf(stderr,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[i],i,value); } break; } } if(atoi(value)) { (*iplist).asn[(entity_id)] = atoi(value); k++; } else { (*iplist).asn[(entity_id)] = 0; k++; } } else if ((value = match_prefix("as-org-name-source:", responses[i].ptr))) { strncpy((*iplist).asOrgNameSource[entity_id],value,20); k++; } else if ((value = match_prefix("org-name-source:", responses[i].ptr))) { strncpy((*iplist).orgNameSource[entity_id],value,20); k++; } else if ((value = match_prefix("net-name-source:", responses[i].ptr))) { strncpy((*iplist).netNameSource[entity_id],value,20); k++; } else if ((value = match_prefix("prefix:", responses[i].ptr))) { strncpy((*iplist).prefix[entity_id],value,20); k++; } else if ((value = match_prefix("org-name:", responses[i].ptr))) { strncpy((*iplist).orgName[entity_id],value,100); k++; } else if ((value = match_prefix("net-name:", responses[i].ptr))) { strncpy((*iplist).netName[entity_id],value,32); k++; } else if ((value = match_prefix("longitude:", responses[i].ptr))) { rm_spaces(value); /* strip out any spaces from the LONGITUDE */ for (j = 0, pntcnt = 0; j < strlen(value); j++) { if(value[j]=='.') pntcnt++; if (!isdigit(value[j]) && (value[j]!='.' || pntcnt>1)) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[i],i,value); else fprintf(stderr,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[i],i,value); } break; } } (*iplist).longitude[(entity_id)]=atof(value); iplist->geoavailable++; k++; } else if ((value = match_prefix("latitude:", responses[i].ptr))) { rm_spaces(value); /* strip out any spaces from the LONGITUDE */ for (j = 0, pntcnt = 0; j < strlen(value); j++) { if(value[j]=='.') pntcnt++; if (!isdigit(value[j]) && (value[j]!='.' || pntcnt>1)) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[i],i,value); else fprintf(stderr,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[i],i,value); } break; } } (*iplist).latitude[(entity_id)]=atof(value); iplist->geoavailable++; k++; } else if ((value = match_prefix("city:", responses[i].ptr))) { strncpy((*iplist).city[entity_id],value,50); iplist->geoavailable++; k++; } else if ((value = match_prefix("country:", responses[i].ptr))) { strncpy((*iplist).country[entity_id],value,50); iplist->geoavailable++; k++; } else if ((value = match_prefix("region:", responses[i].ptr))) { strncpy((*iplist).state[entity_id],value,50); iplist->geoavailable++; k++; } if(value) free(value); if ((entity_id+1) > (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0; } static char * w_lookup_netname_other(whois_session_params * wsess, char *addr, char *serv) { token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *reply, *ans = NULL; int i; int use_this = 1; if (!addr || !serv) return NULL; reply = w_ask(wsess, serv, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",serv); else fprintf(stderr,"No reply from %s.\n",serv); } return NULL; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value=NULL; if ((value = match_prefix("inetnum:", ls[i].ptr))) { a = match_ipprefix(value); if (b) { if (((b->end - b->start) > (a->end - a->start))) { use_this = 1; free(b); b = a; a = NULL; } else { use_this = 0; free(a); a = NULL; } } else { use_this = 1; b = a; a = NULL; } free(value); } else if (use_this && (value = match_prefix("netname:", ls[i].ptr))) { if (ans) free(ans); ans = value; } } free(ls); free(reply); if (b) free(b); return ans; } char * w_lookup_netname(whois_session_params * wsess, char *addr) { token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *na = NULL, *nb = NULL; char *reply, *ans = NULL; int i; int have_new, have_old; if (!addr) return NULL; reply = w_ask(wsess, arin_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); ans = NULL; for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("netname:", ls[i].ptr))) { ans = value; break; } } if (!ans) { for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_inparens(ls[i].ptr))) { char *after = match_afterparens(ls[i].ptr); if (after) { na = value; a = match_iprange(after); } else { na = value; if (ls[i+1].ptr && (a = match_iprange(ls[i+1].ptr))) { /* successful match */ } else { /* Bad format */ free(na); na = NULL; continue; } } } have_new = (na && a); have_old = (nb && b); if (have_new) { if (have_old) { if (((b->end - b->start) > (a->end - a->start))) { /* keep new, discard old */ free(nb); free(b); nb = na; na = NULL; b = a; a = NULL; } else { /* keep old, discard new */ free(na); free(a); na = NULL; a = NULL; } } else { /* nothing old, save new */ nb = na; na = NULL; b = a; a = NULL; } } } /* loop */ if (na) free(na); if (a) free(a); if (b) free(b); free(ls); free(reply); if (!nb) return NULL; /* Add "!" to the beginning of the question */ na = malloc(strlen(nb) + 2); strcpy(&na[1], nb); na[0] = '!'; free(nb); reply = w_ask(wsess, arin_server, na, NULL); free(na); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); } for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("netname:", ls[i].ptr))) { ans = value; break; } } free(ls); free(reply); { char *other = NULL; if (ans && strstr(ans, "RIPE")) { other = w_lookup_netname_other(wsess, addr, ripe_server); } if (ans && !strncmp(ans, "APNIC", 5)) { other = w_lookup_netname_other(wsess, addr, apnic_server); } if (other) { char *together = malloc(strlen(ans) + strlen(other) + 2); together[0]=0; strcpy(together, ans); strcat(together, "/"); strcat(together, other); free(ans); ans = together; } } return ans; } static char * w_lookup_orgname_other(whois_session_params * wsess, char *addr, char *serv) { token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *reply, *ans = NULL; int i; int use_this = 1; if (!addr || !serv) return NULL; reply = w_ask(wsess, serv, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",serv); else fprintf(stderr,"No reply from %s.\n",serv); } return NULL; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value=NULL; if ((value = match_prefix("inetnum:", ls[i].ptr))) { a = match_ipprefix(value); if (b) { if (((b->end - b->start) > (a->end - a->start))) { use_this = 1; free(b); b = a; a = NULL; } else { use_this = 0; free(a); a = NULL; } } else { use_this = 1; b = a; a = NULL; } free(value); }else if (use_this && (value = match_prefix("orgname:", ls[i].ptr))) { if (ans) free(ans); ans = value; } } if (!ans) { for (i = 0; ls[i].ptr; i++) { char *value; if (use_this && (value = match_prefix("descr:", ls[i].ptr))) { if (ans) free(ans); ans = value; break; } } } free(ls); free(reply); if (b) free(b); return ans; } char * w_lookup_orgname(whois_session_params * wsess, char *addr) { token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *na = NULL, *nb = NULL; char *reply, *ans = NULL; int i; int have_new, have_old; if (!addr) return NULL; reply = w_ask(wsess, arin_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("netname:", ls[i].ptr))) { ans = value; break; } } if (!ans) { for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_inparens(ls[i].ptr))) { char *after = match_afterparens(ls[i].ptr); if (after) { na = value; a = match_iprange(after); } else { na = value; if (ls[i+1].ptr && (a = match_iprange(ls[i+1].ptr))) { /* successful match */ } else { /* Bad format */ free(na); na = NULL; continue; } } } have_new = (na && a); have_old = (nb && b); if (have_new) { if (have_old) { if (((b->end - b->start) > (a->end - a->start))) { /* keep new, discard old */ free(nb); free(b); nb = na; na = NULL; b = a; a = NULL; } else { /* keep old, discard new */ free(na); free(a); na = NULL; a = NULL; } } else { /* nothing old, save new */ nb = na; na = NULL; b = a; a = NULL; } } } /* loop */ if (na) free(na); if (a) free(a); if (b) free(b); free(ls); free(reply); if (!nb) return NULL; /* Add "!" to the beginning of the question */ na = malloc(strlen(nb) + 2); strcpy(&na[1], nb); na[0] = '!'; free(nb); reply = w_ask(wsess, arin_server, na, NULL); free(na); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); } for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("orgname:", ls[i].ptr))) { if(ans) free(ans); ans = value; break; } } free(ls); free(reply); { char *other = NULL; if (ans && strstr(ans, "RIPE")) { other = w_lookup_orgname_other(wsess, addr, ripe_server); } if (ans && (!strncmp(ans, "APNIC", 5) || strstr(ans, "Asia Pacific Net") )) { other = w_lookup_orgname_other(wsess, addr, apnic_server); } if (other) { /* char *together = malloc(strlen(ans) + strlen(other) + 4); strcpy(together, other); strcat(together, " ("); strcat(together, ans); strcat(together, ")"); */ free(ans); ans = other; } } return ans; } #ifdef STANDALONE /*---------------------------------------------------------------------------*/ void lft_printf(lft_session_params * sess, const char *templ, ...) { va_list ap; char buf[1024]; (void)sess; va_start (ap, templ); vsprintf(buf, templ, ap); va_end (ap); printf("%s",buf); } /*---------------------------------------------------------------------------*/ static void usage (char *prog) { fprintf (stderr, "\nWhoB - version %s\n\n" " - a likable whois client from the Prefix WhoIs project\n" " visit http://www.pwhois.org\n" "\nUsage: %s [-g] [] \n" "\nMainstream Options:\n" " -g Disable GIGO mode; enable other options then submit query\n" " -R Display the Origin-AS on file at the RADB/IRR also\n" " -p Display the AS-Path learned by the pWhoIs server (pWhoIs-only)\n" " -n Display the network name on file at the registrar\n" " -t Display the date the route was last updated (pWhoIs-only)\n" " -u Display the date the route was last updated in GMT (pWhoIs-only)\n" " -o Disable display of the organization\'s name on file at the registrar\n" "\nAdvanced Options:\n" " -h host Specify your own pWhoIs-compatible server to query\n" " -f file Read from 'file' (or '-' as stdin) as bulk input to pWhoIs\n" " -c Use Cymru\'s whois server instead of pWhoIs\n" " -r Use RIPE NCC\'s RIS whois server instead of pWhoIs\n" "\nPrefix WhoIs Advanced Options:\n" " -A Display all routes transiting the target ASN (pWhoIs-only)\n" " -a Display all routes announced by the target ASN (pWhoIs-only)\n" " -P Display all routes respective to a target prefix (pWhoIs-only)\n" " -N Display all networks registered to the target ASN (pWhoIs-only)\n" " -O Display organizational contact info for the target ASN (pWhoIs-only)\n" "\nVerbosity Options and Status:\n" " -s Display the version and status of the pWhoIs server (pWhoIs-only)\n" " -V Display verbose/debug output. Use more \'V\'s for additional detail\n" " -v Display WhoB\'s version information and exit\n" " me|whoami Display the public IP address of this host\n" "\n" ,version,prog); fprintf(stderr,"Example: %s -gnp 1.2.3.4\n",prog); fprintf(stderr,"Returns: IP Address | ASN-by-prefix (prefix) | AS-Path | NetName | OrgName\n\n"); exit(EXIT_FAILURE); } static void show_startup_msgs (whois_session_params * wsess) { if (wsess->w_noisy) { fprintf(stderr,"WhoB version %s firing up...",version); if (wsess->w_noisy > 1) fprintf(stderr," (verbosity level %d)\n",wsess->w_noisy); else printf ("\n"); if (wsess->w_noisy > 1) { fprintf(stderr,"Data sources:"); if ((strlen(wsess->pw_serv) > 0) && (!use_cymru || read_from_file)) fprintf(stderr," %s (pWhoIs)",wsess->pw_serv); else if ((!use_cymru || read_from_file) && !use_riswhois) fprintf(stderr," %s (pWhoIs)", pwhois_server); else if (!use_cymru) fprintf(stderr," %s (RIPE NCC)", ripe_ris_server); if (use_cymru && !read_from_file) fprintf(stderr," %s (Cymru)",cymru_server); if (display_radb_as) fprintf(stderr,", %s (RADB)",radb_server); fprintf(stderr,".\n"); if (read_from_file) { if (cymrufromfile) fprintf(stderr,"Using Cymru for bulk file resolution.\n"); else if (riswhoisfromfile) fprintf(stderr,"Using RIPE NCC for bulk file resolution.\n"); else fprintf(stderr,"Using Prefix WhoIs for bulk file resolution.\n"); } } if (show_routes_byprefix == 1 && (show_routes_byasn == 1 || show_routes_bytransitasn == 1)) { fprintf(stderr,"You may only perform routeviews one at a time. Using by-ASN.\n"); show_routes_byprefix = 0; } } } int main(int ac, char **av) { struct addrinfo ai_hints, *ai_res; char *addr = NULL; struct in_addr in, pws; int ch; int user_asn = 0; char user_asn_buf[10]; whois_session_params * wsess; memset(&hostname, 0, sizeof(hostname)); wsess = w_init(); setbuf(stdout, NULL); while ((ch = getopt (ac, av, "AaCcfgNnOopPRrstuVvh:w:")) != EOF) switch (ch) { case 'a': use_gigo = 0; show_routes_byasn = 1; go_interactive = 1; break; case 'A': use_gigo = 0; show_routes_bytransitasn = 1; go_interactive = 1; break; case 'N': use_gigo = 0; show_networks_byasn = 1; go_interactive = 1; break; case 'O': use_gigo = 0; show_contacts_byasn = 1; go_interactive = 1; break; case 's': use_gigo = 0; show_server_status = 1; go_interactive = 1; break; case 'P': use_gigo = 0; show_routes_byprefix = 1; go_interactive = 1; break; case 'v': usage(av[0]); break; case 'u': /* show all times in UTC */ #if defined(sun) if(putenv("TZ=GMT0") == -1) { fprintf(stderr, "%s: Unable to set TZ to UTC.",appname); } #else #if !defined(WIN32) && !defined(_WIN32) if (setenv("TZ", "GMT0", 1) == -1) { fprintf(stderr, "%s: Unable to set TZ to UTC.",appname); } #endif #endif show_cache_date = 1; break; case 't': show_cache_date = 1; break; case 'w': if (strlen(optarg) > max_hostname_input) { fprintf(stderr,"Sorry, the server name you supplied was unreasonably long.\n"); exit(EXIT_FAILURE); } if (inet_aton(optarg, &pws)) { strncpy(wsess->pw_serv, optarg, strlen(optarg)); } else { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(optarg, NULL, &ai_hints, &ai_res) != 0) { fprintf(stderr,"Sorry, I cannot resolve \'%s\' to use as your pWhoIs server.\n", optarg); exit(EXIT_FAILURE); } memcpy(&pws, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(pws)); freeaddrinfo(ai_res); strncpy(wsess->pw_serv,inet_ntoa(pws),strlen(inet_ntoa(pws))); } break; case 'h': if (strlen(optarg) > max_hostname_input) { fprintf(stderr,"Sorry, the server name you supplied was unreasonably long.\n"); exit(EXIT_FAILURE); } if (inet_aton(optarg, &pws)) { strncpy(wsess->pw_serv,optarg,strlen(optarg)); } else { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(optarg, NULL, &ai_hints, &ai_res) != 0) { fprintf(stderr,"Sorry, I cannot resolve \'%s\' to use as your pWhoIs server.\n", optarg); exit(EXIT_FAILURE); } memcpy(&pws, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(pws)); freeaddrinfo(ai_res); strncpy(wsess->pw_serv,inet_ntoa(pws),strlen(inet_ntoa(pws))); } break; case 'c': use_cymru = 1; /* cymrufromfile = 1; */ /* Use pwhois Cymru compatibility mode by default */ break; case 'C': use_cymru = 1; cymrufromfile = 1; break; case 'n': display_netname = 1; go_interactive = 1; break; case 'r': use_riswhois = 1; riswhoisfromfile = 1; break; case 'R': display_radb_as = 1; go_interactive = 1; break; case 'o': display_orgname = 0; go_interactive = 1; break; case 'p': display_aspath = 1; go_interactive = 1; break; case 'f': use_gigo = 0; read_from_file = 1; break; case 'V': wsess->w_noisy++; break; case 'g': use_gigo = 0; go_interactive = 1; break; default: usage (av[0]); } /* Catch hostname input without any arguments */ if ((ac - optind) > 0) go_interactive = 1; if (go_interactive > 0) { /* Quickly check stdin for input to avoid David having to type */ /* '-f -' to pipe into to whob, even if it has args :-) */ fd_set rfds; struct timeval timev; int selretval; /* Watch stdin (fd 0) briefly for input. */ FD_ZERO(&rfds); FD_SET(0, &rfds); timev.tv_sec = 0; timev.tv_usec = 100; selretval = select(1, &rfds, NULL, NULL, &timev); if (selretval) { /* There's input in them there hills, treat it as the input file */ use_gigo = 0; read_from_file = 1; use_stdin = 1; } /* else if (wsess->w_noisy) fprintf(stderr,"No input found on stdin.\n"); */ } else if (read_from_file < 1) { /* No indication we should be interactive based on arguments, so wait for STDIN */ use_gigo = 0; read_from_file = 1; use_stdin = 1; } if (((ac - optind) < 1) && (show_server_status != 1) && (go_interactive > 0)) usage (av[0]); /* Show the verbose startup information if verbosity is enabled/requested */ show_startup_msgs(wsess); if (show_server_status == 1) { w_display_pwhois_version(wsess); exit(EXIT_FAILURE); } else if ((read_from_file > 0 && use_stdin < 1) || go_interactive > 0) { if (strlen(av[optind]) > max_hostname_input) { fprintf(stderr,"Sorry, the subject name you supplied was unreasonably long.\n"); exit(EXIT_FAILURE); } else { strncpy(hostname,av[optind],strlen(av[optind])); optind++; } } if (read_from_file) { if (riswhoisfromfile) w_display_bulkfromfile_riswhois(wsess, hostname); else if (cymrufromfile) w_display_bulkfromfile_cymru(wsess, hostname); else w_display_bulkfromfile_pwhois(wsess, hostname); exit(EXIT_SUCCESS); } if (use_gigo > 0) { if (strncmp(hostname,"me",2) == 0 || strncmp(hostname,"whoami",6) == 0) { w_display_myaddress(wsess); exit(EXIT_SUCCESS); } else { w_display_pwhois_gigo(wsess, hostname); exit(EXIT_SUCCESS); } } if ((show_routes_byasn || show_routes_bytransitasn || show_contacts_byasn || show_networks_byasn) && (strlen(hostname) <= 10) && atoi(hostname) && stricontains(hostname,".") < 1) { user_asn = atoi(hostname); if (wsess->w_noisy > 1) fprintf(stderr,"Using user-supplied ASN %d for lookup.\n",user_asn); } else if (show_routes_byprefix == 1) { printf("Displaying all routes for prefix %s.\n",hostname); } else { if (strncmp(hostname,"me",2) == 0 || strncmp(hostname,"whoami",6) == 0) { w_display_myaddress(wsess); exit(EXIT_SUCCESS); } if (inet_aton(hostname, &in)) { addr = hostname; } else { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(hostname, NULL, &ai_hints, &ai_res) != 0) { fprintf(stderr,"Sorry, I cannot resolve \'%s\'\n", hostname); exit(EXIT_FAILURE); } memcpy(&in, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(in)); freeaddrinfo(ai_res); addr = inet_ntoa(in); } } if (show_routes_byasn) { if (user_asn > 0) { printf("Displaying all routes whose Origin-AS is %d.\n", user_asn); snprintf(user_asn_buf,9,"%d",user_asn); w_display_rvbyasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all routes whose Origin-AS is %s.\n", wsess->consolidated_asn); w_display_rvbyasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Sorry, unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_routes_bytransitasn) { if (user_asn > 0) { printf("Displaying all routes whose Transit-AS is %d.\n", user_asn); snprintf(user_asn_buf,9,"%d",user_asn); w_display_rvbytransitasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all routes whose Transit-AS is %s.\n", wsess->consolidated_asn); w_display_rvbytransitasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Sorry, unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_networks_byasn) { if (user_asn > 0) { printf("Displaying all networks registered to the Origin-AS %d.\n", user_asn); snprintf(user_asn_buf,9,"%d",user_asn); w_display_networksbyasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all networks registered to the Origin-AS %s.\n", wsess->consolidated_asn); w_display_networksbyasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Sorry, unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_contacts_byasn) { if (user_asn > 0) { printf("Displaying all contact info on file for Origin-AS %d.\n", user_asn); snprintf(user_asn_buf,9,"%d",user_asn); w_display_contactsbyasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all contact info on file for Origin-AS %s.\n", wsess->consolidated_asn); w_display_contactsbyasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_routes_byprefix) { w_display_rvbyprefix_pwhois(wsess, hostname); } else { printf("%s | Searching...", addr); if (use_cymru) { printf("\b\b\b\b\b\b\b\b\b\b\b\b"); printf("origin-as %d ", w_lookup_as_cymru(wsess, addr)); } else if (use_riswhois) { w_lookup_all_riswhois(wsess, addr); printf("\b\b\b\b\b\b\b\b\b\b\b\b"); printf("origin-as %s (%s) ", wsess->consolidated_asn, wsess->consolidated_route); } else { w_lookup_all_pwhois(wsess, addr); printf("\b\b\b\b\b\b\b\b\b\b\b\b"); printf("origin-as %s (%s) ", wsess->consolidated_asn, wsess->consolidated_route); if (show_cache_date && (strlen(wsess->tbuf) > 0)) printf("| %s ", wsess->tbuf); } if (display_radb_as) printf("| radb-as %d ", w_lookup_as(wsess, addr)); if ((display_aspath) && (!use_cymru) && (!use_riswhois)) printf("| as-path %s ", wsess->consolidated_asp); if (display_netname) { if (use_cymru) { printf("| %s ", w_lookup_netname(wsess, addr)); } else if (use_riswhois) { if (!display_orgname) printf("| %s ", wsess->consolidated_netname); } else { printf("| %s ", wsess->consolidated_netname); } } if (display_orgname) { if (use_cymru) { printf("| %s ", w_lookup_orgname(wsess, addr)); } else { printf("| %s ", wsess->consolidated_orgname); } } printf("\n"); } w_close(wsess); exit(EXIT_SUCCESS); } #endif lft-3.98/configure000755 000765 000024 00000564640 15171551114 014115 0ustar00vicstaff000000 000000 #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.73 for lft 3.93. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2026 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # contradicts POSIX and common usage. Disable this. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf '%s\n' "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac case $# in # (( 0) exec $CONFIG_SHELL $as_opts "$as_myself" ;; *) exec $CONFIG_SHELL $as_opts "$as_myself" "$@" ;; esac # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. printf '%s\n' "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # contradicts POSIX and common usage. Disable this. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case e in #( e) case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else case e in #( e) exitcode=1; echo positional parameters were not saved. ;; esac fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else case e in #( e) as_have_required=no ;; esac fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi ;; esac fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac case $# in # (( 0) exec $CONFIG_SHELL $as_opts "$as_myself" ;; *) exec $CONFIG_SHELL $as_opts "$as_myself" "$@" ;; esac # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. printf '%s\n' "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf '%s\n' "$0: This script requires a shell more modern than all" printf '%s\n' "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf '%s\n' "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf '%s\n' "$0: be upgraded to zsh 4.3.4 or later." else printf '%s\n' "$0: Please tell bug-autoconf@gnu.org and lft@oppleman.com $0: about your system, including any error possibly output $0: before this message. Then install a modern shell, or $0: manually run the script under such a shell if you do $0: have one." fi exit 1 fi ;; esac fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf '%s\n' "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf '%s\n' X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf '%s\n' "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf '%s\n' X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' t clear :clear s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf '%s\n' "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_CONFIG_STATUS= ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='lft' PACKAGE_TARNAME='lft' PACKAGE_VERSION='3.93' PACKAGE_STRING='lft 3.93' PACKAGE_BUGREPORT='lft@oppleman.com' PACKAGE_URL='' ac_unique_file="lft_ifname.h" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS WATCH_OBJS PKGCONFIG ALLOCA INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM SET_MAKE LN_S OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC host_os host_vendor host_cpu host build_os build_vendor build_cpu build ECHO_T ECHO_N ECHO_C target_alias host_alias build_alias LIBS DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_universal enable_gtod with_pcap enable_async_dns with_cares enable_ncurses with_ncurses ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf '%s\n' "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: '$ac_option' Try '$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: '$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf '%s\n' "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf '%s\n' "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`printf '%s\n' $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf '%s\n' "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: '$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf '%s\n' X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but 'cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF 'configure' configures lft 3.93 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print 'checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for '--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or '..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, 'make install' will install all the files in '$ac_default_prefix/bin', '$ac_default_prefix/lib' etc. You can specify an installation prefix other than '$ac_default_prefix' using '--prefix', for instance '--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/lft] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of lft 3.93:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-universal build a universal binary for arm64 and x86_64 on macOS --enable-gtod use gettimeofday() instead of pcap timestamps for per-packet timing --disable-async-dns disable asynchronous DNS via c-ares (default: auto-detect and use if available) --disable-ncurses disable ncurses interactive mode (default: auto-detect and use if available) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pcap=PATH specify prefix path to the pcap library (e.g. /usr/local) --with-cares=PATH specify prefix path to the c-ares library (e.g. /opt/homebrew) --with-ncurses=PATH specify prefix path to the ncurses library (e.g. /opt/homebrew) Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by 'configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf '%s\n' "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf '%s\n' "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf '%s\n' "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF lft configure 3.93 generated by GNU Autoconf 2.73 Copyright (C) 2026 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else case e in #( e) printf '%s\n' "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else case e in #( e) printf '%s\n' "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf '%s\n' "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) eval "$3=yes" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf '%s\n' "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_try_run LINENO # ---------------------- # Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that # executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else case e in #( e) printf '%s\n' "$as_me: program exited with status $ac_status" >&5 printf '%s\n' "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status ;; esac fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (void); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (void); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf '%s\n' "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf '%s\n' "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf '%s\n' "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by lft $as_me 3.93, which was generated by GNU Autoconf 2.73. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf '%s\n' "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf '%s\n' "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # Dump the cache to stdout. It can be in a pipe (this is a requirement). ac_cache_dump () { # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf '%s\n' "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # 'set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # 'set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) } # Print debugging info to stdout. ac_dump_debugging_info () { echo printf '%s\n' "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo ac_cache_dump echo printf '%s\n' "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'*) ac_val=`printf '%s\n' "$ac_val" | sed "s/'/'\\\\\\\\''/g"`;; esac printf '%s\n' "$ac_var='$ac_val'" done | sort echo if test -n "$ac_subst_files"; then printf '%s\n' "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'*) ac_val=`printf '%s\n' "$ac_val" | sed "s/'/'\\\\\\\\''/g"`;; esac printf '%s\n' "$ac_var='$ac_val'" done | sort echo fi if test -s confdefs.h; then printf '%s\n' "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf '%s\n' "$as_me: caught signal $ac_signal" printf '%s\n' "$as_me: exit $exit_status" } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. ac_exit_trap () { exit_status= # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. ac_dump_debugging_info >&5 eval "rm -f $ac_clean_CONFIG_STATUS core *.core core.conftest.*" && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status } trap 'ac_exit_trap $?' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf '%s\n' "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf '%s\n' "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf '%s\n' "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf '%s\n' "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf '%s\n' "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf '%s\n' "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf '%s\n' "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf '%s\n' "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See 'config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf '%s\n' "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf '%s\n' "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C23 (global declarations) ac_c_conftest_c23_globals=' /* Does the compiler advertise conformance to C17 or earlier? Although GCC 14 does not do that, even with -std=gnu23, it is close enough, and defines __STDC_VERSION == 202000L. */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ <= 201710L # error "Compiler advertises conformance to C17 or earlier" #endif // Check alignas. char alignas (double) c23_aligned_as_double; char alignas (0) c23_no_special_alignment; extern char c23_aligned_as_int; char alignas (0) alignas (int) c23_aligned_as_int; // Check alignof. enum { c23_int_alignment = alignof (int), c23_int_array_alignment = alignof (int[100]), c23_char_alignment = alignof (char) }; static_assert (0 < -alignof (int), "alignof is signed"); int function_with_unnamed_parameter (int) { return 0; } void c23_noreturn (); /* Test parsing of string and char UTF-8 literals (including hex escapes). The parens pacify GCC 15. */ bool use_u8 = (!sizeof u8"\xFF") == (!u8'\''x'\''); bool check_that_bool_works = true | false | !nullptr; #if !true # error "true does not work in #if" #endif #if false #elifdef __STDC_VERSION__ #else # error "#elifdef does not work" #endif #ifndef __has_c_attribute # error "__has_c_attribute not defined" #endif #ifndef __has_include # error "__has_include not defined" #endif #define LPAREN() ( #define FORTY_TWO(x) 42 #define VA_OPT_TEST(r, x, ...) __VA_OPT__ (FORTY_TWO r x)) static_assert (VA_OPT_TEST (LPAREN (), 0, <:-) == 42); static_assert (0b101010 == 42); static_assert (0B101010 == 42); static_assert (0xDEAD'\''BEEF == 3'\''735'\''928'\''559); static_assert (0.500'\''000'\''000 == 0.5); enum unsignedish : unsigned int { uione = 1 }; static_assert (0 < -uione); #include constexpr nullptr_t null_pointer = nullptr; static typeof (1 + 1L) two () { return 2; } static long int three () { return 3; } ' # Test code for whether the C compiler supports C23 (body of main). ac_c_conftest_c23_main=' { label_before_declaration: int arr[10] = {}; if (arr[0]) goto label_before_declaration; if (!arr[0]) goto label_at_end_of_block; label_at_end_of_block: } ok |= !null_pointer; ok |= two != three; ' # Test code for whether the C compiler supports C23 (complete). ac_c_conftest_c23_program="${ac_c_conftest_c23_globals} int main (int, char **) { int ok = 0; ${ac_c_conftest_c23_main} return ok; } " # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Do not test the value of __STDC__, because some compilers define it to 0 or do not define it, while otherwise adequately conforming. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (char **p, int i) { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* C89 style stringification. */ #define noexpand_stringify(a) #a const char *stringified = noexpand_stringify(arbitrary+token=sequence); /* C89 style token pasting. Exercises some of the corner cases that e.g. old MSVC gets wrong, but not very hard. */ #define noexpand_concat(a,b) a##b #define expand_concat(a,b) noexpand_concat(a,b) extern int vA; extern int vbee; #define aye A #define bee B int *pvA = &expand_concat(v,aye); int *pvbee = &noexpand_concat(v,bee); /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' /* Does the compiler advertise C99 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif // See if C++-style comments work. #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr, and "aND" is used instead of "and" to work around // GCC bug 40564 which is irrelevant here. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, aND third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; const char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. static struct incomplete_array *volatile incomplete_array_pointer; struct incomplete_array *ia = incomplete_array_pointer; ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Work around memory leak warnings. free (ia); // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; // Do not test for VLAs, as some otherwise-conforming compilers lack them. // C code should instead use __STDC_NO_VLA__; see Autoconf manual. // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || ni.number != 58); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' /* Does the compiler advertise C11 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="install-sh config.guess config.sub" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}/config" # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf '%s\n' "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf '%s\n' "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf '%s\n' "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf '%s\n' "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf '%s\n' "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf '%s\n' "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;; esac fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 printf '%s\n' "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 printf '%s\n' "$as_me: error: '$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w= for ac_val in x $ac_old_val; do ac_old_val_w="$ac_old_val_w $ac_val" done ac_new_val_w= for ac_val in x $ac_new_val; do ac_new_val_w="$ac_new_val_w $ac_val" done if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 printf '%s\n' "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 printf '%s\n' "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 printf '%s\n' "$as_me: former value: '$ac_old_val'" >&2;} { printf '%s\n' "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 printf '%s\n' "$as_me: current value: '$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf '%s\n' "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf '%s\n' "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config/acconfig.h" # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf '%s\n' "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`printf '%s\n' "$build_os" | sed 's/ /-/g'`;; esac { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else case e in #( e) if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf '%s\n' "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`printf '%s\n' "$host_os" | sed 's/ /-/g'`;; esac case $host_os in *cygwin* ) CYGWIN=yes;; * ) CYGWIN=no;; esac printf '%s\n' "#define HOST_SYSTEM_TYPE \"$host\"" >>confdefs.h ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf '%s\n' "$CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf '%s\n' "$ac_ct_CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf '%s\n' "$CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf '%s\n' "$CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf '%s\n' "$CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf '%s\n' "$ac_ct_CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf '%s\n' "$CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf '%s\n' "$ac_ct_CC" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf '%s\n' "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See 'config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf '%s\n' "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'. # So ignore a value of 'no', otherwise this would lead to 'EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an '-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else case e in #( e) ac_file='' ;; esac fi if test -z "$ac_file" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } printf '%s\n' "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See 'config.log' for more details" "$LINENO" 5; } else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf '%s\n' "yes" >&6; } ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf '%s\n' "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both 'conftest.exe' and 'conftest' are 'present' (well, observable) # catch 'conftest.exe'. For instance with Cygwin, 'ls conftest' will # work properly (i.e., refer to 'conftest.exe'), while it won't with # 'rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else case e in #( e) { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See 'config.log' for more details" "$LINENO" 5; } ;; esac fi rm -f conftest conftest$ac_cv_exeext { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf '%s\n' "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); if (!f) return 1; return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use '--host'. See 'config.log' for more details" "$LINENO" 5; } fi fi fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf '%s\n' "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext \ conftest.o conftest.obj conftest.out ac_clean_files=$ac_clean_files_save { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf '%s\n' "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else case e in #( e) printf '%s\n' "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See 'config.log' for more details" "$LINENO" 5; } ;; esac fi rm -f conftest.$ac_cv_objext conftest.$ac_ext ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf '%s\n' "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else case e in #( e) ac_compiler_gnu=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf '%s\n' "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else case e in #( e) CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf '%s\n' "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C23 features" >&5 printf %s "checking for $CC option to enable C23 features... " >&6; } if test ${ac_cv_prog_cc_c23+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c23=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c23_program _ACEOF for ac_arg in '' -std=gnu23 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c23=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c23" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c23" = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf '%s\n' "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c23" = x then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf '%s\n' "none needed" >&6; } else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c23" >&5 printf '%s\n' "$ac_cv_prog_cc_c23" >&6; } CC="$CC $ac_cv_prog_cc_c23" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c23 ac_prog_cc_stdc=c23 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 -std:c11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf '%s\n' "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c11" = x then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf '%s\n' "none needed" >&6; } else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf '%s\n' "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf '%s\n' "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c99" = x then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf '%s\n' "none needed" >&6; } else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf '%s\n' "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf '%s\n' "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c89" = x then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf '%s\n' "none needed" >&6; } else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf '%s\n' "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 ;; esac fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf '%s\n' "yes" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf '%s\n' "no, using $LN_S" >&6; } fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf '%s\n' "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else case e in #( e) cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @printf '%s\n' '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make ;; esac fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf '%s\n' "yes" >&6; } SET_MAKE= else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF/1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF/1 since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir ;; esac fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf '%s\n' "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for getaddrinfo in -lnsl" >&5 printf %s "checking for getaddrinfo in -lnsl... " >&6; } if test ${ac_cv_lib_nsl_getaddrinfo+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char getaddrinfo (void); int main (void) { return getaddrinfo (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_nsl_getaddrinfo=yes else case e in #( e) ac_cv_lib_nsl_getaddrinfo=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_getaddrinfo" >&5 printf '%s\n' "$ac_cv_lib_nsl_getaddrinfo" >&6; } if test "x$ac_cv_lib_nsl_getaddrinfo" = xyes then : printf '%s\n' "#define HAVE_LIBNSL 1" >>confdefs.h LIBS="-lnsl $LIBS" fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for connect in -lsocket" >&5 printf %s "checking for connect in -lsocket... " >&6; } if test ${ac_cv_lib_socket_connect+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char connect (void); int main (void) { return connect (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_connect=yes else case e in #( e) ac_cv_lib_socket_connect=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_connect" >&5 printf '%s\n' "$ac_cv_lib_socket_connect" >&6; } if test "x$ac_cv_lib_socket_connect" = xyes then : printf '%s\n' "#define HAVE_LIBSOCKET 1" >>confdefs.h LIBS="-lsocket $LIBS" fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for inet_aton in -lresolv" >&5 printf %s "checking for inet_aton in -lresolv... " >&6; } if test ${ac_cv_lib_resolv_inet_aton+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char inet_aton (void); int main (void) { return inet_aton (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_resolv_inet_aton=yes else case e in #( e) ac_cv_lib_resolv_inet_aton=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_inet_aton" >&5 printf '%s\n' "$ac_cv_lib_resolv_inet_aton" >&6; } if test "x$ac_cv_lib_resolv_inet_aton" = xyes then : printf '%s\n' "#define HAVE_LIBRESOLV 1" >>confdefs.h LIBS="-lresolv $LIBS" fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for sin in -lm" >&5 printf %s "checking for sin in -lm... " >&6; } if test ${ac_cv_lib_m_sin+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char sin (void); int main (void) { return sin (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_m_sin=yes else case e in #( e) ac_cv_lib_m_sin=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sin" >&5 printf '%s\n' "$ac_cv_lib_m_sin" >&6; } if test "x$ac_cv_lib_m_sin" = xyes then : printf '%s\n' "#define HAVE_LIBM 1" >>confdefs.h LIBS="-lm $LIBS" fi ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf '%s\n' "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf '%s\n' "#define STDC_HEADERS 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "arpa/inet.h" "ac_cv_header_arpa_inet_h" "$ac_includes_default" if test "x$ac_cv_header_arpa_inet_h" = xyes then : printf '%s\n' "#define HAVE_ARPA_INET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" if test "x$ac_cv_header_fcntl_h" = xyes then : printf '%s\n' "#define HAVE_FCNTL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes then : printf '%s\n' "#define HAVE_LIMITS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default" if test "x$ac_cv_header_netdb_h" = xyes then : printf '%s\n' "#define HAVE_NETDB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_in_h" = xyes then : printf '%s\n' "#define HAVE_NETINET_IN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" if test "x$ac_cv_header_stdlib_h" = xyes then : printf '%s\n' "#define HAVE_STDLIB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default" if test "x$ac_cv_header_string_h" = xyes then : printf '%s\n' "#define HAVE_STRING_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "strings.h" "ac_cv_header_strings_h" "$ac_includes_default" if test "x$ac_cv_header_strings_h" = xyes then : printf '%s\n' "#define HAVE_STRINGS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/ioctl.h" "ac_cv_header_sys_ioctl_h" "$ac_includes_default" if test "x$ac_cv_header_sys_ioctl_h" = xyes then : printf '%s\n' "#define HAVE_SYS_IOCTL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" if test "x$ac_cv_header_sys_socket_h" = xyes then : printf '%s\n' "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" if test "x$ac_cv_header_sys_time_h" = xyes then : printf '%s\n' "#define HAVE_SYS_TIME_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" if test "x$ac_cv_header_unistd_h" = xyes then : printf '%s\n' "#define HAVE_UNISTD_H 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes then : else case e in #( e) printf '%s\n' "#define size_t unsigned int" >>confdefs.h ;; esac fi # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for working alloca.h" >&5 printf %s "checking for working alloca.h... " >&6; } if test ${ac_cv_working_alloca_h+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { char *p = (char *) alloca (2 * sizeof (int)); if (p) return 0; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_working_alloca_h=yes else case e in #( e) ac_cv_working_alloca_h=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_alloca_h" >&5 printf '%s\n' "$ac_cv_working_alloca_h" >&6; } if test $ac_cv_working_alloca_h = yes; then printf '%s\n' "#define HAVE_ALLOCA_H 1" >>confdefs.h fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for alloca" >&5 printf %s "checking for alloca... " >&6; } if test ${ac_cv_func_alloca_works+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_func_alloca_works=$ac_cv_working_alloca_h if test "$ac_cv_func_alloca_works" != yes then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifndef alloca # ifdef __GNUC__ # define alloca __builtin_alloca # elif defined _MSC_VER # include # define alloca _alloca # else # ifdef __cplusplus extern "C" # endif void *alloca (size_t); # endif #endif int main (void) { char *p = (char *) alloca (1); if (p) return 0; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_func_alloca_works=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_alloca_works" >&5 printf '%s\n' "$ac_cv_func_alloca_works" >&6; } if test $ac_cv_func_alloca_works = yes; then printf '%s\n' "#define HAVE_ALLOCA 1" >>confdefs.h else # The SVR3 libPW and SVR4 libucb both contain incompatible functions # that cause trouble. Some versions do not even contain alloca or # contain a buggy version. If you still want to use their alloca, # use ar to extract alloca.o from them instead of compiling alloca.c. ALLOCA=\${LIBOBJDIR}alloca.$ac_objext printf '%s\n' "#define C_ALLOCA 1" >>confdefs.h { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking stack direction for C alloca" >&5 printf %s "checking stack direction for C alloca... " >&6; } if test ${ac_cv_c_stack_direction+y} then : printf %s "(cached) " >&6 else case e in #( e) if test "$cross_compiling" = yes then : ac_cv_c_stack_direction=0 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int find_stack_direction (int *addr, int depth) { int dir, dummy = 0; if (! addr) addr = &dummy; *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1; dir = depth ? find_stack_direction (addr, depth - 1) : 0; return dir + dummy; } int main (int argc, char **argv) { return find_stack_direction (0, argc + !argv + 20) < 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_stack_direction=1 else case e in #( e) ac_cv_c_stack_direction=-1 ;; esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_stack_direction" >&5 printf '%s\n' "$ac_cv_c_stack_direction" >&6; } printf '%s\n' "#define STACK_DIRECTION $ac_cv_c_stack_direction" >>confdefs.h fi for ac_func in strftime do : ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime" if test "x$ac_cv_func_strftime" = xyes then : printf '%s\n' "#define HAVE_STRFTIME 1" >>confdefs.h else case e in #( e) # strftime is in -lintl on SCO UNIX. { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for strftime in -lintl" >&5 printf %s "checking for strftime in -lintl... " >&6; } if test ${ac_cv_lib_intl_strftime+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lintl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char strftime (void); int main (void) { return strftime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_intl_strftime=yes else case e in #( e) ac_cv_lib_intl_strftime=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_intl_strftime" >&5 printf '%s\n' "$ac_cv_lib_intl_strftime" >&6; } if test "x$ac_cv_lib_intl_strftime" = xyes then : printf '%s\n' "#define HAVE_STRFTIME 1" >>confdefs.h LIBS="-lintl $LIBS" fi ;; esac fi done ac_fn_c_check_func "$LINENO" "getaddrinfo" "ac_cv_func_getaddrinfo" if test "x$ac_cv_func_getaddrinfo" = xyes then : printf '%s\n' "#define HAVE_GETADDRINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getnameinfo" "ac_cv_func_getnameinfo" if test "x$ac_cv_func_getnameinfo" = xyes then : printf '%s\n' "#define HAVE_GETNAMEINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" if test "x$ac_cv_func_gettimeofday" = xyes then : printf '%s\n' "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inet_ntoa" "ac_cv_func_inet_ntoa" if test "x$ac_cv_func_inet_ntoa" = xyes then : printf '%s\n' "#define HAVE_INET_NTOA 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memset" "ac_cv_func_memset" if test "x$ac_cv_func_memset" = xyes then : printf '%s\n' "#define HAVE_MEMSET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "select" "ac_cv_func_select" if test "x$ac_cv_func_select" = xyes then : printf '%s\n' "#define HAVE_SELECT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" if test "x$ac_cv_func_setenv" = xyes then : printf '%s\n' "#define HAVE_SETENV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "socket" "ac_cv_func_socket" if test "x$ac_cv_func_socket" = xyes then : printf '%s\n' "#define HAVE_SOCKET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strchr" "ac_cv_func_strchr" if test "x$ac_cv_func_strchr" = xyes then : printf '%s\n' "#define HAVE_STRCHR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup" if test "x$ac_cv_func_strdup" = xyes then : printf '%s\n' "#define HAVE_STRDUP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strstr" "ac_cv_func_strstr" if test "x$ac_cv_func_strstr" = xyes then : printf '%s\n' "#define HAVE_STRSTR 1" >>confdefs.h fi case "$host" in *darwin*) printf '%s\n' "#define BSD_IP_STACK 1" >>confdefs.h printf '%s\n' "#define DARWIN 1" >>confdefs.h ;; *netbsd*) printf '%s\n' "#define BSD_IP_STACK 1" >>confdefs.h printf '%s\n' "#define NETBSD 1" >>confdefs.h ;; *openbsd*) printf '%s\n' "#define OPENBSD 1" >>confdefs.h printf '%s\n' "#define BSD_IP_STACK 1" >>confdefs.h ;; *bsd*) printf '%s\n' "#define BSD_IP_STACK 1" >>confdefs.h ;; *linux*) printf '%s\n' "#define _BSD_SOURCE 1" >>confdefs.h ;; *solaris*) case "$host" in *solaris2.4*|*solaris2.5*) printf '%s\n' "#define SOLARIS_LENGTH_IN_CHECKSUM 1" >>confdefs.h ;; esac ;; esac case "$host" in *cygwin*) LIBS="-lws2_32 $LIBS" ;; *) # Check whether --enable-universal was given. if test ${enable_universal+y} then : enableval=$enable_universal; UNIVERSAL="$enableval" else case e in #( e) UNIVERSAL="no" ;; esac fi # Check whether --enable-gtod was given. if test ${enable_gtod+y} then : enableval=$enable_gtod; GTOD="$enableval" else case e in #( e) GTOD="no" ;; esac fi lft_candidate_prefixes="/opt/homebrew /usr/local /opt/local /opt/pkg" pcap_path_override=no # Check whether --with-pcap was given. if test ${with_pcap+y} then : withval=$with_pcap; { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for --with-pcap option" >&5 printf %s "checking for --with-pcap option... " >&6; } case "$withval" in yes|no) as_fn_error $? "please specify a PATH in --with-pcap option!" "$LINENO" 5 ;; *) if test '!' -d "$withval"; then as_fn_error $? "$withval does not exist!" "$LINENO" 5 else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 printf '%s\n' "$withval" >&6; } pcap_path_override=yes if test -d "$withval/include"; then CFLAGS="$CFLAGS -I$withval/include" CPPFLAGS="$CPPFLAGS -I$withval/include" else CFLAGS="$CFLAGS -I$withval" CPPFLAGS="$CPPFLAGS -I$withval" fi if test -d "$withval/lib"; then LIBS="$LIBS -L$withval/lib" else LIBS="$LIBS -L$withval" fi fi ;; esac fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { u_int i = sizeof(((struct sockaddr *)0)->sa_len) ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf '%s\n' "#define HAVE_SOCKADDR_SA_LEN 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext have_pcap=no if test "x$pcap_path_override" = "xno" then : # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKGCONFIG+y} then : printf %s "(cached) " >&6 else case e in #( e) case $PKGCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKGCONFIG="$PKGCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PKGCONFIG="$as_dir$ac_word$ac_exec_ext" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_PKGCONFIG" && ac_cv_path_PKGCONFIG="no" ;; esac ;; esac fi PKGCONFIG=$ac_cv_path_PKGCONFIG if test -n "$PKGCONFIG"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $PKGCONFIG" >&5 printf '%s\n' "$PKGCONFIG" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi if test "x$PKGCONFIG" != "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for libpcap via pkg-config" >&5 printf %s "checking for libpcap via pkg-config... " >&6; } if $PKGCONFIG --exists libpcap 2>/dev/null then : pcap_cflags=`$PKGCONFIG --cflags libpcap` pcap_libs=`$PKGCONFIG --libs libpcap` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $pcap_cflags" LIBS="$LIBS $pcap_libs" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { pcap_if_t *d; pcap_findalldevs(&d, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : have_pcap=yes { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes ($pcap_libs)" >&5 printf '%s\n' "yes ($pcap_libs)" >&6; } else case e in #( e) CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no (link test failed; wrong architecture?)" >&5 printf '%s\n' "no (link test failed; wrong architecture?)" >&6; } ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } ;; esac fi fi fi if test "x$have_pcap" = "xno" && test "x$pcap_path_override" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for libpcap in candidate prefixes" >&5 printf %s "checking for libpcap in candidate prefixes... " >&6; } for _pfx in $lft_candidate_prefixes; do if test -f "$_pfx/include/pcap.h"; then CPPFLAGS="$CPPFLAGS -I$_pfx/include" CFLAGS="$CFLAGS -I$_pfx/include" LDFLAGS="$LDFLAGS -L$_pfx/lib" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $_pfx" >&5 printf '%s\n' "$_pfx" >&6; } break fi done if test "x$_pfx" = "x" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf '%s\n' "not found" >&6; } fi fi if test "x$have_pcap" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for pcap_findalldevs in -lpcap" >&5 printf %s "checking for pcap_findalldevs in -lpcap... " >&6; } if test ${ac_cv_lib_pcap_pcap_findalldevs+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lpcap $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char pcap_findalldevs (void); int main (void) { return pcap_findalldevs (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pcap_pcap_findalldevs=yes else case e in #( e) ac_cv_lib_pcap_pcap_findalldevs=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_findalldevs" >&5 printf '%s\n' "$ac_cv_lib_pcap_pcap_findalldevs" >&6; } if test "x$ac_cv_lib_pcap_pcap_findalldevs" = xyes then : have_pcap=yes; LIBS="-lpcap $LIBS" else case e in #( e) as_fn_error $? " libpcap was not found. It is required to build LFT. Please install the libpcap development library and re-run ./configure. On Debian/Ubuntu: sudo apt-get install libpcap-dev On RHEL/Fedora: sudo dnf install libpcap-devel On FreeBSD ports: net/libpcap On macOS Homebrew: brew install libpcap On macOS MacPorts: sudo port install libpcap Or specify its location with: ./configure --with-pcap=PATH" "$LINENO" 5 ;; esac fi ac_fn_c_check_header_compile "$LINENO" "pcap.h" "ac_cv_header_pcap_h" "$ac_includes_default" if test "x$ac_cv_header_pcap_h" = xyes then : else case e in #( e) as_fn_error $? " pcap.h was not found. The libpcap library was detected but its headers are missing. Please install the libpcap development package. On Debian/Ubuntu: sudo apt-get install libpcap-dev On RHEL/Fedora: sudo dnf install libpcap-devel On FreeBSD ports: net/libpcap On macOS Homebrew: brew install libpcap On macOS MacPorts: sudo port install libpcap Or specify its location with: ./configure --with-pcap=PATH When building libpcap from source, run both 'make install' and 'make install-incl'." "$LINENO" 5 ;; esac fi fi # Check whether --enable-async-dns was given. if test ${enable_async_dns+y} then : enableval=$enable_async_dns; enable_async_dns="$enableval" else case e in #( e) enable_async_dns="yes" ;; esac fi cares_path_override=no # Check whether --with-cares was given. if test ${with_cares+y} then : withval=$with_cares; case "$withval" in yes|no) as_fn_error $? "please specify a PATH in --with-cares option!" "$LINENO" 5 ;; *) if test '!' -d "$withval"; then as_fn_error $? "$withval does not exist!" "$LINENO" 5 fi cares_path_override=yes if test -d "$withval/include"; then CPPFLAGS="$CPPFLAGS -I$withval/include" CFLAGS="$CFLAGS -I$withval/include" else CPPFLAGS="$CPPFLAGS -I$withval" CFLAGS="$CFLAGS -I$withval" fi if test -d "$withval/lib"; then LDFLAGS="$LDFLAGS -L$withval/lib" else LDFLAGS="$LDFLAGS -L$withval" fi ;; esac fi if test "x$enable_async_dns" = "xyes" then : have_cares=no if test "x$cares_path_override" = "xno" then : if test "x$PKGCONFIG" != "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for libcares via pkg-config" >&5 printf %s "checking for libcares via pkg-config... " >&6; } if $PKGCONFIG --exists libcares 2>/dev/null then : cares_cflags=`$PKGCONFIG --cflags libcares` cares_libs=`$PKGCONFIG --libs libcares` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $cares_cflags" LIBS="$LIBS $cares_libs" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { ares_channel c; ares_init(&c); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : have_cares=yes { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes ($cares_libs)" >&5 printf '%s\n' "yes ($cares_libs)" >&6; } else case e in #( e) CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no (link test failed; wrong architecture?)" >&5 printf '%s\n' "no (link test failed; wrong architecture?)" >&6; } ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } ;; esac fi fi fi if test "x$have_cares" = "xno" && test "x$cares_path_override" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for c-ares in candidate prefixes" >&5 printf %s "checking for c-ares in candidate prefixes... " >&6; } for _pfx in $lft_candidate_prefixes; do if test -f "$_pfx/include/ares.h"; then CPPFLAGS="$CPPFLAGS -I$_pfx/include" CFLAGS="$CFLAGS -I$_pfx/include" LDFLAGS="$LDFLAGS -L$_pfx/lib" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $_pfx" >&5 printf '%s\n' "$_pfx" >&6; } break fi done if test "x$_pfx" = "x" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf '%s\n' "not found" >&6; } fi fi if test "x$have_cares" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for ares_init in -lcares" >&5 printf %s "checking for ares_init in -lcares... " >&6; } if test ${ac_cv_lib_cares_ares_init+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lcares $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char ares_init (void); int main (void) { return ares_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_cares_ares_init=yes else case e in #( e) ac_cv_lib_cares_ares_init=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cares_ares_init" >&5 printf '%s\n' "$ac_cv_lib_cares_ares_init" >&6; } if test "x$ac_cv_lib_cares_ares_init" = xyes then : have_cares=maybe fi if test "x$have_cares" = "xmaybe" then : ac_fn_c_check_header_compile "$LINENO" "ares.h" "ac_cv_header_ares_h" "$ac_includes_default" if test "x$ac_cv_header_ares_h" = xyes then : have_cares=yes LIBS="-lcares $LIBS" printf '%s\n' "#define HAVE_CARES 1" >>confdefs.h else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: c-ares library found but ares.h not found; disabling async DNS" >&5 printf '%s\n' "$as_me: WARNING: c-ares library found but ares.h not found; disabling async DNS" >&2;} ;; esac fi fi fi if test "x$have_cares" = "xyes" then : if test "x$cares_path_override" = "xyes" || test "x$PKGCONFIG" = "xno" then : case $LIBS in #( *-lcares*) : ;; #( *) : LIBS="-lcares $LIBS" printf '%s\n' "#define HAVE_CARES 1" >>confdefs.h ;; esac fi fi if test "x$have_cares" != "xyes" then : lft_warn_no_cares=yes fi fi # Check whether --enable-ncurses was given. if test ${enable_ncurses+y} then : enableval=$enable_ncurses; enable_ncurses="$enableval" else case e in #( e) enable_ncurses="yes" ;; esac fi ncurses_path_override=no # Check whether --with-ncurses was given. if test ${with_ncurses+y} then : withval=$with_ncurses; case "$withval" in yes|no) as_fn_error $? "please specify a PATH in --with-ncurses option!" "$LINENO" 5 ;; *) if test '!' -d "$withval"; then as_fn_error $? "$withval does not exist!" "$LINENO" 5 fi ncurses_path_override=yes if test -d "$withval/include"; then CPPFLAGS="$CPPFLAGS -I$withval/include" CFLAGS="$CFLAGS -I$withval/include" else CPPFLAGS="$CPPFLAGS -I$withval" CFLAGS="$CFLAGS -I$withval" fi if test -d "$withval/lib"; then LDFLAGS="$LDFLAGS -L$withval/lib" else LDFLAGS="$LDFLAGS -L$withval" fi ;; esac fi if test "x$enable_ncurses" = "xyes" then : have_ncurses=no if test "x$ncurses_path_override" = "xno" then : if test "x$PKGCONFIG" != "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for ncursesw via pkg-config" >&5 printf %s "checking for ncursesw via pkg-config... " >&6; } if $PKGCONFIG --exists ncursesw 2>/dev/null then : ncurses_cflags=`$PKGCONFIG --cflags ncursesw` ncurses_libs=`$PKGCONFIG --libs ncursesw` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $ncurses_cflags" LIBS="$LIBS $ncurses_libs" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { initscr(); endwin(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : have_ncurses=yes { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes ($ncurses_libs)" >&5 printf '%s\n' "yes ($ncurses_libs)" >&6; } else case e in #( e) CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no (link test failed; wrong architecture?)" >&5 printf '%s\n' "no (link test failed; wrong architecture?)" >&6; } ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } ;; esac fi if test "x$have_ncurses" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for ncurses via pkg-config" >&5 printf %s "checking for ncurses via pkg-config... " >&6; } if $PKGCONFIG --exists ncurses 2>/dev/null then : ncurses_cflags=`$PKGCONFIG --cflags ncurses` ncurses_libs=`$PKGCONFIG --libs ncurses` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $ncurses_cflags" LIBS="$LIBS $ncurses_libs" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { initscr(); endwin(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : have_ncurses=yes { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes ($ncurses_libs)" >&5 printf '%s\n' "yes ($ncurses_libs)" >&6; } else case e in #( e) CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no (link test failed; wrong architecture?)" >&5 printf '%s\n' "no (link test failed; wrong architecture?)" >&6; } ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext else case e in #( e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } ;; esac fi fi fi fi if test "x$have_ncurses" = "xno" && test "x$ncurses_path_override" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for ncurses in candidate prefixes" >&5 printf %s "checking for ncurses in candidate prefixes... " >&6; } for _pfx in $lft_candidate_prefixes; do if test -f "$_pfx/include/ncurses.h"; then CPPFLAGS="$CPPFLAGS -I$_pfx/include" CFLAGS="$CFLAGS -I$_pfx/include" LDFLAGS="$LDFLAGS -L$_pfx/lib" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $_pfx" >&5 printf '%s\n' "$_pfx" >&6; } break fi done if test "x$_pfx" = "x" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf '%s\n' "not found" >&6; } fi fi if test "x$have_ncurses" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for initscr in -lncursesw" >&5 printf %s "checking for initscr in -lncursesw... " >&6; } if test ${ac_cv_lib_ncursesw_initscr+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lncursesw $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char initscr (void); int main (void) { return initscr (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ncursesw_initscr=yes else case e in #( e) ac_cv_lib_ncursesw_initscr=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncursesw_initscr" >&5 printf '%s\n' "$ac_cv_lib_ncursesw_initscr" >&6; } if test "x$ac_cv_lib_ncursesw_initscr" = xyes then : have_ncurses=maybe_w fi if test "x$have_ncurses" = "xmaybe_w" then : ac_fn_c_check_header_compile "$LINENO" "ncurses.h" "ac_cv_header_ncurses_h" "$ac_includes_default" if test "x$ac_cv_header_ncurses_h" = xyes then : have_ncurses=yes LIBS="-lncursesw $LIBS" printf '%s\n' "#define HAVE_NCURSES 1" >>confdefs.h else case e in #( e) have_ncurses=no { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: ncursesw library found but ncurses.h not found; disabling ncurses mode" >&5 printf '%s\n' "$as_me: WARNING: ncursesw library found but ncurses.h not found; disabling ncurses mode" >&2;} ;; esac fi fi fi if test "x$have_ncurses" = "xno" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for initscr in -lncurses" >&5 printf %s "checking for initscr in -lncurses... " >&6; } if test ${ac_cv_lib_ncurses_initscr+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lncurses $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char initscr (void); int main (void) { return initscr (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ncurses_initscr=yes else case e in #( e) ac_cv_lib_ncurses_initscr=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_initscr" >&5 printf '%s\n' "$ac_cv_lib_ncurses_initscr" >&6; } if test "x$ac_cv_lib_ncurses_initscr" = xyes then : have_ncurses=maybe fi if test "x$have_ncurses" = "xmaybe" then : ac_fn_c_check_header_compile "$LINENO" "ncurses.h" "ac_cv_header_ncurses_h" "$ac_includes_default" if test "x$ac_cv_header_ncurses_h" = xyes then : have_ncurses=yes LIBS="-lncurses $LIBS" printf '%s\n' "#define HAVE_NCURSES 1" >>confdefs.h else case e in #( e) have_ncurses=no { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: ncurses library found but ncurses.h not found; disabling ncurses mode" >&5 printf '%s\n' "$as_me: WARNING: ncurses library found but ncurses.h not found; disabling ncurses mode" >&2;} ;; esac fi fi fi if test "x$have_ncurses" = "xyes" then : if test "x$ncurses_path_override" = "xyes" || test "x$PKGCONFIG" = "xno" then : case $LIBS in #( *-lncurses*) : ;; #( *) : LIBS="-lncurses $LIBS" printf '%s\n' "#define HAVE_NCURSES 1" >>confdefs.h ;; esac fi fi if test "x$have_ncurses" != "xyes" then : lft_warn_no_ncurses=yes fi fi if test "x$have_ncurses" = "xyes" then : WATCH_OBJS="lft_watch.o" else case e in #( e) WATCH_OBJS="" ;; esac fi esac { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking if we should build universal binaries" >&5 printf %s "checking if we should build universal binaries... " >&6; } if test "$UNIVERSAL" = "yes"; then case $host_os in *darwin*) CFLAGS="$CFLAGS -arch x86_64 -arch arm64" LDFLAGS="$LDFLAGS -arch x86_64 -arch arm64" printf '%s\n' "#define UNIVERSAL 1" >>confdefs.h { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes (x86_64 + arm64)" >&5 printf '%s\n' "yes (x86_64 + arm64)" >&6; } ;; *) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no (--enable-universal only applies to macOS)" >&5 printf '%s\n' "no (--enable-universal only applies to macOS)" >&6; } ;; esac else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking if we should call gettimeofday for each packet" >&5 printf %s "checking if we should call gettimeofday for each packet... " >&6; } if test "$GTOD" = "yes"; then printf '%s\n' "#define USE_GTOD 1" >>confdefs.h { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf '%s\n' "yes" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } fi ac_config_files="$ac_config_files Makefile" if test "x$lft_warn_no_cares" = "xyes" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: LFT is much faster with asynchronous DNS provided by the c-ares library, which was not detected. It is not required, but strongly recommended. Please consider installing it and re-running ./configure. On Debian/Ubuntu: sudo apt-get install libc-ares-dev On RHEL/Fedora: sudo dnf install c-ares-devel On macOS Homebrew: brew install c-ares On macOS MacPorts: sudo port install c-ares Or specify its location with: ./configure --with-cares=PATH" >&5 printf '%s\n' "$as_me: WARNING: LFT is much faster with asynchronous DNS provided by the c-ares library, which was not detected. It is not required, but strongly recommended. Please consider installing it and re-running ./configure. On Debian/Ubuntu: sudo apt-get install libc-ares-dev On RHEL/Fedora: sudo dnf install c-ares-devel On macOS Homebrew: brew install c-ares On macOS MacPorts: sudo port install c-ares Or specify its location with: ./configure --with-cares=PATH" >&2;} fi if test "x$lft_warn_no_ncurses" = "xyes" then : { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: LFT looks great using ncurses with a text-mode UI, which was not detected. The interactive continuous-monitoring mode (--watch) requires it. Please consider installing the ncurses development library and re-running ./configure. On Debian/Ubuntu: sudo apt-get install libncurses-dev On RHEL/Fedora: sudo dnf install ncurses-devel On macOS Homebrew: brew install ncurses On macOS MacPorts: sudo port install ncurses Or specify its location with: ./configure --with-ncurses=PATH" >&5 printf '%s\n' "$as_me: WARNING: LFT looks great using ncurses with a text-mode UI, which was not detected. The interactive continuous-monitoring mode (--watch) requires it. Please consider installing the ncurses development library and re-running ./configure. On Debian/Ubuntu: sudo apt-get install libncurses-dev On RHEL/Fedora: sudo dnf install ncurses-devel On macOS Homebrew: brew install ncurses On macOS MacPorts: sudo port install ncurses Or specify its location with: ./configure --with-ncurses=PATH" >&2;} fi cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # 'ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* 'ac_cv_foo' will be assigned the # following values. _ACEOF ac_cache_dump | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf '%s\n' "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf '%s\n' "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf '%s\n' "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" case $CONFIG_STATUS in #( -*) : CONFIG_STATUS=./$CONFIG_STATUS ;; #( */*) : ;; #( *) : CONFIG_STATUS=./$CONFIG_STATUS ;; esac ac_write_fail=0 ac_clean_CONFIG_STATUS='"$CONFIG_STATUS"' { printf '%s\n' "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf '%s\n' "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >"$CONFIG_STATUS" <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>"$CONFIG_STATUS" <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # contradicts POSIX and common usage. Disable this. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf '%s\n' "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf '%s\n' "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf '%s\n' X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf '%s\n' "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf '%s\n' X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated exec 6>&1 ## ------------------------------------- ## ## Main body of "$CONFIG_STATUS" script. ## ## ------------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x "$CONFIG_STATUS" || ac_write_fail=1 cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by lft $as_me 3.93, which was generated by GNU Autoconf 2.73. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ '$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF ac_cs_config=`printf '%s\n' "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf '%s\n' "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ lft config.status 3.93 configured by $0, generated by GNU Autoconf 2.73, with options \\"\$ac_cs_config\\" Copyright (C) 2026 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || { awk '' >"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf '%s\n' "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf '%s\n' "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf '%s\n' "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf '%s\n' "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: '$1' Try '$0 --help' for more information.";; --help | --hel | -h ) printf '%s\n' "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: '$1' Try '$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf '%s\n' "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf '%s\n' "$ac_log" } >&5 _ACEOF cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 _ACEOF cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config/acconfig.h") CONFIG_HEADERS="$CONFIG_HEADERS config/acconfig.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to '$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with './config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | sed -n '$='` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | sed -n '$='` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >"$CONFIG_STATUS" || ac_write_fail=1 rm -f conf$$subs.awk cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with './config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script 'defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >"$CONFIG_STATUS" || ac_write_fail=1 cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { suffix = P[macro] D[macro] while (suffix ~ /[\t ]$/) { suffix = substr(suffix, 1, length(suffix) - 1) } # Preserve the white space surrounding the "#". print prefix "define", macro suffix next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain ':'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf '%s\n' "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is 'configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf '%s\n' "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf '%s\n' "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf '%s\n' "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf '%s\n' "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf '%s\n' X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf '%s\n' "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf '%s\n' "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF # Neutralize VPATH when '$srcdir' = '.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf '%s\n' "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf '%s\n' "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf '%s\n' "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_CONFIG_STATUS= test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: case $CONFIG_STATUS in #( -*) : ac_no_opts=-- ;; #( *) : ac_no_opts= ;; esac ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $ac_no_opts "$CONFIG_STATUS" $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf '%s\n' "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi lft-3.98/lft_types.h000644 000765 000024 00000013356 15171551114 014361 0ustar00vicstaff000000 000000 /* * lft_types.h * Layer Four Traceroute * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * */ #ifndef LFT_TYPES_H #define LFT_TYPES_H #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) #include "config/acconfig.win.h" #else #include "config/acconfig.h" #endif #include #ifndef __FAVOR_BSD # define __FAVOR_BSD 1 #endif #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) #define __USE_W32_SOCKETS #include #include #include #include #define LITTLE_ENDIAN 1 #define BYTE_ORDER 1 typedef signed long n_long; typedef signed short n_short; typedef signed long n_time; #include #include #include #include #ifdef __CYGWIN__ # include #else # include "include/win32/wingetopt.h" # if defined(WIN32) || defined(_WIN32) # define SIZEOF_CHAR 1 # define SIZEOF_SHORT 2 # define SIZEOF_LONG 4 # define SIZEOF_LONG_LONG 8 # include "include/libpcap/bittypes.h" # include "include/libpcap/Gnuc.h" # define bzero(buf,cnt) memset(buf,'\0',cnt); # endif #endif #include "include/net/if_arp.h" #include "include/netinet/if_ether.h" #include "include/netinet/ip.h" #include "include/netinet/ip_icmp.h" #include "include/netinet/tcp.h" #include "include/netinet/udp.h" #else #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BSD #include #endif #ifdef BSD_IP_STACK #include #include #if !defined(DARWIN) && !defined(NETBSD) #define HAVE_SNPRINTF #define HAVE_VSNPRINTF #if !defined(OPENBSD) && !defined(__FreeBSD__) #include #endif #endif #endif #include #ifdef sun #include #include #endif #endif #include "lft_ifname.h" #include "lft_lsrr.h" #include "whois.h" #if defined(__OpenBSD__) #include #endif #if defined(__FreeBSD__) || defined(DARWIN) || defined(__APPLE__) #include #elif !defined(DARWIN) && !defined(NETBSD) #include "lft_queue.h" #endif #ifndef EXIT_FAILURE #define EXIT_FAILURE 1 #endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #ifdef __cplusplus extern "C" { #endif /* holds the pseudo-header for generating checksums */ struct sumh { unsigned int src; unsigned int dst; unsigned char fill; unsigned char protocol; unsigned short len; }; /* The actual packet data */ struct trace_packet_s { struct ip ip_hdr; struct ip_lsrr lsrr; /* must go here for ip checksum to work */ struct tcphdr tcp_hdr; struct udphdr udp_hdr; int size; char *payload; int payload_len; }; /* RFC 1393 type trace IP option */ struct rfc1393_ip_option_s { u_char optcode; //=82 u_char optlength; //=12 u_short id; //number to identify icmp trace messages u_short ohc; //outbound hop count u_short rhc; //return hop count struct in_addr origip; //originator ip address } __attribute__((packed)); /* ICMP echo header */ struct icmp_echo_header_s { u_char type; u_char code; u_short checksum; u_short id; u_short sequence; } __attribute__((packed)); /* ICMP trace response header */ struct icmp_trace_reply_header_s { u_char type; u_char code; u_short checksum; u_short id; u_short unused; u_short ohc; //outbound hop count u_short rhc; //return hop count u_long ols; //output link speed u_long olmtu; //output link MTU } __attribute__((packed)); /* Trace packet for RFC 1393 type and ICMP base trace */ struct icmp_trace_packet_s { char * packet; int packet_len; struct ip * ip_hdr; struct rfc1393_ip_option_s * icmp_trace_opt; struct ip_lsrr * lsrr; struct icmp_echo_header_s * echo; char * payload; int payload_len; }; /* Packet container with additional info */ struct trace_packet_info_s { int icmp_type; /* ICMP_UNREACH code, -1 if RST reply */ int is_done; /* is this a final hop? */ short hopno; unsigned int seq; struct timeval sent; struct timeval recv; /* 0 if unreceived */ struct in_addr hopaddr; /* IP address of router */ /* copy of EvtPacketInfoParam */ int asnumber; char netname[512]; char orgname[256]; /* org-name from pwhois bulk query */ struct in_addr last_hop; /*----------------------------*/ union { struct trace_packet_s packet; struct icmp_trace_packet_s icmp_packet; } u; SLIST_ENTRY(trace_packet_info_s) next_by_hop; SLIST_ENTRY(trace_packet_info_s) next; }; /* hop information, by ttl */ struct hop_info_s { int num_sent; int all_sent, all_rcvd; struct timeval ts_last_sent; struct timeval ts_last_recv; struct trace_packet_info_s * done_packet; unsigned short state; unsigned short flags; SLIST_HEAD(hop_packets_s, trace_packet_info_s) packets; uint16_t path_mtu; /* 0 = not determined; >0 = probe size at time of flagging */ int pmtud_blackhole; /* 1 = confirmed: responds to small probes, not large ones */ int pmtud_icmp_suppressor; /* 1 = reclassified: large probe confirmed forwarded downstream */ }; #ifdef __cplusplus } #endif #endif /*LFT_TYPES_H*/ lft-3.98/makefile.vc000644 000765 000024 00000010537 15165341545 014314 0ustar00vicstaff000000 000000 # ========================================================================= # LFT / WhoB — MSVC nmake build file # # Requirements: # Npcap SDK https://npcap.com/dist/npcap-sdk-1.13.zip (or later) # c-ares https://c-ares.org/ (build from source or vcpkg) # # Quick start (from a Visual Studio Developer Command Prompt): # set NPCAP_DIR=C:\npcap-sdk # set CARES_DIR=C:\cares # nmake -f makefile.vc # # To build a debug binary: # nmake -f makefile.vc DEBUG=1 # ========================================================================= # ------------------------------------------------------------------------- # Configurable paths — override on the nmake command line or via environment # ------------------------------------------------------------------------- # Npcap (or WinPcap) SDK root; must contain Include\ and Lib\x64\ (or Lib\) NPCAP_DIR = C:\npcap-sdk # c-ares SDK root; must contain include\ and lib\ CARES_DIR = C:\cares # Set to 1 for a debug build DEBUG = 0 # ------------------------------------------------------------------------- # Compiler / linker # ------------------------------------------------------------------------- CC = cl LINK = link # Platform-specific Npcap lib path (x64 preferred; fall back to Lib\ for x86) NPCAP_LIB = $(NPCAP_DIR)\Lib\x64 # ------------------------------------------------------------------------- # Flags # ------------------------------------------------------------------------- !if "$(DEBUG)" == "1" _OPT = /Od /Zi _DBG = /D_DEBUG _DBGPDB = _DBGSFX = d _LINKDBG = /DEBUG !else _OPT = /O2 _DBG = /DNDEBUG _DBGPDB = _DBGSFX = _LINKDBG = !endif BASE_CFLAGS = /nologo /TC /MD$(_DBGSFX) /W3 \ /DWIN32 /D_WIN32_WINNT=0x0601 \ $(_OPT) $(_DBG) \ /I"$(NPCAP_DIR)\Include" \ /I"$(CARES_DIR)\include" \ /I".\include\win32" LFT_CFLAGS = $(BASE_CFLAGS) /Fdlft.pdb WHOB_CFLAGS = $(BASE_CFLAGS) /Fdwhob.pdb /DSTANDALONE BASE_LIBS = ws2_32.lib Iphlpapi.lib advapi32.lib NPCAP_LIBS = "$(NPCAP_LIB)\wpcap.lib" "$(NPCAP_LIB)\Packet.lib" CARES_LIBS = "$(CARES_DIR)\lib\cares.lib" LFT_LIBS = $(BASE_LIBS) $(NPCAP_LIBS) $(CARES_LIBS) WHOB_LIBS = $(BASE_LIBS) $(CARES_LIBS) # ------------------------------------------------------------------------- # Object lists # ------------------------------------------------------------------------- LFT_OBJECTS = \ lft_lft.obj \ lft_lft_btcptrace.obj \ lft_lft_icmptrace.obj \ lft_lft_ifname.obj \ lft_lft_lib.obj \ lft_whois.obj \ lft_wingetopt.obj \ lft_wingettimeofday.obj \ lft_winlft_ifname.obj WHOB_OBJECTS = \ whob_whois.obj \ whob_wingetopt.obj # ------------------------------------------------------------------------- # Targets # ------------------------------------------------------------------------- all: lft.exe whob.exe clean: -if exist *.obj del *.obj -if exist *.res del *.res -if exist *.pch del *.pch -if exist *.pdb del *.pdb -if exist *.ilk del *.ilk -if exist lft.exe del lft.exe -if exist whob.exe del whob.exe lft.exe: $(LFT_OBJECTS) $(LINK) /NOLOGO $(_LINKDBG) /OUT:$@ $(LFT_OBJECTS) $(LFT_LIBS) whob.exe: $(WHOB_OBJECTS) $(LINK) /NOLOGO $(_LINKDBG) /OUT:$@ $(WHOB_OBJECTS) $(WHOB_LIBS) # ------------------------------------------------------------------------- # Compilation rules # ------------------------------------------------------------------------- lft_lft.obj: .\lft.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_lft_btcptrace.obj: .\lft_btcptrace.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_lft_icmptrace.obj: .\lft_icmptrace.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_lft_ifname.obj: .\lft_ifname.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_lft_lib.obj: .\lft_lib.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_whois.obj: .\whois.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_wingetopt.obj: .\include\win32\wingetopt.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_wingettimeofday.obj: .\include\win32\wingettimeofday.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** lft_winlft_ifname.obj: .\include\win32\winlft_ifname.c $(CC) /c /Fo$@ $(LFT_CFLAGS) $** whob_whois.obj: .\whois.c $(CC) /c /Fo$@ $(WHOB_CFLAGS) $** whob_wingetopt.obj: .\include\win32\wingetopt.c $(CC) /c /Fo$@ $(WHOB_CFLAGS) $** lft-3.98/CHANGELOG000644 000765 000024 00000070752 15174131552 013417 0ustar00vicstaff000000 000000 LFT CHANGELOG lft 3.98 / WhoB 3.98 ---------------------- - Build fixes for Linux: lft_watch.c now explicitly includes for struct winsize and TIOCGWINSZ. These were pulled in transitively on macOS but not on Linux glibc, breaking the build of --watch support out-of-the-box on Linux. - whob link line cleaned up: the whob CLI tool no longer drag-links the ncurses libraries (it never used them). Makefile / Makefile.in now define WHOB_LIBS = $(filter-out -lncurses -lncursesw,$(LIBS)) and use it in the whob target. c-ares stays in the whob link since whois.c uses it for the server-prefetch async resolver path. lft 3.97 / WhoB 3.97 ---------------------- - watch-mode UDP ECMP stability across cycles: watch_reset_session no longer zeros sess->seq_start between cycles, preserving the IP-ID base that UDP probes embed for ECMP flow-hash consistency. Without this, each watch cycle re-randomized the IP-ID base and took a different ECMP branch on multi-path networks, defeating the 3.97 UDP ECMP-stability fix. TCP (sport) and ICMP (icmp_initial_seq anchor) were already preserved correctly. - watch-mode hop count indicator no longer over-counts: with noisy>=2 (forced in watch mode for the log drawer), EVT_RECV_PACKET formerly incremented the live hop counter for every responding TTL including ahead-limit overshoot replies past the target; the handler now recounts from hop_info[] capped at num_hops+1, matching EVT_PROGRESS_OK. The watch status-bar peak counter (max_hops_seen) is now set to the current cycle's n_hops rather than maximized, so it cannot get sticky after a short path follows a long one. - watch-mode topology inheritance (--watch / -W): the first cycle is a conventional broad scan, but once the path length and RTT envelope have been stable for three consecutive cycles, subsequent cycles narrow ttl_limit to learned_num_hops + learn_margin (default 2), widen ahead_limit to the full known path width (capped at 16 to stay friendly to per-source ICMP rate limits), shrink timeout_ms to max(8 * max_observed_rtt, 200 ms), and tighten scatter_ms to 5. Auto-tune resets immediately if a cycle misses the target or the probe protocol changes, reverting to broad-scan defaults until the path stabilizes again. --no-learn disables the feature entirely; --learn-margin N (0 to 10) tunes the TTL probe margin above the learned path length. - UDP probe ECMP flow-hash stability: all probes in a UDP trace session now use a fixed destination port rather than incrementing the port with each TTL (the traditional VJ traceroute approach). Each probe is identified by a pseudorandom session-unique value embedded in the IP ID field; ICMP time-exceeded replies echo this value back in the quoted inner IP header, allowing exact probe-to-reply matching without varying the 5-tuple. The IP ID base is generated the same way as the TCP initial sequence number (rand()), ensuring it is non-zero and session-unique. This keeps every UDP probe on the same ECMP path. - ICMP probe ECMP flow-hash stability: a 2-byte compensation word appended to each ICMP echo payload keeps the ICMP checksum constant across all probes in a session. Because routers hash on the ICMP checksum field as a proxy for the missing port fields, holding the checksum constant ensures all probes follow the same ECMP path. - For completeness, our TCP tracing implementation supported ECMP flow-hash stability (and was the first tool to do this) since 1998. - Added a 'Capabilities' section to the version (-v) output to display the version of libraries against which LFT was linked at compile-time. lft 3.96 / WhoB 3.96 ---------------------- - rtt-statistics (-j N / --stats=N): per-hop RTT min/avg/max/stddev in all output modes; ASCII art candlestick chart above -o hop diagram; global Y-axis scale; jitter alert (>25 ms delta) highlighted in yellow with annotation; -o auto-activates with default 3 probes; -m/-M override probe count. - Added active Path MTU Discovery: -K / --mtu / --path-mtu starts probes at the local interface MTU (queried via SIOCGIFMTU; 1500 fallback) so that bottleneck links return ICMP "fragmentation needed" (type 3, code 4) messages identifying the path MTU; probe size steps down on each PTB received, using the next-hop MTU value or, for pre-RFC-1191 routers that return zero, the next lower RFC 1191 plateau (65535→32000→17914→8166→4352→2002→1492→1006→ 508→296→68); hops where the path MTU is constrained show [MTU: NNNN] in regular output, as pathmtu="NNNN" in XML, and as a separate row in ASCII art (-o); hops that silently drop oversized DF probes without returning a PTB show [MTU Limit] hop N dropped DF probe (NNNN bytes, no PTB received) immediately after the [neglected] line for that hop; -L is silently ignored when -K is active; documented in lft.8 with PTB terminology explained - Extended active PMTUD (-K) to ICMP probe mode (-p): open_sockets() now queries SIOCGIFMTU and sets sess->userlen for ICMP as it does for TCP/UDP; generateICMPPacket() picks up sess->userlen as the total packet size; PTB (type 3, code 4) responses are intercepted in icmp_process_packet() and handled by lft_pmtud_icmp_handle_ptb() which steps down sess->userlen the same way lft_pmtud_handle_ptb() steps down the TCP/UDP trace_packet template; icmp_check_timeouts() flags timed-out hops as pmtud_blackhole and calls lft_pmtud_verify_blackholes() (now non-static) before icmp_finish(); icmp_finish() emits per-hop [MTU Limit] / [neglected] output matching finish() behaviour; lft.8 -K section updated to reference all three probe protocols and explain the payload-inflation rationale - Migrated c-ares usage from deprecated API to c-ares 1.34+ event model: replaced ares_fds()/ares_process() with ARES_OPT_SOCK_STATE_CB / ares_process_fds() in both lft_lib.c (main trace loop) and whois.c (whois server prefetch); replaced ares_gethostbyname() with ares_getaddrinfo() in whois.c; removed all -Wdeprecated-declarations pragma suppressions; builds cleanly at -std=gnu23 with zero warnings - Annotation labels in -o ASCII art updated: stateful firewall now shows Stateful FW (U+21F6), flag-based filter shows Flag FW (U+2691), BSD TCP anomaly shows BSD Bug (U+21C4), AS boundary shows ASN Seam and network boundary shows NET Seam (both U+2573); plain ASCII fallbacks updated to [Stateful FW], [Flag FW], [BSD Bug] respectively; seam fallbacks retain x-AS-Seam-x / x-Net-Seam-x for compatibility - RTT chart x-axis label now shows end-to-end RTT derived from the target column's own min/max samples (not the global trace min/max), formatted as "End-to-End RTT Nmin–Nmax ms, N hops to proto://address:port" where proto reflects the active probe mode (tcp/udp/icmp) and port is the destination port; falls back to "N hops traced, target unreachable" when target was not reached; lft.8 -j entry updated accordingly - Updated include/win32/wingetopt.c to add getopt_long() implementation (required since 3.95 added getopt_long() throughout lft.c; Windows builds were broken at link time without it); wingetopt.h extended with struct option, no_argument / required_argument / optional_argument constants, getopt_long() declaration, and opterr / optopt globals - Rewrote makefile.vc (formerly auto-generated by the defunct Bakefile tool): now hand-maintained with NPCAP_DIR / CARES_DIR configurable paths, proper Npcap SDK linkage (wpcap.lib Packet.lib), c-ares linkage (cares.lib), correct include search paths, and a DEBUG=1 mode; documents SDK download URLs and quick-start instructions in the header comment - Removed deprecated lft.spec instructions - Updated config/install-sh - Updated config/config.guess and config/config.sub lft 3.95 / WhoB 3.95 ---------------------- - Replaced gethostbyname()/gethostbyaddr() with getaddrinfo()/getnameinfo() throughout (~50 calls) for modernization and prerequisite for IPv6 - Added asynchronous reverse-DNS via c-ares: PTR queries sent for each hop IP as it is revealed by an incoming ICMP time-exceeded response, merged into the existing select() loop via ares_fds()/ares_process(); results are ready by the time the trace completes, eliminating post-trace serial DNS latency (typically 180-260ms savings on a 10-15 hop trace) - Whois library lazy DNS resolution: w_init() registers known server hostnames in a cache but fires no lookups; a new w_prefetch() function resolves a single server on first use via c-ares when w_ask() is called, bypassing the OS resolver (and mDNSResponder on macOS) only for the service actually needed - New -Q flag disables async DNS at runtime without recompiling; documented in --help (under Advanced Resolution Options) and the manpage - -T timing display shows "resolving: X.XXs (async)" label when c-ares handled name resolution during the trace, distinguishing from the synchronous path - configure now probes common third-party prefix directories (/opt/homebrew, /usr/local, /opt/local, /opt/pkg) for both libpcap and c-ares before falling back to AC_CHECK_LIB; ./configure now finds Homebrew-installed c-ares on Apple Silicon without requiring manual LDFLAGS/CPPFLAGS - Added pkg-config support for c-ares detection (mirroring existing pcap path) - Added --with-cares=PATH configure option for manual c-ares path override - Added --disable-async-dns configure option to disable c-ares at build time - All async DNS code guarded by #ifdef HAVE_CARES; builds and runs correctly with or without c-ares installed; -Q is a safe no-op when not compiled in - c-ares initialised with ARES_OPT_TIMEOUTMS=500ms, ARES_OPT_TRIES=2 (1000ms worst-case per host); DNS results are a bonus and never block the trace -- unresolved hops display numerically as before - Updated lft.8 manpage to document -Q and async DNS behavior - Added -o ASCII art hop diagram output: horizontal per-hop columns with Unicode box-drawing headers (┌─ HOP N ─┐) with ASCII fallback ([- HOP N -]); ANSI color auto-detected via TERM/COLORTERM/isatty and suppressed by NO_COLOR; terminal-width-aware row wrapping; SOURCE and TARGET columns with port numbers - Column layout (top to bottom): header, hostname, IP address, AS number (-A), network name (-N), round-trip time, then feature annotation line - Feature labels use needlework and symbolic Unicode glyphs when the terminal supports them, with plain ASCII fallbacks for all annotations: seam boundaries, cloaked hops, firewall anomalies, and target state indicators each have a distinct glyph style in Unicode mode and a readable ASCII equivalent when Unicode is unavailable - Narrowed ICMP unreachable stop behavior: only codes 0 (net), 1 (host), 2 (protocol), 3 (port), 9 (net admin prohibited), 10 (host admin prohibited), and 13 (communication admin prohibited) halt the trace by default; all other codes (4, 5-8, 11-12, 14-15) are recorded but the trace continues; new lft_icmp_stops_trace() static inline helper in lft_lib.h shared between lft_lib.c and lft_btcptrace.c - Adaptive mode (-E/-e) now automatically implies --ignore-unreachables (break_on_icmp=0); the adaptive engine needs to probe through devices that send ICMP unreachables to perform stateful path analysis - Added getopt_long() replacing getopt(); every short flag now has a long-form equivalent (e.g. --dport, --min-probes, --max-hops, --ignore-unreachables, --adaptive, --ascii, --tcp-window); multiple aliases supported where traceroute convention warrants (--min-ttl / --first-hop, --max-hops / --ttl-max, --quiet / --silent, --length / --bytes, --device / --receive-device, --from-device / --source-device); all short flags remain fully backward-compatible - Added long-only --help option (OPT_HELP sentinel); displays usage summary and exits - Added -w (--tcp-window) to usage() help text under Advanced Tracing Options and to the manpage; the option existed and functioned but was undocumented in both places - Added "long-form options available, see lft(8)" note to --help output - Fixed manpage: -m and -M descriptions corrected from "re-attempts per host" to "probes per hop"; -M default corrected from 3 to 2 (matching actual code default); "min tll" typo corrected to "min ttl"; "Type of Serice" typo corrected to "Type of Service" - Updated manpage -i description to enumerate the specific ICMP codes that stop the trace by default and note that adaptive mode implies it - Updated manpage -E/e entry to note that adaptive mode implies --ignore-unreachables - Whois and getnameinfo DNS calls now protected by timeout wrappers: w_getaddrinfo_timed() (POSIX: alarm/SIGALRM; Windows: worker thread + WaitForSingleObject/TerminateThread) prevents whois server resolution from stalling the trace indefinitely; lft_getnameinfo_timed() applies the same Windows thread-based ceiling to getnameinfo() calls in the output path - configure now emits a deferred advisory warning (AC_MSG_WARN) when c-ares is not found, displayed at the bottom of configure output; libpcap AC_MSG_ERROR messages updated to consistent style with platform-specific install instructions including macOS Homebrew - Suppressed unresolved [AS?] and [NULL]/[NET?] annotations in -o ASCII art and GraphViz output to avoid cluttering columns where no data is available; regular output retains the annotation but normalises the label from [NULL] to [NET?] for consistency with [AS?] - Added startup privilege check: lft_check_permissions() probes for raw socket access (the same capability pcap requires) before executing the trace; if insufficient, exits immediately with platform-appropriate remediation guidance -- on Linux recommends setcap(8) with setuid-root as a last resort; on macOS/BSD instructs the setuid-root method with the resolved binary path ready to copy-paste; on Windows directs the user to run as Administrator with Npcap installed lft 3.94 / WhoB 3.94 ---------------------- - Fixed libpcap detection in configure: replaced deprecated pcap_lookupdev() (removed in libpcap 1.10) with pcap_findalldevs(), which is what the code actually calls at runtime via lft_pcap_lookupdev() - Added pkg-config support for libpcap detection, with fallback to manual detection for systems without pkg-config - Fixed memory leak in lft_pcap_lookupdev(): pcap_findalldevs() result list was never freed with pcap_freealldevs() - Updated make install to detect platform (Linux vs. others) and check for root privileges before granting capabilities: on Linux with root, uses setcap(8) to grant cap_net_raw,cap_net_admin without a setuid root binary; falls back to setuid root if setcap is unavailable; warns clearly if install is not run as root; on non-Linux platforms uses setuid root directly - Updated --enable-universal to target arm64 + x86_64 (Apple Silicon + Intel) replacing the obsolete i386 + ppc targets - Removed obsolete autoconf macros: AC_HEADER_STDC, AC_C_CONST, AC_PROG_GCC_TRADITIONAL, AC_FUNC_MALLOC, AC_FUNC_REALLOC, AC_FUNC_SELECT_ARGTYPES, AC_STRUCT_TM, AC_HEADER_TIME - Improved configure error messages for missing libpcap with platform-specific install instructions - Replaced pcap_open_live() with pcap_create()/pcap_set_snaplen()/ pcap_set_promisc()/pcap_set_timeout()/pcap_activate() for improved error reporting; activation failures now surface via pcap_geterr() with distinct fatal (ERR_PCAP_ACTIVATE_ERROR) and non-fatal (WRN_PCAP_ACTIVATE) paths - Improved raw socket permission error to display platform-appropriate hint for granting required privileges lft 3.93 / WhoB 3.93 ---------------------- - Updated lft_types.h to reflect some DARWIN (Apple) specifics lft 3.92 / WhoB 3.92 ---------------------- - Changed lft_types.h to reflect some OpenBSD specifics lft 3.91 / WhoB 3.91 ---------------------- - Fixed bugs.debian.org/cgi-bin/bugreport.cgi?bug=922430 - Updated date lft 3.9 / WhoB 3.9 ---------------------- - Fixed a bug that could indicate a target was closed when it was open - Improved support for DLT_NULL interfaces on BSD-like operating systems - Improved deprecated pcap device enumeration method - Improved GraphViz output format appearance lft 3.8 / WhoB 3.8 ---------------------- - Added support for DLT_NULL interfaces lft 3.79 / WhoB 3.79 ---------------------- - Improved feature: 'whob me' and 'whob whoami' to display your public IP lft 3.78 / WhoB 3.78 ---------------------- - Added feature: 'whob me' to display your current public IP address lft 3.77 / WhoB 3.77 ---------------------- - Added feature: 'whob -gA' to show all prefixes TRANSITING an ASN Very powerful, much-requested feature! Enjoy Many thanks to Prefix WhoIs team! lft 3.76 / WhoB 3.76 ---------------------- - Remove compiler warnings using more casts (Victor) - Autoremake ./configure script so everyone has a reasonable default lft 3.75 / WhoB 3.75 ---------------------- - Improved cross-compiling compatiblity (thanks to Gustavo Z.) lft 3.74 / WhoB 3.74 ---------------------- - Fixed source port randomizaiton with -z (thanks to Conor M.) - Fixed compilation on OpenBSD (thanks to Renaud A.) lft 3.71 / WhoB 3.71 ---------------------- - WhoB: Autodetect input from STDIN (pipe) without '-f -' - WhoB: Redirect some extraneous output to STDERR lft 3.7 / WhoB 3.7 ---------------------- - Added support for 4-byte ASNs - Added support for whob reading bulk input from stdin using '-f -' lft 3.6 / WhoB 3.6 ---------------------- - Added support for 4-byte ASNs lft 3.5 / WhoB 3.5 ---------------------- - Roy T. provided DNS speed-ups - Added GraphViz output option with -g lft 3.35 / WhoB 3.5 ---------------------- - Roy T. provided some clean-ups to avoid double free()s - Bug fixes only lft 3.33 / WhoB 3.5 ---------------------- - Fixed free(sess->hostname) bug (segfault on unresolvable hostname) - Improved error hanlding of pcap failures related to data link type - Kurt's FreeBSD fix for pcap snprintf - Bug fixes only lft 3.32 / WhoB 3.5 ---------------------- - Added support for several encapsulating protocols such as PPP - no other changes lft 3.31 / WhoB 3.5 ---------------------- - Added #define for AI_NUMERICSERV undeclared on some platforms/versions - No other changes lft 3.3 / WhoB 3.5 ---------------------- - Improved LFT target detection behind firewalls - Updated LFT for newer pcap version defaults - WhoB ignores comments [#,;] in input files - Added LFT option -f to specify fake source address - Applied many, many, many fixes from Juli M. of USA -- Thanks! - Applied many, many, many fixes from Markus Gothe of Sweden -- Thanks! lft 3.2 / WhoB 3.2 ---------------------- - Added support for 802.1q tagged VLANs - Manual page corrections (thanks to Brett) - WhoB will use Prefix WhoIs for bulk file resolution in Cymru-compatible format with '-c' but Cymru will be used with '-C' lft 3.1 / WhoB 3.1 ---------------------- - New configure options: --enable-gtod Forces LFT to use gettimeofday() on each packet instead of using the BPF timestamp. This is critical on platforms that have enabled 'fastts' or that do not have high-precision BPF timestamping. --enable-universal generates binaries including both PPC and Intel architecture (for users running Mac OS X/Darwin) - Improved compatibility with NetBSD and Darwin/Mac OS X - Added autoconf support for NetBSD - Improved compatibility with older compilers (thanks to Sean) - Updated autoconf bits and pieces - By popular request, reversed the -g option of WhoB WhoB now uses gigo mode by default unless -g is specified which turns ON its parser and enables the other various options lft 3.0 ---------------------- - Completely refactored and now a workable library - LFT has a new (-b) TCP Basic trace method that makes TCP traces NAT-friendly - LFT has a new (-p) ICMP trace method that uses echo requests to trace paths - Many memory issues fixed - Cleanup of several 2.6b5-3.0b features lft 2.6 / WhoB 2.0 ---------------------- - LFT prints 'open' in the target block if the target dest port is open - LFT indicates the reason it marked ports open/closed in verbose(2) output - LFT prints an asterisk when it retransmits a packet after a timeout - LFT uses Prefix WhoIs (bulk) or RISWHOIS (bulk) for netname resolution - LFT has a new (-u) traditional UDP-based tracing feature - LFT displays start and finish times/dates when using the (-T) option - LFT has a new (-U) feature to display all times in UTC/GMT0 - LFT only shows time spent tracing/resolving at verbosity level 1 or higher - LFT sets the ToS bit on outgoing IP datagrams when (-I) option is used - LFT gets timevals from packets instead of calling gettimeofday() - LFT won't be fooled into thinking there's a firewall on a gateway just because adaptive mode ups the state waiting for replies that never come - Improved LFT performance (removed gettimeofday() on each packet) - WhoB/library uses Prefix WhoIs to resolve OrgNames and NetNames - WhoB/library has improved support for RIPE NCC RIS and Prefix WhoIs - WhoB supports bulk resolution (-f option) from an input file - WhoB also supports one-per-line output (-cf option) from a bulk input file - WhoB will use putenv() on Solaris who is missing setenv() to set TZ - WhoB has a new (-g) feature to take input directly from the command line and print output directly from Prefix WhoIs (referred to as GIGO) - Added $DESTDIR support to Makefile (thanks Daniel) - Fixed an off-by-one bug in LFT related to ASN display encountered when a trace contains one or more neglected TTLs - Fixed an off-by-one bug in LFT's verbose output regarding TTLs - Fixed variable ordering in whois.c from 2.6b1 (thanks Erick) - Numerous platform-specific improvements - Reconfigured autoconf and segregated ./config/ - Updated autoconf components to v2.59 lft 2.5 / WhoB 1.5 ---------------------- - Inclusive of betas 2.32 to 2.4x - Added -z option to pseudo-randomize source port - Added behavior to automatically select the most appropriate interface based on routing (this was on the most wanted list) - Improved OpenBSD compatibility (IP length nonzero) - OpenBSD is now detected by autoconf (for configuring the above) - Darwin is now detected by autoconf and its definition disables some BSD features to make it compatible with MacOS X and Darwin - LFT now indicates it has reached the target by printing a 'T' character in the status display (if status is enabled) - Cleanups were made to the verbose output levels (-VVV) - Significantly revamped whois framework makes it easy to include whois functionality into other programs - Added -C and -R and -r options to force alternate ASN sources: - (r)IPE RIS - special thanks to Rene Wilhelm @ RIPE NCC - (C)ymru - (R)ADB - Default ASN source (-A) is now Prefix WhoIs (see pwhois.org) - LFT now queries for ASNs in bulk format after completing a trace if pwhois (default), RIPE NCC RIS, or Cymru is selected - Added dst/src port autoselection based on user-supplied hostname - Vastly improved standalone whois client "whob" see whob.8 (whob manpage) - Makefile now installs 'whob' no-frills whois client (try ./whob) - "Smart" mode is now referred to as "Adaptive" mode (-E) lft 2.31 -------------------- - Fixed time precision on FreeBSD 5.3 (Thanks to Kurt Jaeger) lft 2.3: -------------------- - added WSAIoctl() call to select proper IF on windows based on dest (thanks Graham!) - lowered max_retries to a default of 2 - cleaned up formatting related to -S option - cleaned up verbose output to be more friendly - cleaned up error messages - updated manpage - updated spec file lft 2.2: -------------------- - removed dependence on regex library - removed dependence on INT_MAX - fixed whois code to get the most specific netblock instead of the least specific netblock - can now specify interface by IP instead of by name lft 2.1: -------------------- - Added autoconf support - Ported to cygwin - Fixed the Solaris and BSD makefiles (manpage install errors) - Modified the whois code to resolve ASN and netname lookup problems with RIPE entries. (-A) and (-N) code... - Modified the whois code to support local-as - Vastly streamlined the (-V) verbose output to cover the important information (sequence, etc) of all packets sent and received lft 2.0 (and betas): -------------------- - (-E) and (-F) shouldn't be used at the same time, now LFT knows that - Implemented fixes that enable LFT to run under Solaris/SPARC (Jim McKim) - Changed name from FFT to LFT (layer four trace) - Added ICMP messages for codes 13-15 - Changed the (-P) option added in 1.99 to (-E) in order to force use of the new "smart" engine - Enhanced stateful firewall (packet filter) detection en route - Engine: "smart" mode now tries FIN, SYN, etc table to get packets through which dramatically improves its "find a way" capability - Engine: "smart" mode now detects the BSD bug properly - Engine: "smart" mode now detects firewalls in transit - Compilation: made lft_queue.h the default for all OSs - UI: lots of user interface changes lft 1.99-beta(s): -------------------- - Added Loose Source Routing feature (LSRR) [ <...>] - Added (-N) Netblock name lookup feature - Added (-A) ASN lookup feature - Added (-P) option to automatically send 3 probes for more stats - Added (-V) lots of verbose debugging information - Added (-F) feature to revert to sending FIN packets - Added (-T) option to show trace and resolution timings (LFT is fast) - Added BSD 4.[23] and derivative (bug) inappropriate TTL detection - Engine: Use sequence numbers as packet ID instead of destination port numbers. This achieves a number of goals: Return ICMP and TCP packets are more uniquely identified. It is now possible to check the route for specific destination port, which is important if route varies based on port (such as with transparent proxying using a load balancing switch on port 80) (thank you Ugen!) - Engine: Send SYN packets. Variety of firewalls and NAT devices won't pass through a single TCP packet that is not a part of established connection, unless it carries SYN flag only (initial packet). Pitfall: this may create useless PCB blocks on destination host, if it does not use SYN cookies or some other SYN flood protection. However it's better then not being able to use lft from behind firewalls. (thank you Ugen!) - Renamed (-H) max hops/ttl option - UI cleaned up in several areas, especially result list & RTT display - Detect local packet filter lft 1.91: --------------------- - Fixed Makefile.solaris (last time we'll see this before autoconf) - added for Solaris lft 1.9: -------------------- - Default source port is now 53/tcp (dns-xfer) - Configurable source port on command line through -p or host:port - Configurable initial destination port on command line using -d - Source and Destination ports use /etc/services for lookup - Fixed a bug that created a gethostbyaddr() call even when the user specified NOT to do reverse DNS lookups -- makes -n option way faster! - Changed the way lft auto-detects the IP address of the interface selected * now, we determine what address is assigned to the interface instead of just getting the hosts's primary/default address and using it * thanks, Aaron, for bringing this to our attention! * thanks, Lane, for coming up with a platform-independent way of making it work. lft 1.8: -------------------- - added option to suppress status indicator and only show trace (or errors) * people using lft from a web page wanted this - added option to select the network interface used as hop zero * string length of device is tested to avoid potential buffer overflows - modified the status bar to display: Send mode: ->( / ) Recv mode: <{ } Additional testing platforms for this release: - Aaron Bentley (Univ Toronto) tested lft successfully under Debian Woody Linux * Aaron also contributed a variation of net device selection -- thanks lft 1.7: -------------------- - added option to disable reverse DNS host lookups - made several areas slightly more user-friendly - modified the status bar to display: Send mode: ->( / ) Recv mode: <( ) - changed "source" port from tcp/80 to tcp/25 most firewalls today permit tcp_established implicitly, not traffic from tcp/80 explicitly, therefore we figured sending packets from tcp/25 would be more likely to get through. - modified initialization of 32-bit unsigned integer used to calculate checksum LFT now builds again under Solaris, but still miscalculates checksums - fixed round trip time calculation - several other minor tweaks - tested under RedHat Linux, Darwin 5.5 (MacOS X v10.1) lft 1.6 (nils): --------------- - Great idea. lft-3.98/config/000755 000765 000024 00000000000 15174131662 013441 5ustar00vicstaff000000 000000 lft-3.98/lft.c000644 000765 000024 00000065322 15174131552 013133 0ustar00vicstaff000000 000000 /* This file is part of LFT. The LFT software provided in this Distribution is Copyright 2007 VOSTROM Holdings, Inc. The full text of our legal notices is contained in the file called COPYING, included with this Distribution. Authors: - Victor Oppleman - Eugene Antsilevitch Other copyrights and former authors: - Portions copyright (c) Genuity, Inc. - Portions copyright (c) Markus Gothe - Portions copyright (c) Nils McCarthy */ #include "lft_lib.h" #ifdef HAVE_NCURSES #include #include "lft_watch.h" #endif /* DNS resolver tuning: short timeout/retry so failed lookups don't stall traces. * _res is the global resolver state; on Linux/glibc it is consulted by * getnameinfo() (which LFT now uses in place of gethostbyname/gethostbyaddr). * On macOS, getnameinfo() may route through mDNSResponder which ignores _res, * so lft_lib.c wraps getnameinfo() calls in alarm(1) as a hard ceiling. * res_ninit() with a local state would not affect getnameinfo(), so direct * _res modification remains correct for the Linux/glibc path. * _res is declared in . */ #include #include #if 0 static char def_payload[] = "\0\0\0\0\0\0\0\0\0\0"; /* default payload for UDP packets */ #endif const char *version = "3.98"; /* set version string */ static const char *version_date = "(04/2026)"; /* date of this version */ /* Sentinel value for the long-only --help option. Must not collide with any * printable ASCII character used as a short option. */ #define OPT_HELP 1 #define OPT_NO_ASCII_CHART 2 #define OPT_NO_LEARN 3 /* LFT-AUTOTUNE — disable topology inheritance */ #define OPT_LEARN_MARGIN 4 /* LFT-AUTOTUNE — TTL margin above learned path */ static struct option long_options[] = { {"sport", required_argument, 0, 's'}, {"dport", required_argument, 0, 'd'}, {"random-sport", no_argument, 0, 'z'}, {"min-probes", required_argument, 0, 'm'}, {"max-probes", required_argument, 0, 'M'}, {"device", required_argument, 0, 'D'}, {"receive-device", required_argument, 0, 'D'}, {"length", required_argument, 0, 'L'}, {"bytes", required_argument, 0, 'L'}, {"fin", no_argument, 0, 'F'}, {"from-device", required_argument, 0, 'f'}, {"source-device", required_argument, 0, 'f'}, {"adaptive", no_argument, 0, 'E'}, {"udp", no_argument, 0, 'u'}, {"basic-tcp", no_argument, 0, 'b'}, {"icmp", no_argument, 0, 'p'}, {"ahead", required_argument, 0, 'a'}, {"scatter", required_argument, 0, 'c'}, {"timeout", required_argument, 0, 't'}, {"min-ttl", required_argument, 0, 'l'}, {"first-hop", required_argument, 0, 'l'}, {"max-hops", required_argument, 0, 'H'}, {"ttl-max", required_argument, 0, 'H'}, {"isn", required_argument, 0, 'q'}, {"minimize-delay", no_argument, 0, 'I'}, {"ignore-unreachables", no_argument, 0, 'i'}, {"tcp-window", required_argument, 0, 'w'}, {"numeric", no_argument, 0, 'n'}, {"hostnames-only", no_argument, 0, 'h'}, {"netname", no_argument, 0, 'N'}, {"asn", no_argument, 0, 'A'}, {"no-async-dns", no_argument, 0, 'Q'}, {"ripe-riswhois", no_argument, 0, 'r'}, {"radb", no_argument, 0, 'R'}, {"cymru", no_argument, 0, 'C'}, {"time-keeping", no_argument, 0, 'T'}, {"time-keeping-utc", no_argument, 0, 'U'}, {"quiet", no_argument, 0, 'S'}, {"silent", no_argument, 0, 'S'}, {"verbose", no_argument, 0, 'V'}, {"xml", no_argument, 0, 'x'}, {"graphviz", no_argument, 0, 'g'}, {"graphviz-icons", required_argument, 0, 'G'}, {"ascii", no_argument, 0, 'o'}, {"verify-seam", no_argument, 0, 'y'}, {"version", no_argument, 0, 'v'}, {"mtu", no_argument, 0, 'K'}, {"path-mtu", no_argument, 0, 'K'}, {"stats", required_argument, 0, 'j'}, {"no-async-chart", no_argument, 0, OPT_NO_ASCII_CHART}, {"no-ascii-chart", no_argument, 0, OPT_NO_ASCII_CHART}, {"watch", optional_argument, 0, 'W'}, {"continuous", optional_argument, 0, 'W'}, /* LFT-AUTOTUNE — see lft_lib.c lft_auto_tune_from_learned() */ {"no-learn", no_argument, 0, OPT_NO_LEARN}, {"learn-margin", required_argument, 0, OPT_LEARN_MARGIN}, {"help", no_argument, 0, OPT_HELP}, {0, 0, 0, 0} }; static void usage(lft_session_params * sess, char *prog) { fprintf (stderr, "\nLayer Four Traceroute (LFT)\n\n" " - the alternative traceroute tool for network [reverse] engineers\n" " visit http://www.pwhois.org\n\n" "Usage: %s [] [ <...>] \n" "\nMainstream Options:\n" " -s Set source port (default: random dynamic port in 49152-65535)\n" " -d Destination port number, same as target:port (default: 443)\n" " -z Pseudo-randomize the source port number\n" " -m Minimum number of probes to send per hop\n" " -M Maximum number of probes to send per hop\n" " -D Listen-on/use a device by name or address (\"en1\" or \"1.2.3.4\")\n" " -L Set the length of the probe packet in bytes (ignored when -K is active)\n" " -K Active path MTU discovery: probe at interface MTU, reduce on PTB\n" " -j Collect N probes/hop for RTT stats (min/avg/max/jitter); use with -o for chart\n" " --no-ascii-chart Suppress the candlestick chart from -j output (keep numerical stats)\n" " -W N Continuous monitoring mode (ncurses); N=interval seconds\n" " Also: --watch=N / --continuous=N (e.g. --watch 5)\n" #ifndef HAVE_NCURSES " (unavailable — rebuild with --with-ncurses)\n" #endif " --no-learn Disable topology inheritance — every --watch cycle\n" " probes the full TTL ladder. Default: enabled (cycle 2+\n" " reuses prior path length, RTT envelope, and tighter\n" " timeouts after 3 stable cycles)\n" " --learn-margin N TTL probe margin above the learned path length\n" " (default 2; clamped 0..10)\n" "\nAdvanced Tracing Options:\n" " -F Use TCP FIN packets exclusively (defaults are SYN)\n" " -f Send using a device by name or address (spoofing allowed)\n" " -e | -E Use LFT's stateful engine to detect firewalls and path anomalies\n" " -u Use traditional UDP (probes) for tracing instead of TCP\n" " -b Basic TCP trace\n" " -p Use traditional ICMP (probes) for tracing instead of TCP\n" /*" -P Use RFC1393 IP option for tracing instead of TCP\n" */ " -a Number of hops forward to query before pausing to wait for replies\n" " -c Minimum number of milliseconds between subsequent probes\n" " -t Maximum RTT to wait before assuming packet was dropped\n" " -l Minimum TTL to use on outgoing packets (skips close-proximity hops)\n" " -H Maximum number of hops to traverse (max TTL of packets)\n" " -q Set the initial sequence number (ISN) manually\n" " -w Set the TCP window size in bytes (TCP traces only; default: 32768)\n" " -I Set the ToS field in the IP packet to minimize-delay\n" " -i Ignore ICMP unreachable messages; trace continues through firewalls\n" " -y Test destination upstream transition hop\n" "\nResolution Options:\n" " -n Display hosts numerically; disable use of the DNS resolver\n" " -h Display hosts symbolically; suppress IP address display\n" " -N Display network or AS names where appropriate\n" " -A Display AS numbers resolved by Prefix WhoIs\n" "\nAdvanced Resolution Options:\n" " -Q Disable asynchronous DNS" #ifdef HAVE_CARES "; fall back to synchronous resolver\n" #else " (not compiled in; flag accepted but has no effect)\n" #endif " -r Use RIPE NCC's RIS to resolve ASNs instead of Prefix WhoIs\n" " -R Use the RADB to resolve ASNs instead of Prefix WhoIs\n" " -C Use Cymru to resolve ASNs instead of Prefix WhoIs\n" "\nVerbosity Options and Status:\n" " -T Use execution timers and summarize where LFT spent its time\n" " -U Display all times in UTC (GMT0). Activates -T option automatically\n" " -S Disable the status bar (only show the completed trace)\n" " -v Display LFT's version information and exit\n" "\nOutput Options:\n" " -V Display verbose/debug output. Use more \'V\'s for additional detail\n" " -W Watch mode: ncurses textual user interface. Several dashboards.\n" " -x XML output\n" " -g GraphViz output\n" " -G Enable GraphViz output and override path to icons\n" " -o ASCII art hop diagram (color and Unicode auto-detected)\n" " --help Show this summary; long-form options available, see lft(8)\n" "\n" "Default is: %s -s %d -d %d -m %d -M %d -a %d -c %.0f -t %d -H %d \n\n", prog, prog, sess->sport, sess->dport, sess->retry_min, sess->retry_max, sess->ahead_limit, sess->scatter_ms, sess->timeout_ms, sess->ttl_limit); exit(EXIT_FAILURE); } static void show_version (void) { /* Compile-time options block — one big fprintf so #ifdefs can gate strings */ fprintf (stderr, "\n" "Layer Four Traceroute (LFT) - version %s %s\n\n" " - the alternative traceroute tool for network [reverse] engineers\n\n" " Compile-time options:\n\n" #if defined(DARWIN) || defined(__APPLE__) " + MacOS (or Darwin)\n" #endif #if defined(UNIVERSAL) " + Universal binary\n" #endif #if defined(NETBSD) " + NetBSD\n" #endif #if defined(OPENBSD) " + OpenBSD\n" #endif #if defined(BSD_IP_STACK) " + BSD IP stack\n" #endif #if defined(BSD) " + BSD platform\n" #endif #if defined(linux) " + Linux platform\n" #endif #if defined(sun) " + SUN platform\n" #endif #if !defined(sun) && !defined(linux) && !defined(BSD_IP_STACK) && !defined(OPENBSD) " (unknown architecture)\n" #endif #if defined(SCREWED_IP_LEN) " + IP length big-endian\n" #else " + IP length little-endian\n" #endif #if defined(IP_HDRINCL) " + Full IP headers for raw sockets\n" #else " + Without IP header inclusion\n" #endif #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) || defined( USE_GTOD ) " + Calling gettimeofday() on each packet\n" #endif " + " HOST_SYSTEM_TYPE "\n" #ifdef LFT_DONT_USE_SAFE_UID " + Privilege drop disabled (LFT_DONT_USE_SAFE_UID)\n" #endif "\n Capabilities:\n\n", version, version_date); /* Capabilities — runtime version strings require separate fprintf calls */ fprintf(stderr, " + Packet capture: %s\n", pcap_lib_version()); #ifdef HAVE_CARES fprintf(stderr, " + c-ares async DNS: c-ares %s\n", ares_version(NULL)); #else fprintf(stderr, " - c-ares async DNS: unavailable\n"); #endif #ifdef HAVE_NCURSES fprintf(stderr, " + ncurses TUI: %s\n", curses_version()); #else fprintf(stderr, " - ncurses TUI: unavailable\n"); #endif fprintf(stderr, "\n"); exit(EXIT_SUCCESS); } static void lft_check_permissions(const char *binpath) { /* Fast path: effective root covers both direct root and setuid-root. */ if (geteuid() == 0) return; #if defined(__CYGWIN__) || defined(WIN32) || defined(_WIN32) /* On Windows/Cygwin, raw socket access requires Administrator privileges * and the Npcap packet capture service must be installed and running. */ (void)binpath; fprintf(stderr, "\nLFT: insufficient privileges.\n\n" " On Windows, LFT must be run as Administrator, and the Npcap\n" " packet capture service must be installed and running.\n" " Right-click the terminal and choose \"Run as administrator\",\n" " or launch from an elevated command prompt.\n\n"); exit(EXIT_FAILURE); #else /* Try to open a raw socket — the same privilege that pcap will demand. */ int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock >= 0) { close(sock); return; /* capabilities or other mechanism is granting access */ } if (errno != EPERM && errno != EACCES) return; /* unexpected error; let the real code surface it */ /* No permission. Emit platform-appropriate guidance. */ #if defined(DARWIN) || defined(__APPLE__) (void)binpath; /* suppress unused-variable warning on this path if needed */ fprintf(stderr, "\nLFT: insufficient privileges — raw socket and BPF access are required.\n\n" " LFT is not running as root. macOS does not support Linux-style\n" " capability grants (setcap); the setuid-root method is required:\n\n" " sudo chown root %s && sudo chmod u+s %s\n\n", binpath, binpath); #elif defined(linux) || defined(__linux__) fprintf(stderr, "\nLFT: insufficient privileges — raw socket access is required.\n\n" " LFT does not appear to have the necessary Linux capabilities\n" " (cap_net_raw, cap_net_admin).\n\n" " Recommended fix using Linux capabilities (no setuid-root needed):\n\n" " sudo setcap 'cap_net_raw,cap_net_admin=eip' %s\n\n" " To verify: getcap %s\n" " To remove: sudo setcap -r %s\n\n" " As a last resort, the traditional setuid-root method also works:\n\n" " sudo chown root %s && sudo chmod u+s %s\n\n", binpath, binpath, binpath, binpath, binpath); #elif defined(BSD_IP_STACK) fprintf(stderr, "\nLFT: insufficient privileges — raw socket access is required.\n\n" " LFT is not running as root. Use the setuid-root method:\n\n" " sudo chown root %s && sudo chmod u+s %s\n\n", binpath, binpath); #else fprintf(stderr, "\nLFT: insufficient privileges — raw socket access is required.\n\n" " LFT is not running as root. You may need to run it as root\n" " or set the setuid-root bit on the binary:\n\n" " sudo chown root %s && sudo chmod u+s %s\n\n", binpath, binpath); #endif exit(EXIT_FAILURE); #endif /* !Windows */ } extern int main (int argc, char **argv) { lft_session_params * sess; int ch; int long_index = 0; char *cp = NULL; struct timeval tb; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD( 2, 2 ); WSAStartup( wVersionRequested, &wsaData ); #endif res_init(); _res.retrans = 1; /* 1s timeout per query attempt */ _res.retry = 1; /* 1 retry; default is 4 which causes 20s+ stalls */ sess = LFTSessionOpen(); setbuf(stdout, NULL); gettimeofday (&tb, NULL); /* eventually this might want to use /dev/urandom or * something on machines that have it. otherwise, * this does a fairly decent job of using the system * clock. * * multiply tv_usec (range 0-1000000) to be in range 0-2^31, * and xor to randomize the high bits of tv_sec that don't * change very much. */ srand(tb.tv_sec ^ (tb.tv_usec * 2147)); int user_set_retry_min = 0, user_set_retry_max = 0; while ((ch = getopt_long(argc, argv, "Aa:bCc:D:d:EeFf:H:hIij:KL:l:M:m:NnoPpq:QRrSs:Tt:UuVvW::xw:zgG:y", long_options, &long_index)) != EOF) switch (ch) { case 'f': LFTSetupSendDevice(sess, optarg); break; case 'F': LFTSetupFIN(sess); break; case 'h': LFTSetupDispSymbHost(sess); break; case 'u': LFTSetupUDPMode(sess); break; case 'r': LFTSetupRISLookup(sess); break; case 'R': LFTSetupRADBLookup(sess); break; case 'C': LFTSetupCYMRULookup(sess); break; case 'd': LFTSetupDestinationPort(sess, optarg); break; case 'L': LFTSetupLengthOfPacket(sess, (int)strtol(optarg, (char **)NULL, 10)); break; case 'K': LFTSetupPMTUD(sess); break; case 'q': sess->seq_start = strtol(optarg, (char **)NULL, 10); break; case 'w': sess->win_len = (int)strtol(optarg, (char **)NULL, 10); break; case 'm': sess->retry_min = (int)strtol(optarg, (char **)NULL, 10); user_set_retry_min = 1; break; case 'M': sess->retry_max = (int)strtol(optarg, (char **)NULL, 10); user_set_retry_max = 1; break; case 'j': { int n = (int)strtol(optarg, (char **)NULL, 10); LFTSetupRTTStats(sess, n); break; } case OPT_NO_ASCII_CHART: sess->no_ascii_chart = 1; break; /* LFT-AUTOTUNE — runtime toggles for topology inheritance */ case OPT_NO_LEARN: sess->no_autotune = 1; break; case OPT_LEARN_MARGIN: { int m = (int)strtol(optarg, (char **)NULL, 10); if (m < 0) m = 0; if (m > 10) m = 10; sess->learn_margin = m; break; } case 'N': sess->do_netlookup = 1; break; case 'A': sess->do_aslookup = 1; break; case 'n': LFTSetupDisableResolver(sess); break; case 'T': sess->timetrace = 1; break; case 's': LFTSetupSourcePort(sess, lft_resolve_port(sess,optarg)); break; case 'E': case 'e': LFTSetupAdaptiveMode(sess); break; case 'S': sess->nostatus = 1; break; case 'D': LFTSetupDevice(sess,optarg); break; case 'a': sess->ahead_limit = (int)strtol(optarg, (char **)NULL, 10); break; case 'c': sess->scatter_ms = (int)strtol(optarg, (char **)NULL, 10); if (sess->scatter_ms < 1) sess->scatter_ms = 1; if (sess->scatter_ms > 100) sess->scatter_ms = 100; break; case 't': sess->timeout_ms = (int)strtol(optarg, (char **)NULL, 10); break; case 'p': sess->protocol = 2; break; /*case 'P': //We comment this option. Hardly any routers support this. sess->protocol = 3; break;*/ case 'b': sess->protocol = 4; break; case 'H': if (strtol(optarg, (char **)NULL, 10) > 255) sess->ttl_limit = 255; else sess->ttl_limit = (int)strtol(optarg, (char **)NULL, 10); break; case 'l': sess->ttl_min = (int)strtol(optarg, (char **)NULL, 10); sess->hop_info_length = sess->ttl_min; if (sess->ttl_min > 0) sess->ttl_min--; break; case 'i': sess->break_on_icmp = 0; break; case 'I': sess->set_tos = 1; break; case 'v': show_version(); break; case 'U': /* show all times in UTC */ LFTSetupUTCTimes(sess); break; case 'V': sess->noisy++; sess->nostatus = 1; break; case 'z': sess->random_source = 1; /* Yes, this is a ridiculous randomizer, but it's adequate */ sess->sport = rand()%16384+49152; break; case 'x': setOutputStyle(1); break; case 'g': setOutputStyle(2); break; case 'G': setOutputStyle(2); sess->graphviz_icon_path=optarg; break; case 'o': setOutputStyle(3); break; case 'y': sess->check_seam=1; break; case 'Q': #ifdef HAVE_CARES sess->async_dns_disabled = 1; #else fprintf(stderr, "LFT: -Q has no effect: c-ares was not compiled in\n"); #endif break; case 'W': { int n = optarg ? (int)strtol(optarg, (char **)NULL, 10) : 0; if (n <= 0) n = 5; /* default interval: 5 seconds */ #ifdef HAVE_NCURSES sess->watch_mode = 1; sess->watch_interval = n; setOutputStyle(4); #else fprintf(stderr, "\nLFT: Support for nCurses was not enabled at compile-time.\n" " Please re-run ./configure and rebuild with --with-ncurses\n" " if ncurses is not found automatically by autoconf.\n\n"); exit(EXIT_FAILURE); #endif break; } case OPT_HELP: usage(sess, argv[0]); break; default: usage(sess, argv[0]); } /* rtt_stats: apply probe count to retry_min/retry_max (unless user explicitly set them) */ if (sess->rtt_stats) { if (!user_set_retry_min) sess->retry_min = sess->rtt_stats; if (!user_set_retry_max) sess->retry_max = sess->rtt_stats; if (sess->retry_max < sess->retry_min) sess->retry_max = sess->retry_min; /* auto ahead_limit = 2 × retry_min so the pipeline keeps flowing without * excessive target overrun while collecting multi-probe RTT samples */ sess->ahead_limit = sess->retry_min * 2; } if((argc - optind) < 1) usage(sess, argv[0]); if (sess->noisy && !outputStyleIsXML() && !outputStyleIsGraphViz() && !outputStyleIsASCII()) printf ("Layer Four Traceroute (LFT) version %s", version); if (sess->noisy > 1 && !outputStyleIsXML() && !outputStyleIsGraphViz() && !outputStyleIsASCII()) printf (" ... (verbosity level %d)",sess->noisy); if (sess->noisy && !outputStyleIsXML() && !outputStyleIsGraphViz() && !outputStyleIsASCII()) printf ("\n"); if(outputStyleIsXML()) { printf("\n",sess->noisy); } sess->hostname = argv[optind++]; sess->hostname_lsrr_size = 0; while (optind < argc) { sess->hostname_lsrr[sess->hostname_lsrr_size++] = argv[optind++]; if (sess->hostname_lsrr_size > IP_LSRR_DEST) { if(outputStyleIsXML()) { printf("Unknown host: Too many LSRR hosts - maximum is 8"); printf("\n"); } else { fprintf(stderr, "LFT: Too many LSRR hosts - maximum is 8\n"); } exit(EXIT_FAILURE); } } if (sess->hostname_lsrr_size > 0) { sess->hostname_lsrr[sess->hostname_lsrr_size++] = sess->hostname; sess->hostname = sess->hostname_lsrr[0]; } /* allow hostname:port syntax when -d not specified */ if ((cp = strchr(sess->hostname, ':'))) { if (!sess->dflag) { *cp++ = '\0'; sess->dport = lft_resolve_port(sess, cp); if (sess->dport > 65534) { if(outputStyleIsXML()) printf("Starting port %d is too high. Will use %d instead.", sess->dport, 65534); else fprintf(stderr, "LFT warning: Starting port %d is too high. Will use %d instead.\n", sess->dport, 65534); sess->dport = 65534; } sess->auto_ports = 0; } } { char *binpath = realpath(argv[0], NULL); lft_check_permissions(binpath ? binpath : argv[0]); if (binpath) free(binpath); } #ifdef HAVE_NCURSES if (sess->watch_mode) { WatchModeRun(sess); } else { #endif if (getOutputStyle() == 3) lft_o_spinner_start(sess); /* start live probe counter for -o */ LFTExecute(sess); #ifdef HAVE_NCURSES } #endif LFTSessionClose(sess); #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) WSACleanup(); #endif if(outputStyleIsXML()) printf("\n"); //getch(); return 0; } lft-3.98/include/000755 000765 000024 00000000000 15174131661 013616 5ustar00vicstaff000000 000000 lft-3.98/lft_watch.c000644 000765 000024 00000576333 15174131552 014332 0ustar00vicstaff000000 000000 /* * lft_watch.c -- ncurses continuous monitoring mode for LFT * * Compiled only when HAVE_NCURSES is defined (./configure detects ncurses * or the user provides --with-ncurses=PATH). * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. */ #include "config/acconfig.h" #ifdef HAVE_NCURSES #include "lft_watch.h" #include "lft_lib.h" #include "whois.h" /* ASCIIOutput is defined in lft_lib.c but not exposed in lft_lib.h */ void ASCIIOutput(lft_session_params *sess); #include #include #include #include #include #include #include #include #include #include #include /* struct winsize, TIOCGWINSZ — Linux glibc * does not pull these in transitively the way * macOS does, so include explicitly. */ #include #include /* Forward declarations for helper functions */ /* ========================================================================= * Internal helpers * ========================================================================= */ /* Braille spinner frames (Unicode, 8-frame cycle) */ static const char *SPINNER_FRAMES[WATCH_SPINNER_FRAMES] = { "\xe2\xa0\x8b", /* ⠋ */ "\xe2\xa0\x99", /* ⠙ */ "\xe2\xa0\xb9", /* ⠹ */ "\xe2\xa0\xb8", /* ⠸ */ "\xe2\xa0\xbc", /* ⠼ */ "\xe2\xa0\xb4", /* ⠴ */ "\xe2\xa0\xa6", /* ⠦ */ "\xe2\xa0\x87", /* ⠇ */ }; static const char *SPINNER_ASCII[WATCH_SPINNER_FRAMES] = { "-", "\\", "|", "/", "-", "\\", "|", "/" }; /* Block chars for the RTT history sparkline (Unicode) */ static const char *BLOCK_CHARS[] = { " ", /* 0: no data / timeout */ "\xe2\x96\x81", /* ▁ */ "\xe2\x96\x82", /* ▂ */ "\xe2\x96\x83", /* ▃ */ "\xe2\x96\x84", /* ▄ */ "\xe2\x96\x85", /* ▅ */ "\xe2\x96\x86", /* ▆ */ "\xe2\x96\x87", /* ▇ */ "\xe2\x96\x88", /* █ */ }; #define N_BLOCK_CHARS 9 /* ncurses color pair indices */ #define CP_HEADER 1 /* white on blue */ #define CP_NORMAL 2 /* default fg/bg */ #define CP_GREEN 3 /* green (good RTT) */ #define CP_YELLOW 4 /* yellow (mild) */ #define CP_ORANGE 5 /* magenta (moderate — no true orange in ANSI) */ #define CP_MAGENTA 5 /* alias: magenta for loss/cloaked markers */ #define CP_RED 6 /* red (high RTT / loss) */ #define CP_HIGHLIGHT 7 /* reverse video for flash */ #define CP_STATUS 8 /* cyan on black */ #define CP_DRAWER 9 /* white on black for log drawer */ #define CP_BLUE 10 /* blue bold for ↑ sent counter */ #define CP_LABEL 11 /* light steel blue (xterm-256 147) — column labels */ #define CP_TBLHEAD 12 /* alice blue (xterm-256 195) — timeline table headers */ #define CP_SENT 13 /* medium slate blue (xterm-256 75) — ↑ sent counter */ #define CP_KEYHINT 14 /* cornflower (xterm-256 111) — row 2 key-hint menu */ #define CP_KEYLTR 15 /* light sky blue (xterm-256 117) — bracketed letters */ /* RTT → color pair mapping (ms thresholds) — used for bar chart and host/avg */ static int rtt_color(double rtt_ms) { if (rtt_ms <= 0) return CP_RED; if (rtt_ms < 30) return CP_GREEN; if (rtt_ms < 80) return CP_YELLOW; if (rtt_ms < 150) return CP_ORANGE; return CP_RED; } /* AVG column — finer thresholds than rtt_color() */ static int avg_color(double avg_ms) { if (avg_ms < 20) return CP_GREEN; if (avg_ms < 100) return CP_NORMAL; if (avg_ms < 250) return CP_YELLOW; return CP_RED; } /* JITTER column */ static int jitter_color(double jitter_ms) { if (jitter_ms < 10) return CP_GREEN; if (jitter_ms < 30) return CP_YELLOW; return CP_RED; } /* HOST column — based on loss rate, not RTT */ static int host_color(double loss_pct) { if (loss_pct <= 0) return CP_GREEN; if (loss_pct < 10) return CP_YELLOW; return CP_RED; } /* Loss % → color pair */ static int loss_color(double loss_pct) { if (loss_pct <= 0) return CP_NORMAL; if (loss_pct < 5) return CP_YELLOW; if (loss_pct < 25) return CP_ORANGE; return CP_RED; } /* ========================================================================= * Signal handling for SIGWINCH (terminal resize) * ========================================================================= */ static volatile int watch_resize_pending = 0; /* Termination signal handlers — installed while ncurses is live so Ctrl-C * calls endwin() before dying, leaving the terminal in a clean state. */ static struct sigaction g_old_sa_sigint_watch; static struct sigaction g_old_sa_sigterm_watch; static struct sigaction g_old_sa_sighup_watch; static struct sigaction g_old_sa_sigquit_watch; static volatile sig_atomic_t g_watch_ncurses_active = 0; /* 1 = ncurses is live */ static int g_watch_atexit = 0; /* 1 = atexit registered */ static void watch_sigwinch(int sig) { (void)sig; watch_resize_pending = 1; } /* Termination signal handler: restore terminal then re-raise the signal. * endwin() is not strictly async-signal-safe per POSIX, but it is the * standard ncurses idiom (used by vim, htop, etc.) and safe in practice * on every platform LFT targets. Without it Ctrl-C leaves the terminal * in raw / cursor-hidden mode. */ static void watch_sig_fatal(int sig) { if (g_watch_ncurses_active) { endwin(); g_watch_ncurses_active = 0; } /* Restore original handlers before re-raising so the process receives * the default action (core-dump for SIGQUIT, terminate for the rest). */ sigaction(SIGINT, &g_old_sa_sigint_watch, NULL); sigaction(SIGTERM, &g_old_sa_sigterm_watch, NULL); sigaction(SIGHUP, &g_old_sa_sighup_watch, NULL); sigaction(SIGQUIT, &g_old_sa_sigquit_watch, NULL); raise(sig); } /* atexit safety-net: catches any exit() path that bypasses watch_teardown_ncurses */ static void watch_atexit(void) { if (g_watch_ncurses_active) { endwin(); g_watch_ncurses_active = 0; } } /* ========================================================================= * Spinner animation via SIGALRM * * During a blocking LFTExecute() call, no ncurses code runs so the spinner * freezes. We work around this by arming a repeating SIGALRM that writes * the next spinner frame directly to /dev/tty, bypassing both the ncurses * layer and the stdout→/dev/null redirect that is active during a trace. * * Only async-signal-safe operations are used in the handler (write + integer * arithmetic). The handler is installed with SA_RESTART so that select() * and recv() inside LFTExecute() restart transparently instead of failing * with EINTR. * ========================================================================= */ static volatile sig_atomic_t g_alarm_frame = 0; static watch_session_t *g_watch_ws = NULL; /* set in WatchModeRun for SIGALRM access */ static struct sigaction g_old_sa_alarm; static char g_alarm_cycle_text[80]; /* " tracing cycle N/∞ Xs " */ static int g_alarm_cycle_text_len = 0; /* newterm() state — ncurses is opened on /dev/tty so stdout redirect has no effect */ static SCREEN *g_ncurses_screen = NULL; static FILE *g_tty_in = NULL; static FILE *g_tty_out = NULL; /* Forward declaration — needed by watch_spinner_sigalrm before its definition */ static void watch_render_status_bar(watch_session_t *ws, int spinner_frame); /* Format integer with comma thousands separator into buf[size]. * Returns chars written (not including NUL). Stack-only, safe from signal handler. */ static int fmt_comma(char *buf, int size, int val) { char tmp[24]; int n, i, j, next_comma; if (size <= 1) { if (size == 1) buf[0] = '\0'; return 0; } n = snprintf(tmp, sizeof(tmp), "%d", val); next_comma = n % 3; if (next_comma == 0) next_comma = 3; j = 0; for (i = 0; i < n; i++) { if (i == next_comma && j < size - 1) { buf[j++] = ','; next_comma += 3; } if (j < size - 1) buf[j++] = tmp[i]; } if (j < size) buf[j] = '\0'; return j; } /* SIGALRM handler: updates the status bar via ncurses. * Because ncurses is initialised on /dev/tty (not stdout), the stdout→/dev/null * redirect active during LFTExecute does not affect ncurses output. We can * call ncurses functions and doupdate() directly from the signal handler. * Single-threaded app: no re-entrancy risk from ncurses. */ static void watch_spinner_sigalrm(int sig) { int frame; (void)sig; if (!g_watch_ws) return; frame = ((int)g_alarm_frame + 1) % WATCH_SPINNER_FRAMES; g_alarm_frame = (sig_atomic_t)frame; /* Update sticky-max / sticky-true fields; leave total_replies and * total_sent alone — watch_render_status_bar adds the current-cycle * live count on top of the accumulated baseline when in_trace=1. */ { int hops = lft_o_get_hops(); int tgt = lft_o_get_target(); (void)lft_o_consume_tfail(); /* consume flash flag even if unused */ if (g_watch_ws->max_hops_seen > hops) hops = g_watch_ws->max_hops_seen; if (g_watch_ws->target_ever_reached) tgt = 1; if (hops > g_watch_ws->max_hops_seen) g_watch_ws->max_hops_seen = hops; if (tgt) g_watch_ws->target_ever_reached = 1; } watch_render_status_bar(g_watch_ws, frame); wnoutrefresh(stdscr); doupdate(); } /* Arm the 250 ms SIGALRM spinner. */ static void watch_spinner_arm(void) { struct sigaction sa; struct itimerval itv; memset(&sa, 0, sizeof(sa)); sa.sa_handler = watch_spinner_sigalrm; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; /* restart select()/recv() on signal */ sigaction(SIGALRM, &sa, &g_old_sa_alarm); itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 250000; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 250000; setitimer(ITIMER_REAL, &itv, NULL); } /* Disarm the SIGALRM spinner and restore the previous handler. */ static void watch_spinner_disarm(void) { struct itimerval zero; memset(&zero, 0, sizeof(zero)); setitimer(ITIMER_REAL, &zero, NULL); sigaction(SIGALRM, &g_old_sa_alarm, NULL); } /* ========================================================================= * Log drawer ring buffer * ========================================================================= */ static void watch_log_append(watch_session_t *ws, const char *msg) { int msglen = (int)strlen(msg); int i; if (msglen <= 0) return; for (i = 0; i < msglen; i++) { if (ws->log_len < WATCH_LOG_BUF) { /* Space available: write at tail, grow length */ ws->log_buf[(ws->log_head + ws->log_len) % WATCH_LOG_BUF] = msg[i]; ws->log_len++; } else { /* Buffer full: write at head (overwrites oldest byte), advance head. * log_len stays at WATCH_LOG_BUF — the ring always stays full. */ ws->log_buf[ws->log_head] = msg[i]; ws->log_head = (ws->log_head + 1) % WATCH_LOG_BUF; } } } /* ========================================================================= * Stats accumulator * ========================================================================= */ static void watch_update_hop(watch_hop_t *h, const char *host, double rtt_ms, int asnumber, const char *netname) { int idx; if (host && host[0]) snprintf(h->host, sizeof(h->host), "%s", host); h->cycles_total++; if (rtt_ms > 0) { h->cycles_seen++; h->rtt_last = rtt_ms; if (h->cycles_seen == 1) { h->rtt_min = h->rtt_max = rtt_ms; h->rtt_sum = rtt_ms; } else { if (rtt_ms < h->rtt_min) h->rtt_min = rtt_ms; if (rtt_ms > h->rtt_max) h->rtt_max = rtt_ms; h->rtt_sum += rtt_ms; } h->rtt_avg = h->rtt_sum / h->cycles_seen; h->rtt_jitter = h->rtt_max - h->rtt_min; } /* Store in circular history */ idx = h->history_head; h->rtt_history[idx] = rtt_ms; /* 0 = no reply */ h->history_head = (h->history_head + 1) % WATCH_HISTORY_LEN; if (h->history_count < WATCH_HISTORY_LEN) h->history_count++; /* whois fields */ if (asnumber > 0) snprintf(h->asn, sizeof(h->asn), "AS%d", asnumber); if (netname && netname[0] && strcmp(netname, "NULL") != 0) snprintf(h->netname, sizeof(h->netname), "%.63s", netname); } /* ========================================================================= * Display address helper — IP or PTR hostname (F4) * ========================================================================= */ /* Return the display string for a hop: hostname if show_hostnames and * one is available; otherwise the IP address, or "*" for no reply. */ static const char *hop_display_addr(const watch_hop_t *h, int show_hostnames) { if (show_hostnames && h->hostname[0]) return h->hostname; return h->host[0] ? h->host : "*"; } /* ========================================================================= * Per-IP annotation cache — implementation * * FNV-1a hash over the address bytes (4 for IPv4, 16 for IPv6), masked to * WATCH_IP_CACHE_BUCKETS-1. Collision chains are singly-linked lists. * All memory is malloc'd and freed by watch_ip_cache_free(). * ========================================================================= */ /* FNV-1a hash over the relevant address bytes */ static uint32_t watch_ip_hash(int af, const watch_ipaddr_t *addr) { uint32_t h = 2166136261u; /* FNV offset basis */ int len = (af == AF_INET6) ? 16 : 4; int i; for (i = 0; i < len; i++) { h ^= (uint32_t)addr->raw[i]; h *= 16777619u; /* FNV prime */ } return h & (WATCH_IP_CACHE_BUCKETS - 1); } static int watch_ip_equal(int af, const watch_ipaddr_t *a, const watch_ipaddr_t *b) { int len = (af == AF_INET6) ? 16 : 4; return memcmp(a->raw, b->raw, (size_t)len) == 0; } /* Parse a dotted-decimal or colon-hex string into af+addr. * Returns 1 on success, 0 if the string is not a valid IP. */ static int watch_ip_parse(const char *s, int *af_out, watch_ipaddr_t *addr_out) { memset(addr_out, 0, sizeof(*addr_out)); if (inet_pton(AF_INET6, s, &addr_out->v6) == 1) { *af_out = AF_INET6; return 1; } if (inet_pton(AF_INET, s, &addr_out->v4) == 1) { *af_out = AF_INET; return 1; } return 0; } /* Look up an IP in the cache. Returns the record or NULL if absent. */ static watch_ip_record_t *watch_ip_cache_lookup(watch_ip_cache_t *c, int af, const watch_ipaddr_t *addr) { uint32_t bucket = watch_ip_hash(af, addr); watch_ip_record_t *r = c->buckets[bucket]; while (r) { if (r->af == af && watch_ip_equal(af, &r->addr, addr)) return r; r = r->next; } return NULL; } /* Look up or create a zeroed record for the given IP. * Returns NULL only on malloc failure (extremely rare). */ static watch_ip_record_t *watch_ip_cache_get(watch_ip_cache_t *c, int af, const watch_ipaddr_t *addr) { uint32_t bucket = watch_ip_hash(af, addr); watch_ip_record_t *r = c->buckets[bucket]; while (r) { if (r->af == af && watch_ip_equal(af, &r->addr, addr)) return r; r = r->next; } /* Not found — allocate a new zeroed record and prepend to chain */ r = (watch_ip_record_t *)calloc(1, sizeof(*r)); if (!r) return NULL; r->af = af; r->addr = *addr; r->next = c->buckets[bucket]; c->buckets[bucket] = r; c->count++; return r; } /* Free all records in the cache. The cache struct itself is embedded in * watch_session_t (stack-allocated) so we only free the heap nodes. */ static void watch_ip_cache_free(watch_ip_cache_t *c) { int i; for (i = 0; i < WATCH_IP_CACHE_BUCKETS; i++) { watch_ip_record_t *r = c->buckets[i]; while (r) { watch_ip_record_t *next = r->next; free(r); r = next; } c->buckets[i] = NULL; } c->count = 0; } /* Copy annotation fields from a cache record into a hop's display copies. * Only overwrites fields that the record has (non-empty string). */ static void watch_ip_cache_copy_to_hop(const watch_ip_record_t *rec, watch_hop_t *h) { if (rec->asn[0]) snprintf(h->asn, sizeof(h->asn), "%.31s", rec->asn); if (rec->netname[0]) snprintf(h->netname, sizeof(h->netname), "%.63s", rec->netname); if (rec->orgname[0]) snprintf(h->orgname, sizeof(h->orgname), "%.63s", rec->orgname); if (rec->hostname[0]) snprintf(h->hostname, sizeof(h->hostname), "%.255s", rec->hostname); } /* ========================================================================= * pWhoIs lookup helpers (F2) * ========================================================================= */ /* Query pWhoIs for a single hop and populate the session's ip_cache record. * The hop's display fields (asn/netname/orgname) are also updated from the * result. ws->watch_whois_wsess must already be initialised before calling. * The record's whois_queried flag is set to 1 before the network call so that * errors (server down, timeout) don't cause infinite re-tries. */ static void watch_whois_query_one(watch_session_t *ws, watch_hop_t *h) { int af; watch_ipaddr_t addr; watch_ip_record_t *rec; char asn_buf[32]; if (!watch_ip_parse(h->host, &af, &addr)) return; /* not a parseable IP — skip */ rec = watch_ip_cache_get(&ws->ip_cache, af, &addr); if (!rec) return; /* malloc failure */ /* Cache-hit short-circuit: skip the network lookup only if we already * have USEFUL data cached (at least one of asn/netname/orgname is * populated). If a previous attempt came back empty — transient * pWhoIs timeout, server hiccup, partial response — keep trying on * subsequent cycles so gaps eventually fill in. IANA pre-screen * below still runs first, so private IPs never touch pWhoIs. */ if (rec->whois_queried && (rec->asn[0] || rec->netname[0] || rec->orgname[0])) { watch_ip_cache_copy_to_hop(rec, h); return; } rec->whois_queried = 1; /* mark before I/O so a live query error * doesn't cause re-entry this same cycle */ /* -------------------------------------------------------------------- * IANA pre-screen: private / special-purpose addresses (RFC1918, CGNAT, * loopback, link-local, documentation, multicast, etc.) never have a * useful pWhoIs record. Asking pWhoIs for one is worse than useless — * the server sometimes closes the TCP connection after a nonsense * query, which then breaks the NEXT hop's query too. Detect them * locally and fill the display fields with the RFC tag + registry name * (e.g. NETNAME = ASN = "RFC1918", ORGNAME = "Private-Use"), then * return without touching pWhoIs. * -------------------------------------------------------------------- */ { char iana_rfc[32] = ""; char iana_name[64] = ""; int iana_hit = 0; if (af == AF_INET) iana_hit = lft_iana_v4_lookup(addr.v4, iana_rfc, sizeof(iana_rfc), iana_name, sizeof(iana_name)); else if (af == AF_INET6) iana_hit = lft_iana_v6_lookup(&addr.v6, iana_rfc, sizeof(iana_rfc), iana_name, sizeof(iana_name)); if (iana_hit) { if (iana_rfc[0]) { snprintf(rec->asn, sizeof(rec->asn), "%.31s", iana_rfc); snprintf(rec->netname, sizeof(rec->netname), "%.63s", iana_rfc); } if (iana_name[0]) snprintf(rec->orgname, sizeof(rec->orgname), "%.63s", iana_name); watch_ip_cache_copy_to_hop(rec, h); return; } } if (w_lookup_all_pwhois(ws->watch_whois_wsess, h->host) != 0) return; /* Store results in the cache record */ asn_buf[0] = '\0'; if (ws->watch_whois_wsess->consolidated_asn[0] && ws->watch_whois_wsess->consolidated_asn[0] != '?') { if (ws->watch_whois_wsess->consolidated_asn[0] >= '0' && ws->watch_whois_wsess->consolidated_asn[0] <= '9') snprintf(asn_buf, sizeof(asn_buf), "AS%s", ws->watch_whois_wsess->consolidated_asn); else snprintf(asn_buf, sizeof(asn_buf), "%s", ws->watch_whois_wsess->consolidated_asn); } if (asn_buf[0]) snprintf(rec->asn, sizeof(rec->asn), "%.31s", asn_buf); if (ws->watch_whois_wsess->consolidated_netname[0] && strcmp(ws->watch_whois_wsess->consolidated_netname, "NULL") != 0) snprintf(rec->netname, sizeof(rec->netname), "%.63s", ws->watch_whois_wsess->consolidated_netname); if (ws->watch_whois_wsess->consolidated_orgname[0] && strcmp(ws->watch_whois_wsess->consolidated_orgname, "NULL") != 0) snprintf(rec->orgname, sizeof(rec->orgname), "%.63s", ws->watch_whois_wsess->consolidated_orgname); /* Push results to the hop's display copies */ watch_ip_cache_copy_to_hop(rec, h); } /* Query pWhoIs for all current hops that haven't been queried yet. * Uses per-hop individual queries (not bulk) to avoid the pWhois response * parser's entity_id misalignment bug with sparse records (private/CGNAT IPs). * Initialises ws->watch_whois_wsess if needed. * Sets ws->whois_bulk_done = 1 when all current hops have been attempted. */ static void watch_whois_bulk_query(watch_session_t *ws) { int i; if (!ws->watch_whois_wsess) ws->watch_whois_wsess = w_init(); if (!ws->watch_whois_wsess) { ws->whois_bulk_done = 1; return; } for (i = 0; i < ws->n_hops; i++) { watch_hop_t *h = &ws->hops[i]; if (!h->host[0] || strcmp(h->host, "*") == 0) continue; watch_whois_query_one(ws, h); } ws->whois_bulk_done = 1; } /* Query pWhoIs for any hop that has an IP but no cache record yet. * Called after each cycle to pick up new hops discovered after the initial * startup query — the ip_cache prevents re-querying IPs already seen. */ static void watch_whois_update_new_hops(watch_session_t *ws) { int i; if (!ws->whois_enabled || !ws->watch_whois_wsess) return; for (i = 0; i < ws->n_hops; i++) { watch_hop_t *h = &ws->hops[i]; if (!h->host[0] || strcmp(h->host, "*") == 0) continue; watch_whois_query_one(ws, h); /* cache check + query + hop field sync */ } } /* ========================================================================= * Trace execution — sub-session per cycle * ========================================================================= */ /* After LFTExecute returns, LFT's dns_cache contains whatever c-ares managed * to resolve during the trace (async; may be incomplete if queries timed out). * Inject hostnames from the watch ip_cache for any IPs already resolved in a * previous cycle but missing/incomplete this cycle. This makes the watch * ip_cache the authoritative persistent DNS store and avoids relying on c-ares * finishing in time on every cycle. * * Called in watch_run_one_cycle() between LFTExecute() and watch_capture_from_sess(). */ static void watch_inject_cached_hostnames(watch_session_t *ws, lft_session_params *sess) { int b; for (b = 0; b < WATCH_IP_CACHE_BUCKETS; b++) { watch_ip_record_t *rec = ws->ip_cache.buckets[b]; while (rec) { if (rec->af == AF_INET && rec->hostname[0]) { struct in_addr a = rec->addr.v4; int i, found = 0; /* Look for this IP in LFT's per-cycle dns_cache */ for (i = 0; i < sess->dns_cache_count; i++) { if (sess->dns_cache[i].addr.s_addr == a.s_addr) { found = 1; /* c-ares entry exists; fill name if still empty */ if (!sess->dns_cache[i].name[0]) { strncpy(sess->dns_cache[i].name, rec->hostname, sizeof(sess->dns_cache[i].name) - 1); sess->dns_cache[i].name[sizeof(sess->dns_cache[i].name)-1] = '\0'; } break; } } if (!found && sess->dns_cache_count < LFT_DNS_CACHE_SIZE) { /* Not yet in dns_cache this cycle — add from our cache */ sess->dns_cache[sess->dns_cache_count].addr = a; strncpy(sess->dns_cache[sess->dns_cache_count].name, rec->hostname, sizeof(sess->dns_cache[0].name) - 1); sess->dns_cache[sess->dns_cache_count].name[sizeof(sess->dns_cache[0].name)-1] = '\0'; sess->dns_cache_count++; } } rec = rec->next; } } } /* Capture per-hop data from a completed sub-session into ws */ static void watch_capture_from_sess(watch_session_t *ws, lft_session_params *sub) { int hopno; struct trace_packet_info_s *tp; int maxhop; if (sub->num_hops) maxhop = sub->num_hops; else maxhop = sub->hop_info_length - 1; /* Reset n_alt counters for this cycle before scanning */ for (hopno = 0; hopno < WATCH_MAX_HOPS; hopno++) ws->hops[hopno].n_alt = 0; for (hopno = sub->ttl_min; hopno < sub->hop_info_length && hopno <= maxhop; hopno++) { double best_rtt = 0.0; int best_asn = 0; char best_host[64] = ""; char best_net[512] = ""; int first = 1; if (!sub->hop_info[hopno].all_rcvd) { /* For TCP/UDP traces the target hop (hopno == num_hops) replies with * a direct packet (RST or SYN-ACK), not ICMP TTL-exceeded, so * hop_info[num_hops].all_rcvd is always 0. Skip watch_update_hop * here; the manual target block in watch_run_one_cycle is the sole * handler for that slot — calling it here would double-count * cycles_total and insert a spurious history entry. */ if (sub->num_hops > 0 && hopno == sub->num_hops) { if (hopno >= ws->n_hops) ws->n_hops = hopno + 1; continue; } /* no reply at an intermediate hop this cycle */ watch_update_hop(&ws->hops[hopno], NULL, 0.0, 0, NULL); if (hopno >= ws->n_hops) ws->n_hops = hopno + 1; continue; } /* Gather data from all packets at this TTL */ SLIST_FOREACH(tp, &(sub->hop_info[hopno].packets), next_by_hop) { double rtt; const char *ipstr; if (!tp->recv.tv_sec) continue; if (tp->last_hop.s_addr == tp->hopaddr.s_addr) continue; rtt = timediff_ms(tp->sent, tp->recv); ipstr = inet_ntoa(tp->hopaddr); if (first) { first = 0; best_rtt = rtt; best_asn = tp->asnumber; snprintf(best_host, sizeof(best_host), "%s", ipstr); if (tp->netname[0] && strcmp(tp->netname, "NULL") != 0) snprintf(best_net, sizeof(best_net), "%s", tp->netname); } else { /* check for multipath (different IP at same TTL) */ if (strcmp(ipstr, best_host) != 0) { int n = ws->hops[hopno].n_alt; if (n < WATCH_MAX_ALT_IPS) { /* only add if not already listed */ int found = 0, j; for (j = 0; j < n; j++) { if (strcmp(ws->hops[hopno].alt_hosts[j], ipstr) == 0) { found = 1; break; } } if (!found) snprintf(ws->hops[hopno].alt_hosts[n++], 64, "%s", ipstr); ws->hops[hopno].n_alt = n; } } /* use min RTT across probes at this hop */ if (rtt > 0 && (best_rtt <= 0 || rtt < best_rtt)) best_rtt = rtt; } } if (!first) { watch_update_hop(&ws->hops[hopno], best_host, best_rtt, best_asn, best_net); if (hopno >= ws->n_hops) ws->n_hops = hopno + 1; #ifdef HAVE_CARES /* Capture PTR hostname from sub-session dns_cache (F4). * Store in both the ip_cache record (authoritative) and the * hop's display copy. */ { int di; watch_ipaddr_t haddr; int haf; if (watch_ip_parse(best_host, &haf, &haddr)) { struct in_addr hop_addr4; if (haf == AF_INET) hop_addr4 = haddr.v4; else hop_addr4.s_addr = 0; for (di = 0; di < sub->dns_cache_count; di++) { if (haf == AF_INET && sub->dns_cache[di].addr.s_addr == hop_addr4.s_addr && sub->dns_cache[di].name[0]) { watch_ip_record_t *rec = watch_ip_cache_get(&ws->ip_cache, haf, &haddr); if (rec && !rec->hostname[0]) { snprintf(rec->hostname, sizeof(rec->hostname), "%.255s", sub->dns_cache[di].name); rec->rdns_queried = 1; } snprintf(ws->hops[hopno].hostname, sizeof(ws->hops[hopno].hostname), "%s", sub->dns_cache[di].name); break; } } } } #endif } } /* Trim ws->n_hops to the last hop that has EVER responded. * This prevents no-reply hops beyond the actual path from bloating the * display when num_hops==0 (target unreachable, trace timed out, etc.). * Cloaked hops in the middle are preserved because we search backwards * from the end to find the last hop with any cycles_seen > 0. */ { int last = 0, j; for (j = ws->n_hops - 1; j >= 0; j--) { if (ws->hops[j].cycles_seen > 0) { last = j + 1; break; } } if (last > 0 && last < ws->n_hops) ws->n_hops = last; } } /* Reset the per-trace state on sess so LFTExecute can be called again. * The raw socket (send_sock) and pcap handle (pcapdescr) are preserved; * the HAVE_NCURSES guards in lft_lib.c ensure they are not re-created or * closed across cycles. */ static void watch_reset_session(lft_session_params *sess) { struct trace_packet_info_s *tp; /* Free all per-probe packet records from the previous cycle. * hop_info[].packets entries are the same pointers — freeing * trace_packets is sufficient; no separate per-hop free needed. * * IMPORTANT: the union u holds either: * u.icmp_packet (struct icmp_trace_packet_s) — used for protocol 2/3 (ICMP); * u.icmp_packet.packet is a heap pointer allocated by * generateICMPPacket() and must be freed here. * u.packet (struct trace_packet_s) — used for protocol 0/1 (TCP/UDP); * the first bytes of this struct are an embedded struct ip * (NOT a pointer). Reading u.icmp_packet.packet on a TCP * probe returns the first 8 bytes of struct ip, which is not * a heap address. Calling free() on it triggers * ___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED. */ while (SLIST_FIRST(&sess->trace_packets)) { tp = SLIST_FIRST(&sess->trace_packets); SLIST_REMOVE_HEAD(&sess->trace_packets, next); if ((sess->protocol == 2 || sess->protocol == 3) && tp->u.icmp_packet.packet) { free(tp->u.icmp_packet.packet); tp->u.icmp_packet.packet = NULL; } free(tp); } sess->trace_packets_num = 0; /* Free hop_info array; open_sockets() will re-allocate it next cycle */ if (sess->hop_info) { free(sess->hop_info); sess->hop_info = NULL; } sess->hop_info_length = sess->ttl_min; /* Reset per-trace counters and state */ sess->exit_state = 0; sess->num_hops = 0; sess->num_rcvd = 0; sess->target_open = 0; sess->target_filtered = 0; sess->target_anomaly = 0; sess->trg_probe_is_sent = 0; /* DO NOT reset sess->seq_start here. For UDP it's the IP-ID base * embedded in every probe — a router on the path uses it as part of * the ECMP 5-tuple/flow hash, so re-randomising it between cycles * would route each cycle down a different ECMP branch (defeats the * 3.97 UDP ECMP-stability fix). For TCP it's the ISN; reusing it * across cycles is harmless (each cycle is a fresh half-open SYN). * For ICMP it's unused (icmp_initial_seq anchors the flow). */ /* Destroy c-ares channel from the previous cycle so LFTExecute does not * leak it. LFTExecute will create a fresh channel on entry. */ #ifdef HAVE_CARES if (sess->ares_chan) { ares_destroy(sess->ares_chan); sess->ares_chan = NULL; } #endif /* Free whois session; LFTExecute will open a fresh one if needed */ if (sess->wsess) { w_close(sess->wsess); sess->wsess = NULL; } } /* Run one trace cycle by resetting per-trace state on proto_sess and * calling LFTExecute directly. The raw socket and pcap handle are * reused across cycles (HAVE_NCURSES guards in lft_lib.c prevent them * from being re-created or closed). */ static void watch_run_one_cycle(watch_session_t *ws, lft_session_params *proto_sess) { int saved_stdout, saved_stderr; int pipe_fds[2]; int use_pipe; /* 1 = capture stdout via pipe for log; 0 = /dev/null */ int i; /* Reset per-trace fields; keep send_sock, pcapdescr, and all user params */ watch_reset_session(proto_sess); /* Always redirect stdout+stderr to a pipe so LFT's -VV output is captured * into the log drawer ring buffer. WatchModeRun() forces noisy=2 so there * is always data to capture. On pipe failure fall back to /dev/null. */ use_pipe = (proto_sess->noisy >= 1); /* always true: WatchModeRun forces ≥2 */ pipe_fds[0] = pipe_fds[1] = -1; saved_stdout = dup(STDOUT_FILENO); saved_stderr = dup(STDERR_FILENO); if (use_pipe && pipe(pipe_fds) == 0) { /* Non-blocking write end: LFT never stalls if output exceeds pipe buf */ fcntl(pipe_fds[1], F_SETFL, fcntl(pipe_fds[1], F_GETFL) | O_NONBLOCK); dup2(pipe_fds[1], STDOUT_FILENO); dup2(pipe_fds[1], STDERR_FILENO); close(pipe_fds[1]); pipe_fds[1] = -1; } else { int devnull_fd = open("/dev/null", O_WRONLY); use_pipe = 0; if (devnull_fd >= 0) { dup2(devnull_fd, STDOUT_FILENO); dup2(devnull_fd, STDERR_FILENO); close(devnull_fd); } } /* style=0 keeps EVT_ON_EXIT from triggering a second WatchModeRun */ setOutputStyle(0); /* S1: keep watch_mode=1 so lft_lib.c guards preserve the raw socket and pcap * handle across all cycles. The first LFTExecute opens them as root; after * setuid(getuid()) drops privileges the descriptors remain open and are reused * for every subsequent cycle without needing elevated privileges again. */ /* Ensure rtt_stats is set so ASCIIOutput (view 1) has per-hop chart data */ if (proto_sess->rtt_stats <= 0) proto_sess->rtt_stats = ws->probes_per_hop; /* Pre-seed proto_sess->dns_cache from our persistent ip_cache before * LFTExecute fires c-ares queries. lft_ares_fire() already has a dedup * check that skips any IP already in dns_cache, so injecting resolved IPs * here suppresses redundant PTR queries for hops we have already resolved. * Combined with the watch_mode guard in lft_lib.c (dns_cache_count is not * zeroed when watch_mode=1), stable-path hops are never re-queried after * the first cycle. */ watch_inject_cached_hostnames(ws, proto_sess); /* Prepare the cycle/interval text for the SIGALRM handler and arm it. * ncurses is on /dev/tty so SIGALRM can call ncurses directly even while * stdout is redirected to /dev/null. */ { char maxcyc[16]; if (ws->max_cycles > 0) snprintf(maxcyc, sizeof(maxcyc), "%d", ws->max_cycles); else snprintf(maxcyc, sizeof(maxcyc), "\xe2\x88\x9e"); /* ∞ */ g_alarm_cycle_text_len = snprintf(g_alarm_cycle_text, sizeof(g_alarm_cycle_text), " tracing cycle %d/%s %ds ", ws->cycle, maxcyc, ws->interval); if (g_alarm_cycle_text_len < 0 || g_alarm_cycle_text_len >= (int)sizeof(g_alarm_cycle_text)) g_alarm_cycle_text_len = 0; g_alarm_frame = (sig_atomic_t)ws->spinner_frame; lft_o_counters_activate(); watch_spinner_arm(); } /* LFT-AUTOTUNE — apply learned-path knowledge (no-op on first cycle * and when --no-learn is set or stable_cycles < 3). */ lft_auto_tune_from_learned(proto_sess); LFTExecute(proto_sess); /* Disarm the spinner alarm; stop counter accumulation. */ watch_spinner_disarm(); lft_o_counters_deactivate(); /* Accumulate cumulative counters for the status bar and SIGALRM handler. */ ws->total_replies += lft_o_get_replies(); ws->total_sent += lft_o_get_sent(); if (lft_o_get_hops() > ws->max_hops_seen) ws->max_hops_seen = lft_o_get_hops(); if (lft_o_get_target()) ws->target_ever_reached = 1; ws->spinner_frame = (int)g_alarm_frame; /* Restore stdout + stderr before ncurses refresh */ fflush(stdout); fflush(stderr); dup2(saved_stdout, STDOUT_FILENO); dup2(saved_stderr, STDERR_FILENO); close(saved_stdout); close(saved_stderr); /* Drain captured verbose output into the log ring buffer. * Both ends of the pipe are now closed from stdout/stderr's perspective; * the write end was closed after dup2, so read() returns EOF promptly. */ if (use_pipe && pipe_fds[0] >= 0) { char chunk[4096]; ssize_t nr; fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK); while ((nr = read(pipe_fds[0], chunk, sizeof(chunk) - 1)) > 0) { chunk[nr] = '\0'; watch_log_append(ws, chunk); } close(pipe_fds[0]); pipe_fds[0] = -1; } /* Augment LFT's per-cycle dns_cache with hostnames already in our watch * ip_cache from previous cycles. c-ares resets dns_cache_count=0 at the * top of every LFTExecute and may not finish all queries before the trace * ends; this makes our persistent cache the reliable fallback source. */ watch_inject_cached_hostnames(ws, proto_sess); /* Extract hop data */ if (proto_sess->hop_info) watch_capture_from_sess(ws, proto_sess); /* Track current path length (not historical peak). Using a maximum * here would make the ⬡ counter sticky if a prior cycle inflated the * count via overshoot probes — set directly so the counter always * matches the table. */ ws->max_hops_seen = ws->n_hops; /* LFT-AUTOTUNE — capture learned-path data for use by the next cycle. * Only update when the cycle actually reached the target (n_hops > 0 * and target was hit), so a transient packet-loss cycle doesn't * corrupt the learned envelope. */ if (ws->n_hops > 0 && lft_o_get_target()) { int prev_num = proto_sess->learned.valid ? proto_sess->learned.num_hops : -1; int max_rtt_ms = 0; int min_rtt_ms = 0; int found_min = 0; int i; for (i = 0; i < ws->n_hops; i++) { int avg = (int)ws->hops[i].rtt_avg; if (avg <= 0) continue; if (avg > max_rtt_ms) max_rtt_ms = avg; if (!found_min || avg < min_rtt_ms) { min_rtt_ms = avg; found_min = 1; } } proto_sess->learned.num_hops = ws->n_hops; proto_sess->learned.max_rtt_ms = max_rtt_ms; proto_sess->learned.min_rtt_ms = min_rtt_ms; proto_sess->learned.last_protocol = proto_sess->protocol; proto_sess->learned.valid = 1; if (prev_num == ws->n_hops) proto_sess->learned.stable_cycles++; else proto_sess->learned.stable_cycles = 1; } else if (proto_sess->learned.valid) { /* Cycle missed the target — reset stability so we re-broaden * the trace until things settle again. */ proto_sess->learned.stable_cycles = 0; } /* Handle target: TCP RST/SYN-ACK responses are not stored in hop_info[] * (they're direct packets, not ICMP TTL-exceeded), so watch_capture_from_sess * leaves the target slot as no-reply for TCP traces. For ICMP traces the * target echo-reply IS stored in hop_info[], so watch_capture_from_sess * already called watch_update_hop for it — only do the manual update when * hop_info does NOT have a reply for the target slot (TCP/UDP cases). */ ws->target_hopno = 0; ws->target_open = 0; ws->target_filtered = 0; if (proto_sess->num_hops > 0 && proto_sess->remote_address.s_addr != 0) { int tgt = proto_sess->num_hops; watch_hop_t *wh = &ws->hops[tgt]; const char *tgt_ip = inet_ntoa(proto_sess->remote_address); /* Only insert synthetic target entry when watch_capture_from_sess * didn't already record a reply for this hop. */ int already_captured = (proto_sess->hop_info && tgt < proto_sess->hop_info_length && proto_sess->hop_info[tgt].all_rcvd > 0); if (!already_captured) { if (!wh->host[0]) snprintf(wh->host, sizeof(wh->host), "%s", tgt_ip); wh->cycles_total++; wh->cycles_seen++; { int idx = wh->history_head; wh->rtt_history[idx] = 0.001; wh->history_head = (wh->history_head + 1) % WATCH_HISTORY_LEN; if (wh->history_count < WATCH_HISTORY_LEN) wh->history_count++; } if (tgt >= ws->n_hops) ws->n_hops = tgt + 1; } ws->target_hopno = tgt; ws->target_open = proto_sess->target_open; ws->target_filtered = proto_sess->target_filtered; } /* Log brief completion note */ { char logmsg[128]; snprintf(logmsg, sizeof(logmsg), "cycle %d: %d hops, target_%s\n", ws->cycle, ws->n_hops, proto_sess->target_open > 0 ? "open" : (proto_sess->target_filtered ? "filtered" : (proto_sess->protocol == 2 || proto_sess->protocol == 3) ? "replied" : "closed")); watch_log_append(ws, logmsg); } /* Detect path change (compare hop IPs to snapshot) */ ws->path_changed = 0; if (ws->cycle > 1) { for (i = 0; i < ws->n_hops; i++) { if (strcmp(ws->hops[i].host, ws->path_snapshot[i]) != 0) { /* Only alert when BOTH sides are real IPs. A cloaked hop * ('*') is a firewall/rate-limit gap, not a routing change; * appearing/disappearing silently is normal behaviour. */ int old_real = ws->path_snapshot[i][0] && ws->path_snapshot[i][0] != '*'; int new_real = ws->hops[i].host[0] && ws->hops[i].host[0] != '*'; if (old_real && new_real) { ws->path_changed = 1; ws->path_change_time = time(NULL); char logmsg[192]; snprintf(logmsg, sizeof(logmsg), "PATH CHANGE at hop %d: was %s, now %s\n", i + 1, ws->path_snapshot[i], ws->hops[i].host); watch_log_append(ws, logmsg); break; } } } } /* Update snapshot */ for (i = 0; i < ws->n_hops; i++) snprintf(ws->path_snapshot[i], 64, "%s", ws->hops[i].host); } /* ========================================================================= * ncurses init / teardown * ========================================================================= */ static int watch_init_ncurses(watch_session_t *ws) { setlocale(LC_ALL, ""); /* enable UTF-8 so Unicode block chars render correctly */ /* Open ncurses on /dev/tty directly so that redirecting stdout to /dev/null * during LFTExecute traces has no effect on ncurses output. SIGALRM can * then call ncurses functions + doupdate() and the model stays in sync — * eliminating the blank-flash on the status bar row. */ g_tty_in = fopen("/dev/tty", "r"); g_tty_out = fopen("/dev/tty", "w"); if (!g_tty_in || !g_tty_out) { /* Fall back to stdout-based initscr if /dev/tty unavailable */ if (g_tty_in) { fclose(g_tty_in); g_tty_in = NULL; } if (g_tty_out) { fclose(g_tty_out); g_tty_out = NULL; } initscr(); } else { g_ncurses_screen = newterm(NULL, g_tty_out, g_tty_in); if (!g_ncurses_screen) { fclose(g_tty_in); g_tty_in = NULL; fclose(g_tty_out); g_tty_out = NULL; initscr(); } else { set_term(g_ncurses_screen); } } cbreak(); noecho(); keypad(stdscr, TRUE); nodelay(stdscr, TRUE); /* non-blocking getch() */ curs_set(0); /* hide cursor */ ws->term_rows = LINES; ws->term_cols = COLS; if (has_colors()) { start_color(); use_default_colors(); init_pair(CP_HEADER, COLOR_WHITE, COLOR_BLUE); init_pair(CP_NORMAL, -1, -1); init_pair(CP_GREEN, COLOR_GREEN, -1); init_pair(CP_YELLOW, COLOR_YELLOW, -1); /* CP_ORANGE — prefer xterm-256 palette colour 208 (bright orange) * when the terminal supports it; fall back to COLOR_MAGENTA on * 8-colour terminals. */ if (COLORS >= 256) init_pair(CP_ORANGE, 208, -1); else init_pair(CP_ORANGE, COLOR_MAGENTA, -1); init_pair(CP_RED, COLOR_RED, -1); init_pair(CP_HIGHLIGHT, COLOR_BLACK, COLOR_CYAN); init_pair(CP_STATUS, COLOR_CYAN, -1); init_pair(CP_DRAWER, COLOR_WHITE, -1); /* white on terminal's default bg */ init_pair(CP_BLUE, COLOR_BLUE, -1); /* ↑ sent counter */ /* CP_LABEL — light steel blue (xterm-256 147) for candlestick * column labels (HOP N / SOURCE / TARGET). Falls back to cyan * on 8-colour terminals. */ if (COLORS >= 256) init_pair(CP_LABEL, 147, -1); else init_pair(CP_LABEL, COLOR_CYAN, -1); /* CP_TBLHEAD — alice blue (xterm-256 195) for timeline table * column headers (HOP / HOST / LOSS% / …). Falls back to white * on 8-colour terminals. */ if (COLORS >= 256) init_pair(CP_TBLHEAD, 195, -1); else init_pair(CP_TBLHEAD, COLOR_WHITE, -1); /* CP_SENT — medium slate blue (xterm-256 75) for the status-bar * ↑ sent counter. Brighter than the frame's CP_BLUE so the live * count stands out against the other status-bar stats. */ if (COLORS >= 256) init_pair(CP_SENT, 75, -1); else init_pair(CP_SENT, COLOR_BLUE, -1); /* CP_KEYHINT — cornflower (xterm-256 111) for the row-2 key-hint menu */ if (COLORS >= 256) init_pair(CP_KEYHINT, 111, -1); else init_pair(CP_KEYHINT, COLOR_CYAN, -1); /* CP_KEYLTR — light sky blue (xterm-256 117) for [X] action letters */ if (COLORS >= 256) init_pair(CP_KEYLTR, 117, -1); else init_pair(CP_KEYLTR, COLOR_WHITE, -1); } return (LINES >= WATCH_MIN_ROWS) ? 0 : -1; } static void watch_teardown_ncurses(void) { endwin(); if (g_ncurses_screen) { delscreen(g_ncurses_screen); g_ncurses_screen = NULL; } if (g_tty_out) { fclose(g_tty_out); g_tty_out = NULL; } if (g_tty_in) { fclose(g_tty_in); g_tty_in = NULL; } } /* ========================================================================= * Status bar (bottom 2 rows) * ========================================================================= */ static void watch_render_status_bar(watch_session_t *ws, int spinner_frame) { int row1 = ws->term_rows - 2; int row2 = ws->term_rows - 1; const char *spin; char maxcyc[16]; char c_replies[16], c_sent[16], c_hops[16]; char center_str[320]; char buf[320]; /* row2 key hints / status message */ int i, left_w, center_sec, right_end, total_dashes, dash_l, dash_r; spin = SPINNER_FRAMES[spinner_frame % WATCH_SPINNER_FRAMES]; if (ws->max_cycles > 0) snprintf(maxcyc, sizeof(maxcyc), "%d", ws->max_cycles); else snprintf(maxcyc, sizeof(maxcyc), "\xe2\x88\x9e"); /* ∞ */ /* Format cumulative counters with thousands separators. * During a trace (in_trace=1) add the current-cycle live counts on top * of the accumulated baseline — the SIGALRM handler intentionally does * NOT write these fields, so they hold the cleanly-accumulated previous * totals until watch_run_one_cycle updates them at cycle end. */ { int disp_replies = ws->total_replies + (ws->in_trace ? lft_o_get_replies() : 0); int disp_sent = ws->total_sent + (ws->in_trace ? lft_o_get_sent() : 0); fmt_comma(c_replies, sizeof(c_replies), disp_replies); fmt_comma(c_sent, sizeof(c_sent), disp_sent); /* Left-justify counters to a MINIMUM of 4 display cols (trailing * spaces so the digits sit flush against the ↓/↑ arrows). Keeps * the footer width stable for counts 0…9999; field only grows * once a value crosses 10,000 and the comma-formatted string * naturally needs more chars. */ { char tmp[16]; if ((int)strlen(c_replies) < 4) { snprintf(tmp, sizeof(tmp), "%-4s", c_replies); snprintf(c_replies, sizeof(c_replies), "%s", tmp); } if ((int)strlen(c_sent) < 4) { snprintf(tmp, sizeof(tmp), "%-4s", c_sent); snprintf(c_sent, sizeof(c_sent), "%s", tmp); } } } fmt_comma(c_hops, sizeof(c_hops), ws->max_hops_seen); /* ------------------------------------------------------------------ */ /* Row 1: bordered status line, mirroring the top header style */ /* */ /* └──┤ ⠹ ↓N ↑N ⬡N ✔ ├────────┤ idle cycle N/∞ URL ~Ns ├────┘ */ /* ------------------------------------------------------------------ */ move(row1, 0); clrtoeol(); /* --- Measure widths to compute centering --- */ /* Left section content: spin(1) + " ↓"(3) + replies + " ↑"(3) + sent * + " ⬡ "(4) + hops + " ✔/✗"(3) — ✔/✗ glyph is 1 display col, so * the trailing " ✔" segment is 3 cols not 4. Using 4 made the right * side of the status bar fall exactly 1 col short of term_cols-1. */ int left_content_dw = 1 + 3 + (int)strlen(c_replies) + 3 + (int)strlen(c_sent) + 4 + (int)strlen(c_hops) + 3; /* " ✔" or " ✗" */ left_w = left_content_dw + 7; /* └──┤ space … space ├ */ /* Center section content: merged state+timer field + Cycle + target. * state_field is FIXED-WIDTH — its length is strlen("Idle ~Ns") for the * current interval. The idle form is the template; Tracing and Paused * write their words into the first N chars and pad the rest with '.'. * This prevents any horizontal reflow when the state transitions, so the * border dashes on either side stay visually locked. */ char idle_form[32]; char state_field[32]; int state_w; int i2; snprintf(idle_form, sizeof(idle_form), "Idle ~%ds", ws->interval); state_w = (int)strlen(idle_form); if ((size_t)state_w >= sizeof(state_field)) state_w = sizeof(state_field) - 1; if (ws->paused) { for (i2 = 0; i2 < state_w; i2++) state_field[i2] = '.'; state_field[state_w] = '\0'; if (state_w >= 6) memcpy(state_field, "Paused", 6); } else if (ws->in_trace) { for (i2 = 0; i2 < state_w; i2++) state_field[i2] = '.'; state_field[state_w] = '\0'; if (state_w >= 7) memcpy(state_field, "Tracing", 7); } else { memcpy(state_field, idle_form, state_w + 1); } /* Assemble full center string for centering-math purposes. NOTE: * maxcyc may contain "∞" (U+221E, 3 bytes but 1 display col), so we * measure display width rather than byte length — otherwise the * right-side dashes+corner come up short and the frame breaks. */ snprintf(center_str, sizeof(center_str), "%s Cycle %d/%s %s", state_field, ws->cycle, maxcyc, ws->tgt_label); /* Count display-columns in center_str (non-UTF8-continuation bytes) */ { const char *cp; int dw = 0; for (cp = center_str; *cp; cp++) if (((unsigned char)*cp & 0xC0) != 0x80) dw++; center_sec = dw + 4; /* ┤ space … space ├ */ } right_end = 3; /* ──┘ */ total_dashes = ws->term_cols - left_w - center_sec - right_end; if (total_dashes < 0) total_dashes = 0; /* Center the center section on the full terminal width */ dash_l = (ws->term_cols - center_sec) / 2 - left_w; if (dash_l < 0) dash_l = 0; if (dash_l > total_dashes) dash_l = total_dashes; dash_r = total_dashes - dash_l; /* --- Draw left section --- * Corner is └── (bottom-left closing corner) because nothing extends * below this row at col 0 — the key-hints row underneath is plain * text outside the frame. The table's side │ above meets this └ * naturally to close the box. */ if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); addstr("\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80"); /* └── */ addstr("\xe2\x94\xa4"); /* ┤ */ if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); addstr(" "); /* spinner */ attron(A_BOLD); addstr(spin); attroff(A_BOLD); /* ↓ replies (green bold) */ if (has_colors()) attron(COLOR_PAIR(CP_GREEN) | A_BOLD); addstr(" \xe2\x86\x93"); addstr(c_replies); if (has_colors()) attroff(COLOR_PAIR(CP_GREEN) | A_BOLD); /* ↑ sent (dodger blue bold — brighter than frame's CP_BLUE) */ if (has_colors()) attron(COLOR_PAIR(CP_SENT) | A_BOLD); addstr(" \xe2\x86\x91"); addstr(c_sent); if (has_colors()) attroff(COLOR_PAIR(CP_SENT) | A_BOLD); /* ⬡ hops (cyan bold) */ if (has_colors()) attron(COLOR_PAIR(CP_STATUS) | A_BOLD); addstr(" \xe2\xac\xa1 "); addstr(c_hops); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS) | A_BOLD); /* ✔ / ✗ target (sticky: stays ✔ if ever reached) */ if (ws->target_ever_reached) { if (has_colors()) attron(COLOR_PAIR(CP_GREEN) | A_BOLD); addstr(" \xe2\x9c\x94"); if (has_colors()) attroff(COLOR_PAIR(CP_GREEN) | A_BOLD); } else { if (has_colors()) attron(COLOR_PAIR(CP_RED) | A_BOLD); addstr(" \xe2\x9c\x97"); if (has_colors()) attroff(COLOR_PAIR(CP_RED) | A_BOLD); } /* Close left section: " ├" then dashes then "┤" center "├" dashes "──┘" */ addstr(" "); if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); addstr("\xe2\x94\x9c"); /* ├ */ for (i = 0; i < dash_l; i++) addstr("\xe2\x94\x80"); /* ─ */ addstr("\xe2\x94\xa4"); /* ┤ */ if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); /* Center content — split into (state_field) + (Cycle … target) so the * paused state can blink independently at 1 Hz. */ addstr(" "); { attr_t state_attr; if (ws->paused) { /* 1-Hz blink between outline-text color bold and white bold */ state_attr = (time(NULL) & 1) ? (COLOR_PAIR(CP_SENT) | A_BOLD) : (COLOR_PAIR(CP_NORMAL) | A_BOLD); } else { state_attr = COLOR_PAIR(CP_SENT) | A_BOLD; } if (has_colors()) attron(state_attr); addstr(state_field); if (has_colors()) attroff(state_attr); if (has_colors()) attron(COLOR_PAIR(CP_SENT) | A_BOLD); printw(" Cycle %d/%s %s", ws->cycle, maxcyc, ws->tgt_label); if (has_colors()) attroff(COLOR_PAIR(CP_SENT) | A_BOLD); } addstr(" "); if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); addstr("\xe2\x94\x9c"); /* ├ */ for (i = 0; i < dash_r; i++) addstr("\xe2\x94\x80"); /* ─ */ addstr("\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98"); /* ──┘ (bottom-right corner) */ if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); /* ------------------------------------------------------------------ */ /* Row 2: key hints — OR transient status message if one is active */ /* Key-hint menu is centered on the line and coloured CP_KEYHINT */ /* (xterm 111 cornflower). A transient status message (e.g. the */ /* "saved to /path/file.txt" confirmation) still renders left-aligned */ /* at col 0 for visibility. */ /* ------------------------------------------------------------------ */ move(row2, 0); clrtoeol(); if (ws->status_msg[0] && difftime(time(NULL), ws->status_msg_time) < 3.0) { snprintf(buf, sizeof(buf), " %s", ws->status_msg); if (has_colors()) attron(COLOR_PAIR(CP_STATUS)); mvaddnstr(row2, 0, buf, ws->term_cols - 1); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS)); } else { ws->status_msg[0] = '\0'; /* clear expired message */ snprintf(buf, sizeof(buf), "[1-3]view [q]quit [p]%s [r]reset [c]clear [v]log [w]whois [h]host [x]export [+/-]interval", ws->paused ? "resume" : "pause"); /* Center on the row (ASCII-only string → strlen == display width) */ int menu_w = (int)strlen(buf); int menu_x = (ws->term_cols - menu_w) / 2; if (menu_x < 0) menu_x = 0; /* Render in two colours: CP_KEYLTR for chars inside [...], CP_KEYHINT * for everything else. Walk byte-by-byte flipping attrs at [ / ]. */ move(row2, menu_x); { int in_bracket = 0; const char *p; int remaining = ws->term_cols - menu_x; if (has_colors()) attron(COLOR_PAIR(CP_KEYHINT)); for (p = buf; *p && remaining > 0; p++) { if (*p == '[') { if (has_colors()) { attroff(COLOR_PAIR(CP_KEYHINT)); attron(COLOR_PAIR(CP_KEYHINT)); } addch((unsigned char)*p); if (has_colors()) { attroff(COLOR_PAIR(CP_KEYHINT)); attron(COLOR_PAIR(CP_KEYLTR)); } in_bracket = 1; } else if (*p == ']') { if (has_colors()) { attroff(COLOR_PAIR(CP_KEYLTR)); attron(COLOR_PAIR(CP_KEYHINT)); } addch((unsigned char)*p); in_bracket = 0; } else { addch((unsigned char)*p); } remaining--; } if (has_colors()) { if (in_bracket) attroff(COLOR_PAIR(CP_KEYLTR)); else attroff(COLOR_PAIR(CP_KEYHINT)); } } } /* Callers (watch_render, idle loop, SIGALRM) all call wnoutrefresh+doupdate */ } /* Forward declarations for functions defined later in this file */ static int watch_render_timeline(watch_session_t *ws, int start_row, int max_rows, int col_offset); static void watch_render_status_bar(watch_session_t *ws, int spinner_frame); /* ========================================================================= * View 1 (default): native ncurses port of ASCIIOutput candlestick chart. * * This is a self-contained copy of the ASCIIOutput logic from lft_lib.c, * ported to draw directly into the ncurses window. Having our own copy * lets us add watch-mode-specific customisations (color pairs, row limits, * hostname toggle, etc.) without touching the shared lft_lib.c output path. * * Key helpers (wa_ prefix = Watch Ascii): * wa_utf8_display_width() — count display columns in a UTF-8 string * wa_center() — center-print a string into a fixed-width field * wa_rtt_stats_t / wa_compute_rtt_stats() — per-hop RTT aggregation * wa_col_t — per-hop column descriptor (mirror of ascii_col) * wa_chart_render() — draw the candlestick chart body * wa_render_header_row() — draw ┌─ HOP N ─┐ row * watch_render_ascii_chart() — main entry: build cols[], layout, render * ========================================================================= */ /* ---- rendering context (current cursor position inside ncurses) ---- */ typedef struct { int row; /* current screen row */ int col; /* current screen column */ int max_row; /* stop rendering at this row (exclusive) */ int term_cols; /* full terminal width */ int start_col; /* left content margin (0 = no border; 1 = bordered) */ } wa_ctx_t; /* ---- UTF-8 display width (copied from lft_lib.c) ---- */ static int wa_utf8_display_width(const char *s) { int w = 0; while (*s) { unsigned char b = (unsigned char)*s++; if ((b & 0xC0) != 0x80) w++; } return w; } /* Truncate s in-place so its display width does not exceed max_w. * Truncation always lands on a UTF-8 character boundary. */ static void wa_utf8_truncate_to(char *s, int max_w) { int i = (int)strlen(s); while (i > 0 && wa_utf8_display_width(s) > max_w) { i--; /* Back up over any UTF-8 continuation bytes (10xxxxxx) */ while (i > 0 && ((unsigned char)s[i] & 0xC0) == 0x80) i--; s[i] = '\0'; } } /* Advance to the next row; clears to end of old row. */ static void wa_nl(wa_ctx_t *ctx) { clrtoeol(); ctx->row++; ctx->col = ctx->start_col; if (ctx->row < ctx->max_row) move(ctx->row, ctx->start_col); } /* Write a string at the current position; update ctx->col. * Tracks display-column width (not bytes) via utf8_display_width arithmetic. * ncurses itself clips at the right margin; we just track col for layout. */ static void wa_str(wa_ctx_t *ctx, const char *s) { int dw; if (!s || !*s || ctx->row >= ctx->max_row) return; /* Measure display width — count non-continuation UTF-8 bytes */ dw = wa_utf8_display_width(s); if (ctx->col + dw > ctx->term_cols - 1) dw = ctx->term_cols - 1 - ctx->col; if (dw <= 0) return; move(ctx->row, ctx->col); addstr(s); /* ncurses clips at right margin automatically */ ctx->col += dw; } /* Printf-style wrapper. */ static void wa_printf(wa_ctx_t *ctx, const char *fmt, ...) { char buf[512]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); wa_str(ctx, buf); } /* Repeat a single-byte character n times. */ static void wa_repeat_ch(wa_ctx_t *ctx, char c, int n) { int i; for (i = 0; i < n && ctx->col < ctx->term_cols - 1; i++) { move(ctx->row, ctx->col); addch((unsigned char)c); ctx->col++; } } /* Repeat a UTF-8 string n times (e.g. "─" for box drawing). */ static void wa_repeat_str(wa_ctx_t *ctx, const char *s, int n) { int i; for (i = 0; i < n && ctx->col < ctx->term_cols - 1; i++) { move(ctx->row, ctx->col); addstr(s); ctx->col++; /* each repeat is one display column */ } } /* ---- center-print string s into a field of w display columns ---- */ static void wa_center(wa_ctx_t *ctx, const char *s, int w) { int sl = wa_utf8_display_width(s); int left, right; /* Always reserve at least 1 col of padding on each side when room * exists (i.e. string length + 2 ≤ column width). This gives every * candlestick column a small visual gap so long labels don't butt up * against their neighbours or against the column's cloaked fill. */ if (sl + 2 <= w) { left = (w - sl) / 2; right = w - sl - left; if (left < 1) left = 1; if (right < 1) right = 1; } else { /* Content equals or exceeds column width — fall back to plain * centering (may emit no padding if exactly flush). */ left = (w - sl) / 2; right = w - sl - left; if (left < 0) left = 0; if (right < 0) right = 0; } wa_repeat_ch(ctx, ' ', left); wa_str(ctx, s); wa_repeat_ch(ctx, ' ', right); } /* ---- per-hop RTT statistics (copied from lft_lib.c) ---- */ typedef struct { double rtt_min; double rtt_avg; double rtt_max; double rtt_stddev; int n; } wa_rtt_stats_t; static wa_rtt_stats_t wa_compute_rtt_stats(lft_session_params *sess, int hopno) { wa_rtt_stats_t s; struct trace_packet_info_s *tp; double sum = 0.0, sum2 = 0.0; int n = 0; double rtt_min = 0.0, rtt_max = 0.0; memset(&s, 0, sizeof(s)); if (hopno < 0 || hopno >= sess->hop_info_length) return s; SLIST_FOREACH(tp, &sess->hop_info[hopno].packets, next_by_hop) { if (!tp->recv.tv_sec) continue; { double rtt = timediff_ms(tp->sent, tp->recv); if (n == 0 || rtt < rtt_min) rtt_min = rtt; if (n == 0 || rtt > rtt_max) rtt_max = rtt; sum += rtt; sum2 += rtt * rtt; n++; } } if (n == 0) return s; s.n = n; s.rtt_min = rtt_min; s.rtt_max = rtt_max; s.rtt_avg = sum / n; s.rtt_stddev = (n > 1) ? sqrt(sum2/n - (sum/n)*(sum/n)) : 0.0; if (s.rtt_stddev < 0.0) s.rtt_stddev = 0.0; return s; } /* ---- per-column descriptor (mirror of struct ascii_col) ---- */ #define WA_MAX_COLS 256 #define WA_CHART_H 8 /* candlestick body rows */ #define WA_CHART_SEP 4 /* display columns between hops */ #define WA_JITTER_WARN 25.0 #define WA_MAX_COL_W 17 /* maximum column width for candlestick/bar charts */ typedef struct { char hdr[64]; char host[256]; char sub[128]; char asn[32]; char net[64]; char rtt[32]; char ann[64]; char mtu[32]; char jitter_ann[32]; wa_rtt_stats_t stats; int jitter_warn; int is_hole; int is_target; int is_source; int is_ghost; /* 1 = hop known from history but cloaked this cycle */ int anomtype; int w; /* column display width */ int n_alt; /* number of alternate (multipath) IPs */ char alt_hosts[WATCH_MAX_ALT_IPS][64]; /* alternate IP strings */ } wa_col_t; /* ---- ncurses color helpers ---- */ /* Map semantic intent to an existing watch color pair + attribute */ static void wa_color_on(int pair, int attr) { if (has_colors()) attron(COLOR_PAIR(pair)); if (attr) attron(attr); } static void wa_color_off(int pair, int attr) { if (has_colors()) attroff(COLOR_PAIR(pair)); if (attr) attroff(attr); } /* ---- render hop-label header row for one slice of columns ---- * flip_outer=0: ┌─ HOP N ─┐ for all boxes (top row above chart) * flip_outer=1: └─ HOP N ─┐ / ┌─ HOP N ─┘ outer corners (bottom row below chart) */ static void wa_render_header_row(wa_ctx_t *ctx, const wa_col_t *cols, int start, int end, int flip_outer) { int i; for (i = start; i <= end && ctx->col < ctx->term_cols - 1; i++) { const wa_col_t *c = &cols[i]; int hlen = (int)strlen(c->hdr); /* Strip outer "[ " and " ]" to get inner label like "HOP 1" */ char label[64]; int label_len; if (hlen >= 4) { snprintf(label, sizeof(label), "%.*s", hlen - 4, c->hdr + 2); label_len = (int)strlen(label); } else { snprintf(label, sizeof(label), "%s", c->hdr); label_len = hlen; } int inner = c->w - 2; int dashes_l = (inner - label_len - 2) / 2; int dashes_r = inner - label_len - 2 - dashes_l; if (dashes_l < 0) dashes_l = 0; if (dashes_r < 0) dashes_r = 0; /* bottom row: all corners flip to └ / ┘ */ const char *lc = flip_outer ? "\xe2\x94\x94" /* └ */ : "\xe2\x94\x8c"; /* ┌ */ const char *rc = flip_outer ? "\xe2\x94\x98" /* ┘ */ : "\xe2\x94\x90"; /* ┐ */ /* Frame chars (┌─┐└─┘) in cyan; label text ("HOP N" / "SOURCE" * / "TARGET") in CP_LABEL (light steel blue) for visual * distinction from frame and data text. */ wa_color_on(CP_STATUS, 0); wa_str(ctx, lc); wa_repeat_str(ctx, "\xe2\x94\x80", dashes_l); /* ─ × n */ wa_color_off(CP_STATUS, 0); wa_color_on(CP_LABEL, 0); wa_printf(ctx, " %s ", label); wa_color_off(CP_LABEL, 0); wa_color_on(CP_STATUS, 0); wa_repeat_str(ctx, "\xe2\x94\x80", dashes_r); /* ─ × n */ wa_str(ctx, rc); wa_color_off(CP_STATUS, 0); if (i < end) wa_repeat_ch(ctx, ' ', WA_CHART_SEP); } wa_nl(ctx); } /* ---- draw the candlestick chart body for one column slice ---- */ static void wa_chart_render(wa_ctx_t *ctx, const wa_col_t *cols, int start, int end, double g_min, double g_max, const char *info_str) { int i, r; double range = (g_max > g_min + 0.1) ? (g_max - g_min) : 1.0; (void)g_min; /* Y-axis prefix width */ char ymax_buf[16]; snprintf(ymax_buf, sizeof(ymax_buf), "%dms", (int)ceil(g_max > 0 ? g_max : 1.0)); int yval_w = (int)strlen(ymax_buf); /* ---- header row (above chart for spatial context) ---- */ wa_color_on(CP_NORMAL, A_DIM); wa_printf(ctx, "%*s ", yval_w, ""); wa_color_off(CP_NORMAL, A_DIM); wa_render_header_row(ctx, cols, start, end, 0); if (ctx->row >= ctx->max_row) return; /* ---- max RTT label row ---- */ { int any = 0; for (i = start; i <= end; i++) if (cols[i].stats.n > 0 || cols[i].is_hole) { any = 1; break; } if (any) { wa_color_on(CP_NORMAL, A_DIM); wa_printf(ctx, "%*s ", yval_w, ""); wa_color_off(CP_NORMAL, A_DIM); for (i = start; i <= end; i++) { const wa_col_t *c = &cols[i]; if (c->stats.n > 0) { char lbl[24]; snprintf(lbl, sizeof(lbl), "%dms max", (int)round(c->stats.rtt_max)); if (c->is_ghost) wa_color_on(CP_NORMAL, A_DIM); else wa_color_on(c->jitter_warn ? CP_YELLOW : CP_GREEN, 0); wa_center(ctx, lbl, c->w); if (c->is_ghost) wa_color_off(CP_NORMAL, A_DIM); else wa_color_off(c->jitter_warn ? CP_YELLOW : CP_GREEN, 0); } else if (c->is_hole) { wa_color_on(CP_NORMAL, A_DIM); { int d; wa_str(ctx, " "); for (d = 0; d < c->w - 2; d++) wa_str(ctx, "\xe2\x96\x91"); if (c->w >= 2) wa_str(ctx, " "); } /* ░ padded */ wa_color_off(CP_NORMAL, A_DIM); } else { wa_repeat_ch(ctx, ' ', c->w); } if (i < end) wa_repeat_ch(ctx, ' ', WA_CHART_SEP); } wa_nl(ctx); if (ctx->row >= ctx->max_row) return; } } /* ---- precompute per-column bar row indices ---- */ int col_max_row[WA_MAX_COLS]; int col_avg_row[WA_MAX_COLS]; int col_min_row[WA_MAX_COLS]; for (i = start; i <= end; i++) { const wa_col_t *c = &cols[i]; if (c->stats.n > 0) { col_max_row[i] = (int)round((g_max - c->stats.rtt_max) / range * (WA_CHART_H - 1)); col_avg_row[i] = (int)round((g_max - c->stats.rtt_avg) / range * (WA_CHART_H - 1)); col_min_row[i] = (int)round((g_max - c->stats.rtt_min) / range * (WA_CHART_H - 1)); if (col_max_row[i] < 0) col_max_row[i] = 0; if (col_max_row[i] >= WA_CHART_H) col_max_row[i] = WA_CHART_H - 1; if (col_avg_row[i] < 0) col_avg_row[i] = 0; if (col_avg_row[i] >= WA_CHART_H) col_avg_row[i] = WA_CHART_H - 1; if (col_min_row[i] < 0) col_min_row[i] = 0; if (col_min_row[i] >= WA_CHART_H) col_min_row[i] = WA_CHART_H - 1; if (col_avg_row[i] < col_max_row[i]) col_avg_row[i] = col_max_row[i]; if (col_avg_row[i] > col_min_row[i]) col_avg_row[i] = col_min_row[i]; if (c->stats.rtt_min != c->stats.rtt_max && col_max_row[i] == col_min_row[i]) { if (col_min_row[i] < WA_CHART_H - 1) col_min_row[i]++; else if (col_max_row[i] > 0) col_max_row[i]--; if (col_avg_row[i] < col_max_row[i]) col_avg_row[i] = col_max_row[i]; if (col_avg_row[i] > col_min_row[i]) col_avg_row[i] = col_min_row[i]; } } else { col_max_row[i] = col_avg_row[i] = col_min_row[i] = 0; } } /* ---- chart body rows ---- */ for (r = 0; r < WA_CHART_H && ctx->row < ctx->max_row; r++) { /* Y-axis prefix */ wa_color_on(CP_NORMAL, A_DIM); if (r % 2 == 0) { double row_rtt = g_max - (double)r / (WA_CHART_H - 1) * range; wa_printf(ctx, "%*dms ", yval_w - 2, (int)round(row_rtt)); wa_str(ctx, "\xe2\x94\xa4 "); /* ┤ */ } else { wa_printf(ctx, "%*s ", yval_w, ""); wa_str(ctx, "\xe2\x94\x82 "); /* │ */ } wa_color_off(CP_NORMAL, A_DIM); for (i = start; i <= end && ctx->col < ctx->term_cols - 1; i++) { const wa_col_t *c = &cols[i]; int cw = c->w; if (c->stats.n == 0) { if (c->is_hole) { wa_color_on(CP_NORMAL, A_DIM); { int d; wa_str(ctx, " "); for (d = 0; d < cw - 2; d++) wa_str(ctx, "\xe2\x96\x91"); if (cw >= 2) wa_str(ctx, " "); } /* ░ padded */ wa_color_off(CP_NORMAL, A_DIM); } else { wa_repeat_ch(ctx, ' ', cw); } } else { int mr = col_max_row[i]; int ar = col_avg_row[i]; int mir = col_min_row[i]; int collapsed = (mr == mir); int in_shaft = (r >= mr && r <= mir); int is_max = (r == mr); int is_min = (r == mir); int is_avg = (r == ar); int d, mid; if (in_shaft) { /* Ghost hops render entirely dim — no color distinction */ if (c->is_ghost) wa_color_on(CP_NORMAL, A_DIM); else if (is_avg) wa_color_on(c->jitter_warn ? CP_YELLOW : CP_GREEN, 0); else if (is_max || is_min) wa_color_on(CP_NORMAL, A_BOLD); else wa_color_on(CP_NORMAL, A_DIM); } /* Bars are rendered 1-col thinner on each side (bw = cw-2) * so they visually fit inside the column brackets ┌─ HOP N ─┐ * above / └─ HOP N ─┘ below rather than butting against them. */ int bw = (cw >= 2) ? (cw - 2) : cw; int pad = (cw >= 2) ? 1 : 0; if (!in_shaft) { wa_repeat_ch(ctx, ' ', cw); } else if (collapsed || is_avg) { /* full-width solid bar █ */ wa_repeat_ch(ctx, ' ', pad); for (d = 0; d < bw; d++) wa_str(ctx, "\xe2\x96\x88"); wa_repeat_ch(ctx, ' ', pad); } else if (is_max) { /* max whisker cap ──┬── */ wa_repeat_ch(ctx, ' ', pad); mid = (bw - 1) / 2; for (d = 0; d < bw; d++) wa_str(ctx, d == mid ? "\xe2\x94\xac" /* ┬ */ : "\xe2\x94\x80"); /* ─ */ wa_repeat_ch(ctx, ' ', pad); } else if (is_min) { /* min whisker cap ──┴── */ wa_repeat_ch(ctx, ' ', pad); mid = (bw - 1) / 2; for (d = 0; d < bw; d++) wa_str(ctx, d == mid ? "\xe2\x94\xb4" /* ┴ */ : "\xe2\x94\x80"); /* ─ */ wa_repeat_ch(ctx, ' ', pad); } else { /* shaft / wick: single │ centered (wa_center keeps * 1-col min padding via its built-in behavior) */ wa_center(ctx, "\xe2\x94\x82", cw); /* │ */ } if (in_shaft) { if (c->is_ghost) wa_color_off(CP_NORMAL, A_DIM); else if (is_avg) wa_color_off(c->jitter_warn ? CP_YELLOW : CP_GREEN, 0); else if (is_max || is_min) wa_color_off(CP_NORMAL, A_BOLD); else wa_color_off(CP_NORMAL, A_DIM); } } if (i < end) wa_repeat_ch(ctx, ' ', WA_CHART_SEP); } wa_nl(ctx); } if (ctx->row >= ctx->max_row) return; /* ---- min RTT label row ---- */ { int any = 0; for (i = start; i <= end; i++) if (cols[i].stats.n > 0 || cols[i].is_hole) { any = 1; break; } if (any) { wa_color_on(CP_NORMAL, A_DIM); wa_printf(ctx, "%*s ", yval_w, ""); wa_color_off(CP_NORMAL, A_DIM); for (i = start; i <= end; i++) { const wa_col_t *c = &cols[i]; if (c->stats.n > 0) { char lbl[24]; snprintf(lbl, sizeof(lbl), "%dms min", (int)round(c->stats.rtt_min)); if (c->is_ghost) wa_color_on(CP_NORMAL, A_DIM); else wa_color_on(c->jitter_warn ? CP_YELLOW : CP_GREEN, 0); wa_center(ctx, lbl, c->w); if (c->is_ghost) wa_color_off(CP_NORMAL, A_DIM); else wa_color_off(c->jitter_warn ? CP_YELLOW : CP_GREEN, 0); } else if (c->is_hole) { wa_color_on(CP_NORMAL, A_DIM); { int d; wa_str(ctx, " "); for (d = 0; d < c->w - 2; d++) wa_str(ctx, "\xe2\x96\x91"); if (c->w >= 2) wa_str(ctx, " "); } /* ░ padded */ wa_color_off(CP_NORMAL, A_DIM); } else { wa_repeat_ch(ctx, ' ', c->w); } if (i < end) wa_repeat_ch(ctx, ' ', WA_CHART_SEP); } wa_nl(ctx); if (ctx->row >= ctx->max_row) return; } } /* ---- X-axis separator row: └─────┤ label ├───── (only when info_str * is non-empty). Previously emitted a blank wa_nl() even when info_str * was empty, which left a redundant blank row between the "Nms min" * row and the bottom hop-label brackets. That gap is now gone. */ if (info_str && info_str[0]) { int any = 0; for (i = start; i <= end; i++) if (cols[i].stats.n > 0) { any = 1; break; } if (any) { int total_w = 0; for (i = start; i <= end; i++) { total_w += cols[i].w; if (i < end) total_w += WA_CHART_SEP; } char range_lbl[256]; snprintf(range_lbl, sizeof(range_lbl), "\xe2\x94\xa4 %s \xe2\x94\x9c", info_str); /* ┤ … ├ */ int label_disp = wa_utf8_display_width(range_lbl); int dash_total = total_w - 2 - label_disp; int dash_l = dash_total / 2; int dash_r = dash_total - dash_l; if (dash_l < 0) dash_l = 0; if (dash_r < 0) dash_r = 0; wa_color_on(CP_NORMAL, A_DIM); wa_printf(ctx, "%*s ", yval_w, ""); wa_str(ctx, "\xe2\x94\x94\xe2\x94\x80"); /* └─ */ wa_repeat_str(ctx, "\xe2\x94\x80", dash_l); wa_str(ctx, range_lbl); wa_repeat_str(ctx, "\xe2\x94\x80", dash_r); wa_color_off(CP_NORMAL, A_DIM); wa_nl(ctx); } } } /* * Build col[] descriptors from proto_sess and render the full candlestick * chart (one or more column slices) into ncurses rows [start_row, max_row). * Returns the number of rows consumed. */ static int watch_render_ascii_chart(watch_session_t *ws, lft_session_params *proto_sess, int start_row, int max_rows, int start_col) { wa_col_t cols[WA_MAX_COLS]; int ncols = 0; int hopno, maxhop; int reply, noreply, neglstart, neglend; int prevasn = -1, prevasn_hopno = 0; struct in_addr classbmask, masked_target, prevasn_hopaddr; int netreached = 0; int asseam_hopno = -1, netseam_hopno = -1; int lastishole = 0; int found_target = 0; struct trace_packet_info_s *tp; /* Usable content width: full terminal minus left margin minus right border col */ int term_cols = ws->term_cols - start_col - (start_col > 0 ? 1 : 0); wa_ctx_t ctx = { start_row, start_col, start_row + max_rows, ws->term_cols, start_col }; if (proto_sess == NULL || ws->n_hops == 0) return 0; /* ---- determine SEAM positions ---- */ inet_aton("255.255.0.0", &classbmask); masked_target.s_addr = proto_sess->remote_address.s_addr & classbmask.s_addr; if (proto_sess->num_hops) maxhop = proto_sess->num_hops; else maxhop = proto_sess->hop_info_length - 1; for (hopno = proto_sess->ttl_min; hopno < proto_sess->hop_info_length; hopno++) { int icmpcode = -100; if (proto_sess->hop_info[hopno].all_rcvd) { lastishole = 0; SLIST_FOREACH(tp, &(proto_sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec) { if (hopno <= maxhop) icmpcode = tp->icmp_type; if (tp->last_hop.s_addr != tp->hopaddr.s_addr) { if ((tp->hopaddr.s_addr & classbmask.s_addr) == masked_target.s_addr) netreached = 1; else netseam_hopno = hopno; if (proto_sess->do_aslookup || proto_sess->do_netlookup) { if (prevasn == -1) { if (tp->asnumber) { prevasn = tp->asnumber; prevasn_hopno = hopno; prevasn_hopaddr = tp->hopaddr; } } else if (tp->asnumber && tp->asnumber != prevasn) { asseam_hopno = prevasn_hopno; prevasn = tp->asnumber; prevasn_hopno = hopno; prevasn_hopaddr = tp->hopaddr; } else if (tp->asnumber && tp->asnumber == prevasn) { prevasn_hopno = hopno; prevasn_hopaddr = tp->hopaddr; } } } } } } else { lastishole = 1; } if (icmpcode == -1) break; } if (!netreached) netseam_hopno = -1; if (lastishole) asseam_hopno = -1; (void)prevasn_hopaddr; /* ---- build SOURCE column ---- */ { wa_col_t *c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->is_source = 1; snprintf(c->hdr, sizeof(c->hdr), "[ SOURCE ]"); /* Chart always shows IP — no hostname lookup in the candlestick pane */ snprintf(c->host, sizeof(c->host), "%s", inet_ntoa(proto_sess->local_address)); if (proto_sess->protocol < 2 || proto_sess->protocol > 3) { size_t hlen = strlen(c->host); snprintf(c->host + hlen, sizeof(c->host) - hlen, ":%d", proto_sess->sport); } c->sub[0] = '\0'; /* host IS the IP — no sub-line needed */ c->w = wa_utf8_display_width(c->hdr); if (wa_utf8_display_width(c->host) > c->w) c->w = wa_utf8_display_width(c->host); } /* ---- build per-hop columns ---- */ neglstart = neglend = -1; reply = noreply = 0; (void)reply; (void)noreply; for (hopno = proto_sess->ttl_min; hopno < proto_sess->hop_info_length; hopno++) { int anomtype = 0; int icmpcode = -100; wa_col_t *c; if (ncols >= WA_MAX_COLS) break; if ((proto_sess->hop_info[hopno].state == HS_SEND_FIN) && (proto_sess->hop_info[hopno+1].state == HS_SEND_SYN) && (proto_sess->hop_info[hopno+1].ts_last_recv.tv_sec)) anomtype = 1; if ((proto_sess->hop_info[hopno].state != HS_SEND_SYN_ACK) && (proto_sess->hop_info[hopno+1].state == HS_SEND_SYN_ACK) && (hopno == (proto_sess->num_hops - 1))) anomtype = 2; if ((proto_sess->hop_info[hopno].flags & HF_ENDPOINT) && (noreply >= ((maxhop - proto_sess->ttl_min) / 2)) && proto_sess->num_hops > 3) anomtype = 3; if (proto_sess->hop_info[hopno].all_rcvd == 0) { /* If we have historical data for this TTL (seen in a prior cycle), * render it as a "ghost" column rather than collapsing it into a * featureless cloaked hole. This preserves RTT history, ASN, and * host data in the chart even when a router stops responding. */ if (hopno < WATCH_MAX_HOPS && ws->hops[hopno].cycles_seen > 0 && ws->hops[hopno].host[0] && strcmp(ws->hops[hopno].host, "*") != 0) { /* Flush any pending pure-hole block before the ghost column */ if (neglstart != -1 && ncols < WA_MAX_COLS) { c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->is_hole = 1; if (neglstart == neglend) snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", neglstart + 1); else snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d-%d ]", neglstart + 1, neglend + 1); snprintf(c->host, sizeof(c->host), "cloaked"); snprintf(c->rtt, sizeof(c->rtt), neglstart == neglend ? "TTL %d" : "TTLs %d-%d", neglstart + 1, neglend + 1); c->w = wa_utf8_display_width(c->hdr); if (wa_utf8_display_width(c->host) > c->w) c->w = wa_utf8_display_width(c->host); if (wa_utf8_display_width(c->rtt) > c->w) c->w = wa_utf8_display_width(c->rtt); neglstart = neglend = -1; } /* Build ghost column from ws->hops[hopno] historical data */ if (ncols < WA_MAX_COLS) { const watch_hop_t *wh = &ws->hops[hopno]; c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->is_ghost = 1; snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", hopno + 1); /* Candlestick chart always shows the IP address — * hostnames only appear in the timeline table below. * This drops the sub-line, saving one chart row and * avoiding per-cycle alignment churn when hostnames * resolve (or fail to resolve) asynchronously. */ snprintf(c->host, sizeof(c->host), "%s", wh->host[0] ? wh->host : "*"); c->sub[0] = '\0'; /* ASN / NETNAME are deliberately NOT populated here — * they render in the timeline table below the chart. * Showing them in the candlestick made them flicker in * and out depending on whether a cycle had the data * cached, making the chart look unstable. */ c->asn[0] = '\0'; c->net[0] = '\0'; /* Historical RTT stats — use all-time min/avg/max */ c->stats.n = wh->cycles_seen; c->stats.rtt_min = wh->rtt_min; c->stats.rtt_max = wh->rtt_max; c->stats.rtt_avg = wh->rtt_avg; c->stats.rtt_stddev = 0.0; snprintf(c->rtt, sizeof(c->rtt), "%d/%d/%dms", (int)round(wh->rtt_min), (int)round(wh->rtt_avg), (int)round(wh->rtt_max)); c->w = wa_utf8_display_width(c->hdr); if (wa_utf8_display_width(c->host) > c->w) c->w = wa_utf8_display_width(c->host); if (wa_utf8_display_width(c->rtt) > c->w) c->w = wa_utf8_display_width(c->rtt); } continue; } if (neglstart == -1) neglstart = hopno; neglend = hopno; continue; } /* emit pending hole column */ if (neglstart != -1 && ncols < WA_MAX_COLS) { c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->is_hole = 1; if (neglstart == neglend) snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", neglstart + 1); else snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d-%d ]", neglstart + 1, neglend + 1); snprintf(c->host, sizeof(c->host), "cloaked"); /* TTL shown in the RTT row (dim color) — avoids a separate sub line */ snprintf(c->rtt, sizeof(c->rtt), neglstart == neglend ? "TTL %d" : "TTLs %d-%d", neglstart + 1, neglend + 1); c->w = wa_utf8_display_width(c->hdr); if (wa_utf8_display_width(c->host) > c->w) c->w = wa_utf8_display_width(c->host); if (wa_utf8_display_width(c->rtt) > c->w) c->w = wa_utf8_display_width(c->rtt); neglstart = neglend = -1; } SLIST_FOREACH(tp, &(proto_sess->hop_info[hopno].packets), next_by_hop) { if (!tp->recv.tv_sec) continue; if (hopno <= maxhop) icmpcode = tp->icmp_type; if (tp->last_hop.s_addr == tp->hopaddr.s_addr) continue; if (ncols >= WA_MAX_COLS) break; c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->anomtype = anomtype; if (icmpcode == -1) { c->is_target = 1; found_target = 1; snprintf(c->hdr, sizeof(c->hdr), "[ TARGET ]"); if (proto_sess->target_open > 0) snprintf(c->ann, sizeof(c->ann), "[open]"); else if (proto_sess->target_filtered) snprintf(c->ann, sizeof(c->ann), "[filtered]"); else if (proto_sess->protocol == 2 || proto_sess->protocol == 3) snprintf(c->ann, sizeof(c->ann), "[replied]"); else snprintf(c->ann, sizeof(c->ann), "[closed]"); } else { snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", hopno + 1); if (anomtype == 1) snprintf(c->ann, sizeof(c->ann), "\xe2\x87\xb6 Stateful FW"); else if (anomtype == 2) snprintf(c->ann, sizeof(c->ann), "\xe2\x9a\x91 Flag FW"); else if (anomtype == 3) snprintf(c->ann, sizeof(c->ann), "\xe2\x87\x84 BSD Bug"); else if (hopno == asseam_hopno) snprintf(c->ann, sizeof(c->ann), "\xe2\x95\xb3 ASN Seam"); else if (hopno == netseam_hopno) snprintf(c->ann, sizeof(c->ann), "\xe2\x95\xb3 NET Seam"); } { /* When hostname display is on and we have a resolved name, * show hostname as the primary label and IP as the sub-line — * exactly matching the ghost-column behavior so a hop never * flickers between IP and hostname as it alternates live/ghost. */ const watch_hop_t *wh_live = (hopno >= 0 && hopno < WATCH_MAX_HOPS) ? &ws->hops[hopno] : NULL; /* Candlestick chart always shows the IP address — hostnames * only appear in the timeline table below. This drops the * sub-line entirely, saving one chart row. */ snprintf(c->host, sizeof(c->host), "%s", inet_ntoa(tp->hopaddr)); c->sub[0] = '\0'; (void)wh_live; } if (c->is_target && (proto_sess->protocol < 2 || proto_sess->protocol > 3)) { size_t hlen = strlen(c->host); snprintf(c->host + hlen, sizeof(c->host) - hlen, ":%d", proto_sess->dport); } /* No sub fallback: we intentionally show IP-only in the chart pane */ /* ASN / NETNAME deliberately left empty — they render in the * timeline table below. Populating them here caused them to * flicker in and out between cycles depending on pWhoIs cache * state, which made the chart look unstable. */ c->asn[0] = '\0'; c->net[0] = '\0'; /* RTT — prefer watch session's rolling stats for chart stability */ { int wi = hopno - proto_sess->ttl_min; if (wi >= 0 && wi < ws->n_hops && ws->hops[wi].cycles_seen > 0) { /* Use rolling watch stats for smoother multi-cycle display */ c->stats.n = ws->hops[wi].cycles_seen; c->stats.rtt_min = ws->hops[wi].rtt_min; c->stats.rtt_avg = ws->hops[wi].rtt_avg; c->stats.rtt_max = ws->hops[wi].rtt_max; c->stats.rtt_stddev = 0.0; snprintf(c->rtt, sizeof(c->rtt), "%d/%d/%dms", (int)round(c->stats.rtt_min), (int)round(c->stats.rtt_avg), (int)round(c->stats.rtt_max)); } else if (proto_sess->rtt_stats) { c->stats = wa_compute_rtt_stats(proto_sess, hopno); if (c->stats.n > 0) snprintf(c->rtt, sizeof(c->rtt), "%d/%d/%dms", (int)round(c->stats.rtt_min), (int)round(c->stats.rtt_avg), (int)round(c->stats.rtt_max)); } else if (tp->recv.tv_sec) { double rtt = timediff_ms(tp->sent, tp->recv); c->stats.n = 1; c->stats.rtt_min = c->stats.rtt_avg = c->stats.rtt_max = rtt; snprintf(c->rtt, sizeof(c->rtt), "%.1fms", rtt); } } if (proto_sess->pmtud && proto_sess->hop_info[hopno].path_mtu > 0) snprintf(c->mtu, sizeof(c->mtu), "[MTU: %u]", (unsigned)proto_sess->hop_info[hopno].path_mtu); c->w = wa_utf8_display_width(c->hdr); if (wa_utf8_display_width(c->host) > c->w) c->w = wa_utf8_display_width(c->host); if (wa_utf8_display_width(c->sub) > c->w) c->w = wa_utf8_display_width(c->sub); /* c->asn and c->net intentionally excluded — they no longer render * in the chart, so including them in width calcs would waste space. */ if (wa_utf8_display_width(c->rtt) > c->w) c->w = wa_utf8_display_width(c->rtt); if (wa_utf8_display_width(c->ann) > c->w) c->w = wa_utf8_display_width(c->ann); if (wa_utf8_display_width(c->mtu) > c->w) c->w = wa_utf8_display_width(c->mtu); if (found_target) break; } if (found_target) break; } /* trailing hole */ if (neglstart != -1 && ncols < WA_MAX_COLS) { wa_col_t *c = &cols[ncols++]; memset(c, 0, sizeof(*c)); c->is_hole = 1; if (neglstart == neglend) snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d ]", neglstart + 1); else snprintf(c->hdr, sizeof(c->hdr), "[ HOP %d-%d ]", neglstart + 1, neglend + 1); snprintf(c->host, sizeof(c->host), "cloaked"); /* TTL shown in the RTT row (dim color) — avoids a separate sub line */ snprintf(c->rtt, sizeof(c->rtt), neglstart == neglend ? "TTL %d" : "TTLs %d-%d", neglstart + 1, neglend + 1); c->w = wa_utf8_display_width(c->hdr); if (wa_utf8_display_width(c->host) > c->w) c->w = wa_utf8_display_width(c->host); if (wa_utf8_display_width(c->rtt) > c->w) c->w = wa_utf8_display_width(c->rtt); } if (ncols == 0) return 0; /* ---- cap column widths at WA_MAX_COL_W ---- * The hostname is the only field that can realistically push a column * beyond 17 display columns; truncate it from the right so the label * row stays within the cap. All other fields (hdr, rtt, ann, mtu) are * fixed-format strings that are ≤17 chars in practice. */ { int ci; for (ci = 0; ci < ncols; ci++) { if (cols[ci].w > WA_MAX_COL_W) { wa_utf8_truncate_to(cols[ci].host, WA_MAX_COL_W); cols[ci].w = WA_MAX_COL_W; } } } /* ---- global RTT bounds + jitter detection ---- */ double g_rtt_min = 0.0, g_rtt_max = 0.0; { int g_found = 0, gi; double max_jitter = 0.0; int jitter_col = -1; for (gi = 0; gi < ncols; gi++) { if (cols[gi].stats.n > 0) { if (!g_found || cols[gi].stats.rtt_min < g_rtt_min) g_rtt_min = cols[gi].stats.rtt_min; if (!g_found || cols[gi].stats.rtt_max > g_rtt_max) g_rtt_max = cols[gi].stats.rtt_max; g_found = 1; double jitter = cols[gi].stats.rtt_max - cols[gi].stats.rtt_min; if (jitter > max_jitter) { max_jitter = jitter; jitter_col = gi; } } } if (jitter_col >= 0 && max_jitter > WA_JITTER_WARN) { int jms = (int)round(max_jitter); cols[jitter_col].jitter_warn = 1; snprintf(cols[jitter_col].jitter_ann, sizeof(cols[jitter_col].jitter_ann), "\xe2\x9a\xa1 %dms \xce\x94", jms); if (cols[jitter_col].ann[0] == '\0') snprintf(cols[jitter_col].ann, sizeof(cols[jitter_col].ann), "\xe2\x9a\xa1 jitter %dms", jms); if (wa_utf8_display_width(cols[jitter_col].jitter_ann) > cols[jitter_col].w) cols[jitter_col].w = wa_utf8_display_width(cols[jitter_col].jitter_ann); if (wa_utf8_display_width(cols[jitter_col].ann) > cols[jitter_col].w) cols[jitter_col].w = wa_utf8_display_width(cols[jitter_col].ann); } } /* ---- x-axis info string ---- */ char xa_info[256] = ""; { double tgt_min = 0.0, tgt_max = 0.0; int found_tgt = 0, gi; int n_hops = ncols - 1; const char *tgt_name = inet_ntoa(proto_sess->remote_address); char tgt_url[80]; switch (proto_sess->protocol) { case 1: snprintf(tgt_url, sizeof(tgt_url), "udp://%s:%d", tgt_name, proto_sess->dport); break; case 2: case 3: snprintf(tgt_url, sizeof(tgt_url), "icmp://%s", tgt_name); break; default: snprintf(tgt_url, sizeof(tgt_url), "tcp://%s:%d", tgt_name, proto_sess->dport); break; } for (gi = 0; gi < ncols; gi++) { if (cols[gi].is_target && cols[gi].stats.n > 0) { tgt_min = cols[gi].stats.rtt_min; tgt_max = cols[gi].stats.rtt_max; found_tgt = 1; break; } } if (found_tgt) snprintf(xa_info, sizeof(xa_info), "End-to-End RTT %d\xe2\x80\x93%dms, %d hops to %s", (int)round(tgt_min), (int)round(tgt_max), n_hops, tgt_url); else snprintf(xa_info, sizeof(xa_info), "%d hops traced, target unreachable", n_hops); } /* Y-axis prefix width */ { char ybuf[16]; snprintf(ybuf, sizeof(ybuf), "%dms", (int)ceil(g_rtt_max > 0 ? g_rtt_max : 1.0)); int y_prefix_w = (int)strlen(ybuf) + 3; int effective_w = term_cols - y_prefix_w; if (effective_w < 20) effective_w = 20; /* ---- render column slices ---- */ int start = 0; while (start < ncols && ctx.row < ctx.max_row) { int end = start; int used = cols[start].w; while (end + 1 < ncols) { int needed = used + WA_CHART_SEP + cols[end + 1].w; if (needed > effective_w) break; used = needed; end++; } /* blank row before each slice */ if (ctx.row < ctx.max_row) { clrtoeol(); wa_nl(&ctx); } /* candlestick chart — no x-axis RTT label (shown in top border) */ wa_chart_render(&ctx, cols, start, end, g_rtt_min, g_rtt_max, ""); if (ctx.row >= ctx.max_row) break; /* ---- header row (hop labels, bottom — outer corners └ / ┘) ---- */ if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); wa_render_header_row(&ctx, cols, start, end, 1); if (ctx.row >= ctx.max_row) break; /* ---- hostname row ---- */ if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); { int ii; for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *c = &cols[ii]; if (c->is_hole) wa_color_on(CP_NORMAL, A_DIM); else if (c->is_target) wa_color_on(CP_NORMAL, A_BOLD); else if (c->anomtype) wa_color_on(CP_RED, 0); else if (c->is_source) wa_color_on(CP_NORMAL, A_BOLD); wa_center(&ctx, c->host, c->w); if (c->is_hole) wa_color_off(CP_NORMAL, A_DIM); else if (c->is_target) wa_color_off(CP_NORMAL, A_BOLD); else if (c->anomtype) wa_color_off(CP_RED, 0); else if (c->is_source) wa_color_off(CP_NORMAL, A_BOLD); if (ii < end) wa_str(&ctx, " -> "); } } wa_nl(&ctx); if (ctx.row >= ctx.max_row) break; /* ---- alternate IP rows (multipath) ---- */ /* Show one row per alternate IP level across all columns in this slice. * Stats/bar already combine all paths; these lines just identify them. */ { int alt_idx; for (alt_idx = 0; alt_idx < WATCH_MAX_ALT_IPS; alt_idx++) { int ii, any = 0; for (ii = start; ii <= end; ii++) if (alt_idx < cols[ii].n_alt) { any = 1; break; } if (!any) break; if (ctx.row >= ctx.max_row) break; if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *ca = &cols[ii]; const char *alt = (alt_idx < ca->n_alt) ? ca->alt_hosts[alt_idx] : ""; if (alt[0]) wa_color_on(CP_STATUS, A_DIM); wa_center(&ctx, alt, ca->w); if (alt[0]) wa_color_off(CP_STATUS, A_DIM); if (ii < end) wa_repeat_ch(&ctx, ' ', WA_CHART_SEP); } wa_nl(&ctx); } } if (ctx.row >= ctx.max_row) break; /* ---- sub line (IP in parens or TTL for holes) ---- */ { int ii, any = 0; for (ii = start; ii <= end; ii++) if (cols[ii].sub[0]) { any=1; break; } if (any) { if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *c = &cols[ii]; if (c->is_hole) wa_color_on(CP_NORMAL, A_DIM); wa_center(&ctx, c->sub, c->w); if (c->is_hole) wa_color_off(CP_NORMAL, A_DIM); if (ii < end) wa_repeat_ch(&ctx, ' ', WA_CHART_SEP); } wa_nl(&ctx); if (ctx.row >= ctx.max_row) break; } } /* ---- ASN line ---- */ { int ii, any = 0; for (ii = start; ii <= end; ii++) if (cols[ii].asn[0]) { any=1; break; } if (any) { if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *c = &cols[ii]; if (c->asn[0]) wa_color_on(c->is_hole ? CP_NORMAL : CP_YELLOW, c->is_hole ? A_DIM : 0); wa_center(&ctx, c->asn, c->w); if (c->asn[0]) wa_color_off(c->is_hole ? CP_NORMAL : CP_YELLOW, c->is_hole ? A_DIM : 0); if (ii < end) wa_repeat_ch(&ctx, ' ', WA_CHART_SEP); } wa_nl(&ctx); if (ctx.row >= ctx.max_row) break; } } /* ---- net name line ---- */ { int ii, any = 0; for (ii = start; ii <= end; ii++) if (cols[ii].net[0]) { any=1; break; } if (any) { if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *c = &cols[ii]; if (c->net[0]) wa_color_on(CP_YELLOW, 0); wa_center(&ctx, c->net, c->w); if (c->net[0]) wa_color_off(CP_YELLOW, 0); if (ii < end) wa_repeat_ch(&ctx, ' ', WA_CHART_SEP); } wa_nl(&ctx); if (ctx.row >= ctx.max_row) break; } } /* ---- RTT line ---- */ { int ii, any = 0; for (ii = start; ii <= end; ii++) if (cols[ii].rtt[0]) { any=1; break; } if (any) { if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *c = &cols[ii]; if (c->rtt[0]) wa_color_on(c->is_hole ? CP_NORMAL : CP_GREEN, c->is_hole ? A_DIM : 0); wa_center(&ctx, c->rtt, c->w); if (c->rtt[0]) wa_color_off(c->is_hole ? CP_NORMAL : CP_GREEN, c->is_hole ? A_DIM : 0); if (ii < end) wa_repeat_ch(&ctx, ' ', WA_CHART_SEP); } wa_nl(&ctx); if (ctx.row >= ctx.max_row) break; } } /* ---- annotation line ---- */ { int ii, any = 0; for (ii = start; ii <= end; ii++) if (cols[ii].ann[0]) { any=1; break; } if (any) { if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *c = &cols[ii]; if (c->ann[0]) { if (c->is_target) { if (proto_sess->target_open > 0) wa_color_on(CP_GREEN, A_BOLD); else if (proto_sess->target_filtered) wa_color_on(CP_YELLOW, 0); else if (proto_sess->protocol == 2 || proto_sess->protocol == 3) wa_color_on(CP_GREEN, 0); else wa_color_on(CP_RED, A_BOLD); } else if (c->anomtype > 0) { wa_color_on(CP_RED, 0); } else { wa_color_on(CP_ORANGE, 0); } } wa_center(&ctx, c->ann, c->w); if (c->ann[0]) { if (c->is_target) { if (proto_sess->target_open > 0) wa_color_off(CP_GREEN, A_BOLD); else if (proto_sess->target_filtered) wa_color_off(CP_YELLOW, 0); else if (proto_sess->protocol == 2 || proto_sess->protocol == 3) wa_color_off(CP_GREEN, 0); else wa_color_off(CP_RED, A_BOLD); } else if (c->anomtype > 0) { wa_color_off(CP_RED, 0); } else { wa_color_off(CP_ORANGE, 0); } } if (ii < end) wa_repeat_ch(&ctx, ' ', WA_CHART_SEP); } wa_nl(&ctx); if (ctx.row >= ctx.max_row) break; } } /* ---- MTU line ---- */ if (proto_sess->pmtud) { int ii, any = 0; for (ii = start; ii <= end; ii++) if (cols[ii].mtu[0]) { any=1; break; } if (any) { if (y_prefix_w > 0) wa_printf(&ctx, "%*s", y_prefix_w, ""); for (ii = start; ii <= end && ctx.col < ctx.term_cols - 1; ii++) { wa_col_t *c = &cols[ii]; if (c->mtu[0]) wa_color_on(CP_YELLOW, 0); wa_center(&ctx, c->mtu, c->w); if (c->mtu[0]) wa_color_off(CP_YELLOW, 0); if (ii < end) wa_repeat_ch(&ctx, ' ', WA_CHART_SEP); } wa_nl(&ctx); if (ctx.row >= ctx.max_row) break; } } start = end + 1; } } return ctx.row - start_row; } /* ---- View name label for the right side of the top border ---- */ static const char *watch_view_name(int view) { switch (view) { case 2: return "Bars"; case 3: return "Status Board"; default: return "Candlestick"; } } /* ---- Shared top border row (row 0) for all views ---- * Left: ┌──┤ Layer Four Traceroute (LFT) ├ * Middle: [dash_l]┤ rtt_info ├[dash_r] (omitted when rtt_info NULL/empty) * Right: ┤ ViewName ├──┐ * * When RTT is present, it is centered in the space between LFT and VIEW * sections (equal dash_l / dash_r split). */ /* Build the centre RTT/hop summary string for the top border. * Used by all three views so they share identical header content. */ static void watch_build_rtt_info(watch_session_t *ws, char *buf, int sz) { int i, last_hop = -1; for (i = ws->n_hops - 1; i >= 0; i--) { if (ws->hops[i].rtt_avg > 0.0) { last_hop = i; break; } } if (last_hop >= 0) { snprintf(buf, (size_t)sz, "End-to-End RTT %d\xe2\x80\x93%dms, %d hops to %s", (int)round(ws->hops[last_hop].rtt_min), (int)round(ws->hops[last_hop].rtt_max), ws->n_hops, ws->tgt_label); } else { snprintf(buf, (size_t)sz, "%s %d hops", ws->tgt_label, ws->n_hops); } } static void watch_render_top_border(watch_session_t *ws, const char *rtt_info) { const char *lft_label = "Layer Four Traceroute (LFT)"; const char *view_label = watch_view_name(ws->current_view); int lft_dw = wa_utf8_display_width(lft_label); int view_dw = wa_utf8_display_width(view_label); /* ┌──┤ space LFT space ├ = lft_dw + 7 */ int lft_end = lft_dw + 7; /* ┤ space VIEW space ├──┐ = view_dw + 7 */ int view_end = view_dw + 7; int i; /* Macros for switching between frame (blue box-drawing) and text (cyan) * color pairs so we can draw a mixed-color top border in one pass. */ #define FRAME_ON() do { if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); } while (0) #define FRAME_OFF() do { if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); } while (0) #define TEXT_ON() do { if (has_colors()) attron(COLOR_PAIR(CP_SENT) | A_BOLD); } while (0) #define TEXT_OFF() do { if (has_colors()) attroff(COLOR_PAIR(CP_SENT) | A_BOLD); } while (0) move(0, 0); FRAME_ON(); addstr("\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80"); /* ┌── */ addstr("\xe2\x94\xa4"); /* ┤ */ FRAME_OFF(); TEXT_ON(); addstr(" "); addstr(lft_label); addstr(" "); TEXT_OFF(); FRAME_ON(); addstr("\xe2\x94\x9c"); /* ├ */ if (rtt_info && rtt_info[0]) { int info_dw = wa_utf8_display_width(rtt_info); int rtt_sec = info_dw + 4; /* ┤ space info space ├ */ int total = ws->term_cols - lft_end - rtt_sec - view_end; if (total < 0) total = 0; int dash_l = (ws->term_cols - rtt_sec) / 2 - lft_end; if (dash_l < 0) dash_l = 0; if (dash_l > total) dash_l = total; int dash_r = total - dash_l; for (i = 0; i < dash_l; i++) addstr("\xe2\x94\x80"); /* ─ */ addstr("\xe2\x94\xa4"); /* ┤ */ FRAME_OFF(); TEXT_ON(); addstr(" "); addstr(rtt_info); addstr(" "); TEXT_OFF(); FRAME_ON(); addstr("\xe2\x94\x9c"); /* ├ */ for (i = 0; i < dash_r; i++) addstr("\xe2\x94\x80"); /* ─ */ } else { int total = ws->term_cols - lft_end - view_end; if (total < 0) total = 0; for (i = 0; i < total; i++) addstr("\xe2\x94\x80"); /* ─ */ } addstr("\xe2\x94\xa4"); /* ┤ */ FRAME_OFF(); TEXT_ON(); addstr(" "); addstr(view_label); addstr(" "); TEXT_OFF(); FRAME_ON(); addstr("\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80"); /* ├── */ addstr("\xe2\x94\x90"); /* ┐ */ FRAME_OFF(); #undef FRAME_ON #undef FRAME_OFF #undef TEXT_ON #undef TEXT_OFF } static void watch_render_view1(watch_session_t *ws, lft_session_params *proto_sess) { int chart_max, chart_rows_used; int div_row, bot_row, timeline_start, timeline_rows; int r, i; /* Framed layout: * row 0 : ┌──┤ RTT info ├──────────────────────────────────────────┐ * rows 1..N : │ candlestick chart │ * row N+1 : ├──────────────────────────────────────────────────────────┤ * rows N+2..M : │ rolling timeline table │ * row M+1 (=bot) : └──────────────────────────────────────────────────────────┘ * row M+2 : status bar row 1 * row M+3 : status bar row 2 */ bot_row = ws->term_rows - 3; /* bottom border row (above status bar) */ chart_max = (bot_row - 1) * 6 / 10; /* ~60% of usable rows for chart */ if (chart_max < 4) chart_max = 4; clear(); /* ------------------------------------------------------------------ */ /* Compute end-to-end RTT for the top border title */ /* ------------------------------------------------------------------ */ char rtt_info[256]; watch_build_rtt_info(ws, rtt_info, (int)sizeof(rtt_info)); /* ------------------------------------------------------------------ */ /* Row 0: top border */ /* ------------------------------------------------------------------ */ watch_render_top_border(ws, rtt_info); if (proto_sess == NULL || ws->n_hops == 0) { mvprintw(2, 2, "Collecting initial trace data..."); return; } /* ------------------------------------------------------------------ */ /* Chart: rows 1..chart_max, content at col 1..term_cols-2. The */ /* chart is given a FIXED slot of chart_max rows whether it uses all */ /* of them or not — that way the divider row (and the timeline table */ /* below it) stays put when the number of hops/paths changes between */ /* cycles. Without this, a growing candlestick bumps the whole table */ /* downward on each cycle and vice versa. */ /* ------------------------------------------------------------------ */ chart_rows_used = watch_render_ascii_chart(ws, proto_sess, 1, chart_max, 1); (void)chart_rows_used; /* ------------------------------------------------------------------ */ /* Divider: ├──────────────────────────────────────────────────────────┤ */ /* Fixed at row (1 + chart_max) regardless of actual chart height. */ /* ------------------------------------------------------------------ */ div_row = 1 + chart_max; if (div_row >= bot_row) div_row = bot_row - 1; if (div_row > 0 && div_row < ws->term_rows - 2) { if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); move(div_row, 0); addstr("\xe2\x94\x9c"); /* ├ */ for (i = 1; i < ws->term_cols - 1; i++) addstr("\xe2\x94\x80"); /* ─ */ addstr("\xe2\x94\xa4"); /* ┤ */ if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); } timeline_start = div_row + 1; /* ------------------------------------------------------------------ */ /* Timeline: rows timeline_start..bot_row, content at col 1..end-2 */ /* The table's bottom border is the status bar itself (├──...──┤). */ /* ------------------------------------------------------------------ */ timeline_rows = bot_row - timeline_start + 1; if (timeline_rows >= 2) watch_render_timeline(ws, timeline_start, timeline_rows, 1); /* ------------------------------------------------------------------ */ /* Left │ and right │ borders for each content row */ /* The chart and timeline render with col_offset=1 / start_col=1, so */ /* col 0 and col term_cols-1 are free for the borders. Borders extend */ /* all the way down to bot_row; the status bar row below acts as the */ /* closing horizontal via ├── ... ──┤ connectors. */ /* ------------------------------------------------------------------ */ if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); /* Use ncurses native ACS_VLINE + mvvline() for continuous solid sides, * in two segments so the divider row keeps its ├ and ┤ connectors. */ if (div_row > 1) mvvline(1, 0, ACS_VLINE, div_row - 1); if (bot_row > div_row) mvvline(div_row + 1, 0, ACS_VLINE, bot_row - div_row); if (div_row > 1) mvvline(1, ws->term_cols - 1, ACS_VLINE, div_row - 1); if (bot_row > div_row) mvvline(div_row + 1, ws->term_cols - 1, ACS_VLINE, bot_row - div_row); if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); (void)r; /* no longer used */ } /* ========================================================================= * View 2: A+B hybrid — native bar chart (top) + timeline table (bottom) * ========================================================================= */ /* * Native ncurses RTT bar chart — drawn directly from ws->hops[] data. * Uses Unicode block characters and ncurses color pairs for consistent * styling with the timeline table below. * Returns the number of rows consumed. */ static int watch_render_native_chart(watch_session_t *ws, int start_row, int max_rows, int col_offset) { int i, r; double g_max = 0.0; int n = ws->n_hops; int y_lbl_w = 8; /* "9999ms |" */ int bar_rows, avail_cols, col_w, bar_w; int hop_row, ip_row; int right_edge = ws->term_cols - col_offset; /* exclusive right bound */ if (n <= 0 || max_rows < 4) return 0; /* Find global RTT max across hops that have data (ignore 100% loss hops) */ for (i = 0; i < n; i++) { double lp = (ws->hops[i].cycles_total > 0) ? 100.0 * (ws->hops[i].cycles_total - ws->hops[i].cycles_seen) / ws->hops[i].cycles_total : 100.0; if (lp < 100.0 && ws->hops[i].rtt_max > g_max) g_max = ws->hops[i].rtt_max; } if (g_max <= 0.0) g_max = 100.0; g_max *= 1.15; /* 15% headroom so bars don't clip the top */ /* Row allocation: bar area + 2 label rows (hop number + IP) */ bar_rows = max_rows - 2; if (bar_rows < 2) bar_rows = 2; avail_cols = right_edge - col_offset - y_lbl_w; if (avail_cols < 1) avail_cols = 1; col_w = avail_cols / n; if (col_w < 1) col_w = 1; if (col_w > WA_MAX_COL_W) col_w = WA_MAX_COL_W; /* keep label rows readable */ bar_w = (col_w > 1) ? col_w - 1 : 1; /* 1-char gap between hop columns */ /* Draw bar rows top→bottom (row 0 = g_max ms, row bar_rows-1 = 0 ms) */ for (r = 0; r < bar_rows; r++) { int screen_row = start_row + r; double rtt_at_row; if (screen_row >= ws->term_rows - 2) break; move(screen_row, 0); clrtoeol(); /* Y-axis label: only at top, midpoint, and bottom rows */ if (has_colors()) attron(COLOR_PAIR(CP_STATUS)); rtt_at_row = g_max * (bar_rows - 1 - r) / (double)(bar_rows > 1 ? bar_rows - 1 : 1); if (r == 0) mvprintw(screen_row, col_offset, "%5.0fms |", g_max); else if (r == bar_rows / 2) mvprintw(screen_row, col_offset, "%5.0fms |", rtt_at_row); else if (r == bar_rows - 1) mvprintw(screen_row, col_offset, " 0ms |"); else mvprintw(screen_row, col_offset, " "); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS)); /* Per-hop bar segments */ for (i = 0; i < n; i++) { watch_hop_t *h = &ws->hops[i]; double lp = (h->cycles_total > 0) ? 100.0 * (h->cycles_total - h->cycles_seen) / h->cycles_total : 100.0; int cx = col_offset + y_lbl_w + i * col_w; int w = (cx + bar_w <= right_edge) ? bar_w : right_edge - cx; int fill_top, max_top, j; if (cx >= right_edge) break; if (w < 1) w = 1; move(screen_row, cx); if (lp >= 100.0) { /* Cloaked hop: loss colour asterisk at the bottom row, blank above */ if (r == bar_rows - 1) { if (has_colors()) attron(COLOR_PAIR(CP_MAGENTA) | A_BOLD); addstr("*"); if (has_colors()) attroff(COLOR_PAIR(CP_MAGENTA) | A_BOLD); } else { printw("%*s", w, ""); } continue; } /* bar fills from avg RTT down; max-RTT mark appears above it */ fill_top = bar_rows - 1 - (int)(h->rtt_avg / g_max * (bar_rows - 1) + 0.5); max_top = bar_rows - 1 - (int)(h->rtt_max / g_max * (bar_rows - 1) + 0.5); if (fill_top < 0) fill_top = 0; if (max_top < 0) max_top = 0; if (max_top > fill_top) max_top = fill_top; if (r >= fill_top) { /* Filled bar (avg RTT height and below) */ if (has_colors()) attron(COLOR_PAIR(rtt_color(h->rtt_avg))); for (j = 0; j < w; j++) addstr("\xe2\x96\x88"); /* █ */ if (has_colors()) attroff(COLOR_PAIR(rtt_color(h->rtt_avg))); } else if (r == max_top && max_top < fill_top) { /* Max-RTT cap mark (▄) just above the filled bar */ if (has_colors()) attron(COLOR_PAIR(rtt_color(h->rtt_max))); addstr("\xe2\x96\x84"); /* ▄ */ if (has_colors()) attroff(COLOR_PAIR(rtt_color(h->rtt_max))); } else { printw("%*s", w, ""); } } } /* Hop-number label row */ hop_row = start_row + bar_rows; if (hop_row < ws->term_rows - 2) { move(hop_row, 0); clrtoeol(); if (has_colors()) attron(COLOR_PAIR(CP_STATUS) | A_BOLD); mvprintw(hop_row, col_offset, "%8s", ""); for (i = 0; i < n; i++) { int cx = col_offset + y_lbl_w + i * col_w; if (cx >= right_edge) break; move(hop_row, cx); printw("%-*d", col_w < 4 ? col_w : 3, i + 1); } if (has_colors()) attroff(COLOR_PAIR(CP_STATUS) | A_BOLD); } /* IP/host label row */ ip_row = start_row + bar_rows + 1; if (ip_row < ws->term_rows - 2) { move(ip_row, 0); clrtoeol(); for (i = 0; i < n; i++) { watch_hop_t *h = &ws->hops[i]; int cx = col_offset + y_lbl_w + i * col_w; int trunc_w = col_w > 1 ? col_w - 1 : 1; char trunc[64]; if (cx >= right_edge) break; if (trunc_w > (int)sizeof(trunc) - 1) trunc_w = (int)sizeof(trunc) - 1; /* Bar chart always shows the IP — hostnames only appear in * the timeline table below, so 'h' only affects that. */ snprintf(trunc, sizeof(trunc), "%-.*s", trunc_w, h->host[0] ? h->host : "*"); move(ip_row, cx); if (has_colors()) attron(COLOR_PAIR(rtt_color(h->rtt_avg))); addstr(trunc); if (has_colors()) attroff(COLOR_PAIR(rtt_color(h->rtt_avg))); } } return max_rows; } /* Render the rolling timeline table into rows [start_row .. start_row+max_rows). * col_offset: left margin (0 = full-width, 1 = inside a framed border). */ static int watch_render_timeline(watch_session_t *ws, int start_row, int max_rows, int col_offset) { int i, row = start_row; int hist_cols; /* history sparkline cells after fixed columns */ int fixed_w; /* width of fixed columns (HOP … JITTER) */ int host_w; /* HOST column: auto-widened to fit longest name */ int whois_w = 0; /* ASN(8) + NETNAME(16) [+ ORGNAME(20)] */ int show_orgname = 0; int row_end = start_row + max_rows - 1; /* right border column (content must stop before this) */ int right_col = ws->term_cols - (col_offset > 0 ? 1 : 0); if (row >= ws->term_rows - 2) return 0; /* ------------------------------------------------------------------ */ /* Compute host_w: widest hop address/name, clamped to fit. */ /* To avoid horizontal layout shift when the user presses 'h' to */ /* toggle hostnames, host_w is the max of the IP width AND the */ /* hostname width (regardless of current show_hostnames) — so the */ /* column budget is identical in both states. */ /* ------------------------------------------------------------------ */ host_w = 15; /* minimum */ for (i = 0; i < ws->n_hops; i++) { const char *ip = ws->hops[i].host[0] ? ws->hops[i].host : "*"; const char *name = ws->hops[i].hostname[0] ? ws->hops[i].hostname : ip; int w_ip = (int)strlen(ip); int w_name = (int)strlen(name); int w = (w_ip > w_name) ? w_ip : w_name; /* Target hop gets a state tag appended — budget 11 extra chars. */ if (i == ws->target_hopno && ws->target_hopno > 0) w += 11; if (w > host_w) host_w = w; } if (host_w > 64) host_w = 64; /* sanity cap */ /* Whois column widths */ if (ws->whois_enabled) { whois_w = 8 + 2 + 16 + 2; /* ORGNAME if the terminal is wide enough (evaluated after host_w) */ if (ws->term_cols - col_offset >= 1 + 3 + 2 + host_w + 2 + 5 + 2 + 6 + 2 + 6 + 2 + 6 + 2 + 4 + 2 + whois_w + 20 + 2 + 4) { whois_w += 20 + 2; show_orgname = 1; } } /* Fixed column width (everything except the sparkline) */ fixed_w = 1 + 3 + 2 + host_w + 2 + 5 + 2 + 6 + 2 + 6 + 2 + 6 + 2 + 4 + 2; /* If host_w makes fixed columns too wide, shrink host_w to fit. */ { int min_hist = 8; /* keep at least 8 sparkline cells */ int max_fixed = right_col - col_offset - whois_w - min_hist - 2; if (fixed_w > max_fixed && max_fixed > 1 + 3 + 2 + 15 + 2 + 5 + 2 + 6 + 2 + 6 + 2 + 6 + 2 + 4 + 2) { fixed_w = max_fixed; host_w = fixed_w - (1 + 3 + 2 + 2 + 5 + 2 + 6 + 2 + 6 + 2 + 6 + 2 + 4 + 2); if (host_w < 15) host_w = 15; /* recompute fixed_w with clamped host_w */ fixed_w = 1 + 3 + 2 + host_w + 2 + 5 + 2 + 6 + 2 + 6 + 2 + 6 + 2 + 4 + 2; } } hist_cols = (right_col - col_offset) - fixed_w - whois_w - 2; if (hist_cols < 0) hist_cols = 0; /* ------------------------------------------------------------------ */ /* Header row — column labels left, sparkline legend right */ /* ------------------------------------------------------------------ */ move(row, col_offset); clrtoeol(); if (has_colors()) attron(COLOR_PAIR(CP_TBLHEAD)); if (ws->whois_enabled && show_orgname) mvprintw(row, col_offset, " %-3s %-*s %-5s %-6s %-6s %-6s %-4s %-8.8s %-16.16s %-20.20s", "HOP", host_w, "HOST", "LOSS%", "MIN", "AVG", "MAX", "JIT", "ASN", "NETNAME", "ORGNAME"); else if (ws->whois_enabled) mvprintw(row, col_offset, " %-3s %-*s %-5s %-6s %-6s %-6s %-4s %-8.8s %-16.16s", "HOP", host_w, "HOST", "LOSS%", "MIN", "AVG", "MAX", "JIT", "ASN", "NETNAME"); else mvprintw(row, col_offset, " %-3s %-*s %-5s %-6s %-6s %-6s %-4s", "HOP", host_w, "HOST", "LOSS%", "MIN", "AVG", "MAX", "JIT"); if (has_colors()) attroff(COLOR_PAIR(CP_TBLHEAD)); /* Sparkline legend: ▪= 24) { /* Legend total: "▪ col_offset) { move(row, leg_col); if (has_colors()) { attron(COLOR_PAIR(CP_GREEN)); } addstr("\xe2\x96\xaa"); /* ▪ green */ if (has_colors()) { attroff(COLOR_PAIR(CP_GREEN)); attron(COLOR_PAIR(CP_NORMAL) | A_DIM); } addstr(" 0 && row <= row_end && row < ws->term_rows - 2) { int span_sec = hist_cols * (ws->interval > 0 ? ws->interval : 1); int step; int nt; int i; if (span_sec <= 30) step = 5; else if (span_sec <= 150) step = 30; else if (span_sec <= 600) step = 60; else if (span_sec <= 3000) step = 300; else step = 900; nt = span_sec / step + 1; if (nt > 4) nt = 4; if (nt < 2) nt = 2; move(row, col_offset); clrtoeol(); /* Use dim default-fg (grey) rather than dim cyan — on many terminals * dim cyan renders with a greenish tint, which the user flagged. */ if (has_colors()) attron(COLOR_PAIR(CP_NORMAL) | A_DIM); /* Pad the fixed columns to keep sparkline area aligned */ mvprintw(row, col_offset, "%*s", fixed_w + (ws->whois_enabled ? whois_w : 0), ""); /* Draw the dashed scale line across the sparkline area */ move(row, right_col - hist_cols); for (i = 0; i < hist_cols; i++) addstr("\xe2\x94\x80"); /* ─ U+2500 */ /* Helper: format an age (seconds) into a compact label */ #define WATCH_FMT_AGE(age_, lbl_) do { \ if ((age_) == 0) snprintf((lbl_), sizeof(lbl_), "current"); \ else if ((age_) >= 3600) { \ int _h = (age_) / 3600, _m = ((age_) % 3600) / 60; \ if (_m == 0) snprintf((lbl_), sizeof(lbl_), "%dh", _h); \ else snprintf((lbl_), sizeof(lbl_), "%dh%dm", _h, _m); \ } else if ((age_) >= 60) snprintf((lbl_), sizeof(lbl_), "%dm", (age_)/60); \ else snprintf((lbl_), sizeof(lbl_), "%ds", (age_)); \ } while (0) /* Left-edge anchor label: the actual total span of the sparkline * (oldest visible sample, not rounded to step). Planted at pos 0. */ { char lbl[16]; char padded[24]; int llen; WATCH_FMT_AGE(span_sec, lbl); snprintf(padded, sizeof(padded), "%s ", lbl); /* flush-left */ llen = (int)strlen(padded); if (llen < hist_cols) mvaddstr(row, right_col - hist_cols, padded); } /* Overlay step-based intermediate labels + right-edge "current". * Left-most step label is suppressed if it would overlap the span * anchor we just planted. */ for (i = 0; i < nt; i++) { int age = (nt - 1 - i) * step; int pos = hist_cols - 1 - (age / (ws->interval > 0 ? ws->interval : 1)); char lbl[16]; char padded[24]; int llen; WATCH_FMT_AGE(age, lbl); /* Leading + trailing space separates label text from dashes */ snprintf(padded, sizeof(padded), " %s ", lbl); llen = (int)strlen(padded); /* Right-align "current" at the rightmost edge */ if (age == 0) pos = hist_cols - llen; else pos = pos - (llen / 2); if (pos < 0) pos = 0; if (pos + llen > hist_cols) pos = hist_cols - llen; if (pos < 0) continue; /* Skip if this would overlap the left-edge span anchor (~10 cols) */ if (age != 0 && pos < 10) continue; mvaddstr(row, right_col - hist_cols + pos, padded); } #undef WATCH_FMT_AGE if (has_colors()) attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); row++; } /* ------------------------------------------------------------------ */ /* Per-hop rows */ /* ------------------------------------------------------------------ */ for (i = 0; i < ws->n_hops && row <= row_end && row < ws->term_rows - 2; i++) { watch_hop_t *h = &ws->hops[i]; double loss_pct = (h->cycles_total > 0) ? 100.0 * (h->cycles_total - h->cycles_seen) / h->cycles_total : 0.0; char host_trunc[72]; /* large enough for host_w (max 64) + state tag */ char loss_s[8], min_s[8], avg_s[8], max_s[8], jitter_s[9]; int col; /* For the target hop, append state annotation */ if (i == ws->target_hopno && ws->target_hopno > 0) { const char *state = ws->target_open ? "[open]" : ws->target_filtered ? "[filtered]" : (ws->protocol == 2 || ws->protocol == 3) ? "[replied]" : "[closed]"; int tag_len = (int)strlen(state) + 1; /* " [TAG]" */ int name_max = host_w - tag_len; if (name_max < 5) name_max = 5; snprintf(host_trunc, sizeof(host_trunc), "%.*s %s", name_max, hop_display_addr(h, ws->show_hostnames), state); } else { snprintf(host_trunc, sizeof(host_trunc), "%.*s", host_w, hop_display_addr(h, ws->show_hostnames)); } if (loss_pct >= 100.0) snprintf(loss_s, sizeof(loss_s), "100%%"); else snprintf(loss_s, sizeof(loss_s), "%d%%", (int)round(loss_pct)); snprintf(min_s, sizeof(min_s), "%.1f", h->rtt_min); snprintf(avg_s, sizeof(avg_s), "%d", (int)round(h->rtt_avg)); snprintf(max_s, sizeof(max_s), "%.1f", h->rtt_max); snprintf(jitter_s, sizeof(jitter_s), "%d", (int)round(h->rtt_jitter)); move(row, col_offset); clrtoeol(); /* HOP number — cyan (same as ASN column values) */ if (has_colors()) attron(COLOR_PAIR(CP_STATUS)); mvprintw(row, col_offset, " %3d ", i + 1); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS)); /* HOST — loss-based color; bold when any loss present */ { int hc = host_color(loss_pct); attr_t ha = (loss_pct > 0) ? (COLOR_PAIR(hc) | A_BOLD) : COLOR_PAIR(hc); if (has_colors()) attron(ha); printw("%-*s ", host_w, host_trunc); if (has_colors()) attroff(ha); } /* LOSS% — 0%=green+bold, <10%=yellow+bold, <50%=red, ≥50%=red+bold */ { attr_t la; if (loss_pct <= 0) la = COLOR_PAIR(CP_GREEN) | A_BOLD; else if (loss_pct < 10) la = COLOR_PAIR(CP_YELLOW) | A_BOLD; else if (loss_pct < 50) la = COLOR_PAIR(CP_RED); else la = COLOR_PAIR(CP_RED) | A_BOLD; if (has_colors()) attron(la); printw("%-5s ", loss_s); if (has_colors()) attroff(la); } /* MIN — dim */ if (has_colors()) attron(COLOR_PAIR(CP_NORMAL) | A_DIM); printw("%-6s ", min_s); if (has_colors()) attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); /* AVG — finer thresholds */ { int ac = avg_color(h->rtt_avg); if (has_colors()) attron(COLOR_PAIR(ac)); printw("%-6s ", avg_s); if (has_colors()) attroff(COLOR_PAIR(ac)); } /* MAX — dim */ if (has_colors()) attron(COLOR_PAIR(CP_NORMAL) | A_DIM); printw("%-6s ", max_s); if (has_colors()) attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); /* JITTER */ { int jc = jitter_color(h->rtt_jitter); if (has_colors()) attron(COLOR_PAIR(jc)); printw("%-4s ", jitter_s); if (has_colors()) attroff(COLOR_PAIR(jc)); } /* ASN — cyan; NETNAME — dim grey; ORGNAME — cyan (NETNAME/ORGNAME swapped) */ if (ws->whois_enabled && show_orgname) { if (has_colors()) attron(COLOR_PAIR(CP_STATUS)); printw("%-8.8s ", h->asn[0] ? h->asn : "-"); if (has_colors()) { attroff(COLOR_PAIR(CP_STATUS)); attron(COLOR_PAIR(CP_NORMAL) | A_DIM); } printw("%-16.16s ", h->netname[0] ? h->netname : "-"); if (has_colors()) { attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); attron(COLOR_PAIR(CP_STATUS)); } printw("%-20.20s ", h->orgname[0] ? h->orgname : "-"); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS)); } else if (ws->whois_enabled) { if (has_colors()) attron(COLOR_PAIR(CP_STATUS)); printw("%-8.8s ", h->asn[0] ? h->asn : "-"); if (has_colors()) { attroff(COLOR_PAIR(CP_STATUS)); attron(COLOR_PAIR(CP_NORMAL) | A_DIM); } printw("%-16.16s ", h->netname[0] ? h->netname : "-"); if (has_colors()) attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); } /* RTT history sparkline (newest at right, stops before right border). * Colors are relative to this hop's all-time average RTT: * ≤ avg×1.2 → green * ≤ avg×1.5 → yellow * > avg×1.5 → magenta (hot/high jitter) * no reply → solid ▪ in red+bold (loss marker) * Block height scales 0→g_max across N_BLOCK_CHARS levels. */ if (hist_cols > 0) { int j; int n = h->history_count; double g_max = 0; double avg = (h->rtt_avg > 0) ? h->rtt_avg : 1.0; for (j = 0; j < n; j++) { int idx = (h->history_head - n + j + WATCH_HISTORY_LEN) % WATCH_HISTORY_LEN; if (h->rtt_history[idx] > g_max) g_max = h->rtt_history[idx]; } if (g_max <= 0) g_max = 1; col = right_col - hist_cols; move(row, col); for (j = 0; j < hist_cols; j++) { int age = hist_cols - 1 - j; int src_idx; double v; int block; int sc; if (age >= n) { addstr(" "); continue; } src_idx = (h->history_head - 1 - age + WATCH_HISTORY_LEN) % WATCH_HISTORY_LEN; v = h->rtt_history[src_idx]; if (v <= 0) { /* Loss: solid red block marker ▪ */ if (has_colors()) attron(COLOR_PAIR(CP_RED) | A_BOLD); addstr("\xe2\x96\xaa"); /* ▪ U+25AA */ if (has_colors()) attroff(COLOR_PAIR(CP_RED) | A_BOLD); } else { block = (int)(v / g_max * (N_BLOCK_CHARS - 2)) + 1; if (block >= N_BLOCK_CHARS) block = N_BLOCK_CHARS - 1; /* Color relative to this hop's average */ if (v <= avg * 1.2) sc = CP_GREEN; else if (v <= avg * 1.5) sc = CP_YELLOW; else sc = CP_MAGENTA; if (has_colors()) attron(COLOR_PAIR(sc)); addstr(BLOCK_CHARS[block]); if (has_colors()) attroff(COLOR_PAIR(sc)); } } } row++; /* Multipath alternate IPs — one sub-row per extra path, matching the * indented style LFT uses in standard (-o) output. ↳ sits inside the * hop-number column; the IP left-aligns at the HOST column start. * All three table views (1, 2, 3) share this via watch_render_timeline. */ if (h->n_alt > 0) { int k; for (k = 0; k < h->n_alt && row <= row_end && row < ws->term_rows - 2; k++) { move(row, col_offset); clrtoeol(); if (has_colors()) attron(COLOR_PAIR(CP_STATUS) | A_DIM); /* col_offset+4 → ↳ (1 col) + space (1 col) → IP at col_offset+6 = HOST */ mvprintw(row, col_offset + 4, "\xe2\x86\xb3 %-*s", host_w, h->alt_hosts[k]); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS) | A_DIM); row++; } } } /* Path change notice (horizontally centered) */ if (ws->path_changed && difftime(time(NULL), ws->path_change_time) < WATCH_PATH_FLASH_SECS) { if (row <= row_end && row < ws->term_rows - 2) { char ts[32]; char banner[96]; int bw; int bx; struct tm *tm_info; time_t t = ws->path_change_time; tm_info = localtime(&t); strftime(ts, sizeof(ts), "%H:%M:%S", tm_info); snprintf(banner, sizeof(banner), " *** PATH CHANGE DETECTED at %s *** ", ts); bw = (int)strlen(banner); bx = (ws->term_cols - bw) / 2; if (bx < col_offset) bx = col_offset; move(row, col_offset); clrtoeol(); if (has_colors()) attron(COLOR_PAIR(CP_RED) | A_BOLD | A_REVERSE); mvaddstr(row, bx, banner); if (has_colors()) attroff(COLOR_PAIR(CP_RED) | A_BOLD | A_REVERSE); row++; } } return row - start_row; } /* ========================================================================= * View 3: MTR-style stats board (was view 4; old full-screen timeline and * topology views removed when collapsing 4 views → 3). * ========================================================================= */ static void watch_render_view4(watch_session_t *ws) { int i, row = 1; int r; int bot_row = ws->term_rows - 3; /* last content row; status bar closes below */ clear(); watch_render_top_border(ws, NULL); /* Header row — leading space becomes the side │ after border draw. * All variable-width columns use %-N.Ns (both pad AND truncate) so * toggling 'h' (hostname ↔ IP) never shifts subsequent columns. */ if (has_colors()) attron(COLOR_PAIR(CP_TBLHEAD)); if (ws->whois_enabled) mvprintw(row++, 0, " %-3s %-20.20s %5s %5s %5s %7s %7s %7s %7s %7s %-8.8s %-15.15s %-19.19s", "HOP", "HOST", "LOSS%", "SENT", "RECV", "MIN", "AVG", "MAX", "STDDEV", "LAST", "ASN", "NETNAME", "ORGNAME"); else mvprintw(row++, 0, " %-3s %-20.20s %5s %5s %5s %7s %7s %7s %7s %7s", "HOP", "HOST", "LOSS%", "SENT", "RECV", "MIN", "AVG", "MAX", "STDDEV", "LAST"); if (has_colors()) attroff(COLOR_PAIR(CP_TBLHEAD)); /* Per-hop rows — per-column colors match watch_render_timeline exactly * so views 1, 2, 3 all use the same palette where columns overlap. * View 3 adds SENT/RECV/STDDEV/LAST; those use AVG-style thresholds. */ for (i = 0; i < ws->n_hops && row < ws->term_rows - 2; i++) { watch_hop_t *h = &ws->hops[i]; double loss_pct = (h->cycles_total > 0) ? 100.0 * (h->cycles_total - h->cycles_seen) / h->cycles_total : 0.0; char host_trunc[72]; char loss_s[8]; /* Approximate stddev from history */ double stddev = 0; if (h->history_count > 1) { double sum2 = 0; int j; int n = h->history_count; for (j = 0; j < n; j++) { int idx = (h->history_head - n + j + WATCH_HISTORY_LEN) % WATCH_HISTORY_LEN; double v = h->rtt_history[idx]; double d = v - h->rtt_avg; sum2 += d * d; } stddev = sqrt(sum2 / n); } snprintf(host_trunc, sizeof(host_trunc), "%.20s", hop_display_addr(h, ws->show_hostnames)); if (loss_pct >= 100.0) snprintf(loss_s, sizeof(loss_s), "100%%"); else snprintf(loss_s, sizeof(loss_s), "%d%%", (int)round(loss_pct)); move(row, 0); clrtoeol(); /* HOP — cyan (same as ASN values, matching timeline) */ if (has_colors()) attron(COLOR_PAIR(CP_STATUS)); mvprintw(row, 0, " %3d ", i + 1); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS)); /* HOST — loss-based color, bold if any loss */ { int hc = host_color(loss_pct); attr_t ha = (loss_pct > 0) ? (COLOR_PAIR(hc) | A_BOLD) : COLOR_PAIR(hc); if (has_colors()) attron(ha); printw("%-20.20s ", host_trunc); if (has_colors()) attroff(ha); } /* LOSS% — same thresholds as timeline */ { attr_t la; if (loss_pct <= 0) la = COLOR_PAIR(CP_GREEN) | A_BOLD; else if (loss_pct < 10) la = COLOR_PAIR(CP_YELLOW) | A_BOLD; else if (loss_pct < 50) la = COLOR_PAIR(CP_RED); else la = COLOR_PAIR(CP_RED) | A_BOLD; if (has_colors()) attron(la); printw("%5s ", loss_s); if (has_colors()) attroff(la); } /* SENT / RECV — dim (counters, not thresholds) */ if (has_colors()) attron(COLOR_PAIR(CP_NORMAL) | A_DIM); printw("%5d %5d ", h->cycles_total, h->cycles_seen); if (has_colors()) attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); /* MIN — dim */ if (has_colors()) attron(COLOR_PAIR(CP_NORMAL) | A_DIM); printw("%7.1f ", h->rtt_min); if (has_colors()) attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); /* AVG — threshold color */ { int ac = avg_color(h->rtt_avg); if (has_colors()) attron(COLOR_PAIR(ac)); printw("%7.1f ", h->rtt_avg); if (has_colors()) attroff(COLOR_PAIR(ac)); } /* MAX — dim */ if (has_colors()) attron(COLOR_PAIR(CP_NORMAL) | A_DIM); printw("%7.1f ", h->rtt_max); if (has_colors()) attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); /* STDDEV — jitter thresholds */ { int jc = jitter_color(stddev); if (has_colors()) attron(COLOR_PAIR(jc)); printw("%7.1f ", stddev); if (has_colors()) attroff(COLOR_PAIR(jc)); } /* LAST — avg-style thresholds */ { int lc = avg_color(h->rtt_last); if (has_colors()) attron(COLOR_PAIR(lc)); printw("%7.1f ", h->rtt_last); if (has_colors()) attroff(COLOR_PAIR(lc)); } /* ASN — cyan; NETNAME — dim grey; ORGNAME — cyan (swapped) */ if (ws->whois_enabled) { if (has_colors()) attron(COLOR_PAIR(CP_STATUS)); printw("%-8.8s ", h->asn[0] ? h->asn : "-"); if (has_colors()) { attroff(COLOR_PAIR(CP_STATUS)); attron(COLOR_PAIR(CP_NORMAL) | A_DIM); } printw("%-15.15s ", h->netname[0] ? h->netname : "-"); if (has_colors()) { attroff(COLOR_PAIR(CP_NORMAL) | A_DIM); attron(COLOR_PAIR(CP_STATUS)); } printw("%-19.19s", h->orgname[0] ? h->orgname : "-"); if (has_colors()) attroff(COLOR_PAIR(CP_STATUS)); } row++; } /* Side │ borders — solid ACS_VLINE from row 1 to bot_row. Status bar row * below has └──┘ corners that close the frame, matching views 1 and 2. */ if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); if (bot_row >= 1) { mvvline(1, 0, ACS_VLINE, bot_row); mvvline(1, ws->term_cols - 1, ACS_VLINE, bot_row); } if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); (void)r; } /* ========================================================================= * Log drawer * ========================================================================= */ static void watch_render_log_drawer(watch_session_t *ws) { int drawer_height = ws->term_rows / 4; int start_row = ws->term_rows - 2 - drawer_height; int visible_rows = drawer_height - 1; /* content rows below the title */ /* Content width: inside left │ + 1-space indent, up to right │ exclusive */ int content_w = ws->term_cols - 3; /* col 0=│, col 1=space, col(last)=│ */ int i; if (start_row < 0) start_row = 0; if (visible_rows < 1) visible_rows = 1; if (content_w < 1) content_w = 1; /* ------------------------------------------------------------------ */ /* Auto-resume: return to live mode after 30 s of inactivity */ /* ------------------------------------------------------------------ */ if (ws->log_scroll_offset > 0 && difftime(time(NULL), ws->log_last_scroll) >= 30.0) { ws->log_scroll_offset = 0; ws->log_anchor_nlines = 0; } /* ------------------------------------------------------------------ */ /* Linearize the ring buffer into a temporary flat array */ /* ------------------------------------------------------------------ */ char *tmp = NULL; char **lines_arr = NULL; int nlines = 0; tmp = (char *)malloc((size_t)ws->log_len + 1); { int max_ptrs = ws->log_len / 2 + 2; if (max_ptrs > 16384) max_ptrs = 16384; lines_arr = (char **)malloc((size_t)max_ptrs * sizeof(char *)); } if (tmp && lines_arr) { int max_ptrs = ws->log_len / 2 + 2; if (max_ptrs > 16384) max_ptrs = 16384; for (i = 0; i < ws->log_len; i++) tmp[i] = ws->log_buf[(ws->log_head + i) % WATCH_LOG_BUF]; tmp[ws->log_len] = '\0'; char *p = tmp; while (*p && nlines < max_ptrs) { char *eol = strchr(p, '\n'); if (eol) *eol = '\0'; lines_arr[nlines++] = p; if (eol) p = eol + 1; else break; } } /* ------------------------------------------------------------------ */ /* Set the frozen anchor on the first frame where scroll_offset > 0 */ /* ------------------------------------------------------------------ */ if (ws->log_scroll_offset > 0 && ws->log_anchor_nlines == 0) ws->log_anchor_nlines = nlines; /* Clamp scroll offset to the usable range for this anchor */ if (ws->log_scroll_offset > 0) { int max_scroll = (ws->log_anchor_nlines > visible_rows) ? ws->log_anchor_nlines - visible_rows : 0; if (ws->log_scroll_offset > max_scroll) ws->log_scroll_offset = max_scroll; } /* ------------------------------------------------------------------ */ /* Decide which lines to show */ /* ------------------------------------------------------------------ */ int first_line; if (ws->log_scroll_offset == 0) { /* Live mode: always tail the buffer */ first_line = (nlines > visible_rows) ? nlines - visible_rows : 0; } else { /* Frozen mode: anchor_nlines is the fixed reference */ first_line = ws->log_anchor_nlines - visible_rows - ws->log_scroll_offset; if (first_line < 0) first_line = 0; } /* ------------------------------------------------------------------ */ /* Title row label — two fixed-width variants, both 44 display cols: */ /* */ /* Live: "Trace Log Live · ↑k ↓j to scroll · [v] close" */ /* Scroll: "Trace Log · Line NNNNN of NNNNN · [v] close" */ /* */ /* %5d gives right-justified 5-digit numbers (max 16384 lines), so */ /* the total label width never changes between states or line counts. */ /* Unicode: · = U+00B7 ↑ = U+2191 ↓ = U+2193 ─ = U+2500 */ /* ------------------------------------------------------------------ */ { char label[256]; if (ws->log_scroll_offset == 0) { /* Live mode — 44 display cols */ snprintf(label, sizeof(label), "Trace Log Live " "\xc2\xb7" /* · */ " \xe2\x86\x91k" /* ↑k */ " \xe2\x86\x93j" /* ↓j */ " to scroll " "\xc2\xb7" /* · */ " [v] close"); } else { /* Scrolling mode — 44 display cols */ int bottom = ws->log_anchor_nlines - ws->log_scroll_offset; if (bottom < 0) bottom = 0; snprintf(label, sizeof(label), "Trace Log " "\xc2\xb7" /* · */ " Line %5d of %5d " "\xc2\xb7" /* · */ " [v] close", bottom, ws->log_anchor_nlines); } /* Count display columns (non-continuation UTF-8 bytes = display chars) */ int lw = 0; { const char *cp; for (cp = label; *cp; cp++) if (((unsigned char)*cp & 0xC0) != 0x80) lw++; } /* Total border content: ├ + ─── + space + label + fill ─s + ┤ */ /* ─── + space = 4 display cols; ├ and ┤ each = 1; fill fills the rest */ int fill = ws->term_cols - 2 - 4 - lw; if (fill < 0) fill = 0; move(start_row, 0); clrtoeol(); if (has_colors()) attron(COLOR_PAIR(CP_STATUS) | A_DIM); addstr("\xe2\x94\x9c" /* ├ */ "\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80 "); /* ─── (space) */ if (has_colors()) { attroff(COLOR_PAIR(CP_STATUS) | A_DIM); attron(COLOR_PAIR(CP_STATUS) | A_BOLD); } addstr(label); if (has_colors()) { attroff(COLOR_PAIR(CP_STATUS) | A_BOLD); attron(COLOR_PAIR(CP_STATUS) | A_DIM); } for (i = 0; i < fill; i++) addstr("\xe2\x94\x80"); /* ─ */ addstr("\xe2\x94\xa4"); /* ┤ */ if (has_colors()) attroff(COLOR_PAIR(CP_STATUS) | A_DIM); } /* ------------------------------------------------------------------ */ /* Content rows */ /* ------------------------------------------------------------------ */ { int row; for (row = 0; row < visible_rows; row++) { int screen_row = start_row + 1 + row; int line_idx = first_line + row; move(screen_row, 0); clrtoeol(); /* Left border │ */ if (has_colors()) attron(COLOR_PAIR(CP_STATUS) | A_DIM); addstr("\xe2\x94\x82"); /* │ */ if (has_colors()) attroff(COLOR_PAIR(CP_STATUS) | A_DIM); /* 1-space indent then log content, hard-truncated */ addstr(" "); if (tmp && lines_arr && line_idx >= 0 && line_idx < nlines && lines_arr[line_idx][0]) { if (has_colors()) attron(COLOR_PAIR(CP_DRAWER)); mvaddnstr(screen_row, 2, lines_arr[line_idx], content_w); if (has_colors()) attroff(COLOR_PAIR(CP_DRAWER)); } /* Right border │ */ if (has_colors()) attron(COLOR_PAIR(CP_STATUS) | A_DIM); mvaddstr(screen_row, ws->term_cols - 1, "\xe2\x94\x82"); /* │ */ if (has_colors()) attroff(COLOR_PAIR(CP_STATUS) | A_DIM); } } free(tmp); free(lines_arr); } /* ========================================================================= * View 1: A+B Hybrid * ========================================================================= */ static void watch_render_view2(watch_session_t *ws, lft_session_params *proto_sess) { int chart_rows, table_rows, chart_max; int r, i; int bot_row = ws->term_rows - 3; /* last content row; status bar closes below */ int sep; char rtt_info[256]; { int _r; for (_r = 0; _r < ws->term_rows - 2; _r++) { move(_r, 0); clrtoeol(); } } watch_build_rtt_info(ws, rtt_info, (int)sizeof(rtt_info)); watch_render_top_border(ws, rtt_info); /* How many rows for the chart vs table */ chart_max = (ws->term_rows - 5) / 2; /* at most half the usable rows */ if (chart_max < 5) chart_max = 5; chart_rows = watch_render_native_chart(ws, 1, chart_max, 1); if (chart_rows < 0) chart_rows = 0; (void)chart_rows; /* unused — sep is locked to chart_max for stability */ /* Divider: ├──...──┤ (T-junctions with side │ borders). Fixed at * (1 + chart_max) — see view 1 for rationale. */ sep = 1 + chart_max; if (sep < bot_row) { if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); move(sep, 0); addstr("\xe2\x94\x9c"); /* ├ */ for (i = 1; i < ws->term_cols - 1; i++) addstr("\xe2\x94\x80"); /* ─ */ addstr("\xe2\x94\xa4"); /* ┤ */ if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); } /* Timeline table in remaining space (col_offset=1 leaves room for sides) */ table_rows = bot_row - sep; if (table_rows > 1) watch_render_timeline(ws, sep + 1, table_rows, 1); /* Side │ borders — two segments around the sep row (sep already has ├ ┤) */ if (has_colors()) attron(COLOR_PAIR(CP_BLUE)); if (sep > 1) mvvline(1, 0, ACS_VLINE, sep - 1); if (bot_row > sep) mvvline(sep + 1, 0, ACS_VLINE, bot_row - sep); if (sep > 1) mvvline(1, ws->term_cols - 1, ACS_VLINE, sep - 1); if (bot_row > sep) mvvline(sep + 1, ws->term_cols - 1, ACS_VLINE, bot_row - sep); if (has_colors()) attroff(COLOR_PAIR(CP_BLUE)); (void)r; } /* ========================================================================= * Top-level render dispatcher * ========================================================================= */ static void watch_render(watch_session_t *ws, lft_session_params *proto_sess, int spinner_frame) { /* Handle terminal resize */ if (watch_resize_pending) { endwin(); refresh(); watch_resize_pending = 0; ws->term_rows = LINES; ws->term_cols = COLS; if (LINES < WATCH_MIN_ROWS) { endwin(); fprintf(stderr, "\nLFT: The minimum height required for --watch is %d rows," " expand your terminal window if possible.\n\n", WATCH_MIN_ROWS); ws->running = 0; return; } } switch (ws->current_view) { case 2: watch_render_view2(ws, proto_sess); break; case 3: watch_render_view4(ws); break; /* Stats Board (was view 4) */ default: watch_render_view1(ws, proto_sess); break; } if (ws->log_open) watch_render_log_drawer(ws); watch_render_status_bar(ws, spinner_frame); /* ncurses model stays in sync (SIGALRM uses ncurses, not raw writes), * so no wredrawln needed — just compose and flush. */ wnoutrefresh(stdscr); doupdate(); } /* ========================================================================= * Keypress handler * ========================================================================= */ /* ========================================================================= * Screen capture (F3) — saves the current ncurses screen to .txt / .ansi * ========================================================================= */ static void watch_do_screen_capture(watch_session_t *ws) { /* ---- Modal dialog ---- */ int mh = 11, mw = 58; int my = (ws->term_rows - mh) / 2; int mx = (ws->term_cols - mw) / 2; WINDOW *modal; char fname[128]; int pos = 0; /* cursor in fname[] */ time_t now; struct tm *tm_now; int ch; if (my < 0) my = 0; if (mx < 0) mx = 0; modal = newwin(mh, mw, my, mx); if (!modal) return; /* Pre-fill with timestamp */ now = time(NULL); tm_now = localtime(&now); strftime(fname, sizeof(fname), "lft_watch_%Y%m%d_%H%M%S", tm_now); pos = (int)strlen(fname); keypad(modal, TRUE); for (;;) { /* Draw modal */ werase(modal); if (has_colors()) wattron(modal, COLOR_PAIR(CP_STATUS) | A_BOLD); box(modal, 0, 0); mvwprintw(modal, 0, 2, " Save Watch Screen "); if (has_colors()) wattroff(modal, COLOR_PAIR(CP_STATUS) | A_BOLD); mvwprintw(modal, 2, 2, "Filename (without extension):"); if (has_colors()) wattron(modal, A_BOLD); mvwprintw(modal, 3, 2, "> "); if (has_colors()) wattroff(modal, A_BOLD); /* Input field: mw-6 chars wide */ { int field_w = mw - 6; char disp[128]; snprintf(disp, sizeof(disp), "%-*.*s", field_w, field_w, fname); mvwaddnstr(modal, 3, 4, disp, field_w); } mvwprintw(modal, 5, 2, "Saves: %s.txt", fname); if (has_colors() && has_colors()) mvwprintw(modal, 6, 2, " %s.ansi (color)", fname); if (has_colors()) wattron(modal, COLOR_PAIR(CP_STATUS)); mvwprintw(modal, 9, 2, " [Enter] Save [Esc] Cancel "); if (has_colors()) wattroff(modal, COLOR_PAIR(CP_STATUS)); /* Position cursor in the input field */ wmove(modal, 3, 4 + pos); wrefresh(modal); ch = wgetch(modal); if (ch == 27 /* Esc */) { delwin(modal); return; /* cancelled */ } if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) { break; /* save */ } if ((ch == KEY_BACKSPACE || ch == 127 || ch == '\b') && pos > 0) { fname[--pos] = '\0'; } else if (ch >= 32 && ch < 127 && pos < (int)sizeof(fname) - 2) { /* Accept printable ASCII except path separators */ if (ch != '/' && ch != '\\') { fname[pos++] = (char)ch; fname[pos] = '\0'; } } } delwin(modal); if (fname[0] == '\0') return; /* nothing typed */ /* ---- Save .txt (plain text, strip attributes) ---- */ { char txt_path[256]; FILE *fp; int r, c; snprintf(txt_path, sizeof(txt_path), "%s.txt", fname); fp = fopen(txt_path, "w"); if (fp) { for (r = 0; r < ws->term_rows; r++) { for (c = 0; c < ws->term_cols; c++) { chtype cell = mvinch(r, c); char ch_out = (char)(cell & A_CHARTEXT); if (ch_out == '\0') ch_out = ' '; fputc(ch_out, fp); } fputc('\n', fp); } fclose(fp); snprintf(ws->status_msg, sizeof(ws->status_msg), "Saved: %s.txt", fname); ws->status_msg_time = time(NULL); } else { snprintf(ws->status_msg, sizeof(ws->status_msg), "Error: could not create %s.txt", fname); ws->status_msg_time = time(NULL); } } /* ---- Save .ansi (with color escape codes if supported) ---- */ if (has_colors()) { char ansi_path[256]; FILE *fp; int r, c; short fg_prev = -1, bg_prev = -1; snprintf(ansi_path, sizeof(ansi_path), "%s.ansi", fname); fp = fopen(ansi_path, "w"); if (fp) { for (r = 0; r < ws->term_rows; r++) { for (c = 0; c < ws->term_cols; c++) { chtype cell = mvinch(r, c); chtype attr = cell & ~A_CHARTEXT; char ch_out = (char)(cell & A_CHARTEXT); short fg = -1, bg = -1; int pair_num = (int)PAIR_NUMBER(attr); if (pair_num > 0) pair_content((short)pair_num, &fg, &bg); /* Emit color codes only on change */ if (fg != fg_prev || bg != bg_prev) { fprintf(fp, "\033[0m"); if (fg >= 0 && fg <= 7) fprintf(fp, "\033[3%dm", fg); if (bg >= 0 && bg <= 7) fprintf(fp, "\033[4%dm", bg); if (attr & A_BOLD) fprintf(fp, "\033[1m"); fg_prev = fg; bg_prev = bg; } if (ch_out == '\0') ch_out = ' '; fputc(ch_out, fp); } fprintf(fp, "\033[0m\n"); fg_prev = bg_prev = -1; } fclose(fp); /* Append ".ansi" note to status message */ { char tmp[192]; snprintf(tmp, sizeof(tmp), "%s + .ansi", ws->status_msg); snprintf(ws->status_msg, sizeof(ws->status_msg), "%s", tmp); } } } touchwin(stdscr); refresh(); } /* ========================================================================= * Key dispatcher * ========================================================================= */ static void watch_handle_key(watch_session_t *ws, int ch) { if (ch == ERR) return; /* no key pressed */ switch (ch) { case '1': ws->current_view = 1; break; case '2': ws->current_view = 2; break; case '3': ws->current_view = 3; break; /* Stats Board */ case 'q': case 'Q': ws->running = 0; break; case 'p': case 'P': if (ws->paused) { /* Resuming: break idle loop so trace starts immediately */ ws->paused = 0; ws->interrupt_sleep = 1; } else { ws->paused = 1; /* Pausing: idle loop drains naturally */ } break; case 'r': case 'R': { /* Reset per-hop stats and cumulative counters; keep path and cycle */ int i; for (i = 0; i < WATCH_MAX_HOPS; i++) { watch_hop_t *h = &ws->hops[i]; h->cycles_seen = 0; h->cycles_total = 0; h->rtt_min = h->rtt_avg = h->rtt_max = 0; h->rtt_jitter = h->rtt_last = h->rtt_sum = 0; h->history_head = h->history_count = 0; } ws->total_replies = 0; ws->total_sent = 0; ws->max_hops_seen = 0; ws->target_ever_reached = 0; ws->interrupt_sleep = 1; /* start next trace immediately */ break; } case 'c': case 'C': { /* Full clear: reset stats, hops, cycle, AND all annotation caches * so pWhoIs + DNS are re-fetched from scratch next cycle. */ int i; watch_ip_record_t *rec; for (i = 0; i < WATCH_MAX_HOPS; i++) { watch_hop_t *h = &ws->hops[i]; h->cycles_seen = 0; h->cycles_total = 0; h->rtt_min = h->rtt_avg = h->rtt_max = 0; h->rtt_jitter = h->rtt_last = h->rtt_sum = 0; h->history_head = h->history_count = 0; h->asn[0] = h->netname[0] = h->orgname[0] = '\0'; h->hostname[0] = '\0'; } ws->total_replies = 0; ws->total_sent = 0; ws->max_hops_seen = 0; ws->target_ever_reached = 0; ws->n_hops = 0; ws->cycle = 0; /* Flush whois + rDNS from the per-IP cache so everything is * re-queried. Hostname fields cleared unconditionally (c-ares * re-submits anyway; watch ip_cache injection will repopulate). */ for (i = 0; i < WATCH_IP_CACHE_BUCKETS; i++) { for (rec = ws->ip_cache.buckets[i]; rec; rec = rec->next) { rec->asn[0] = '\0'; rec->netname[0] = '\0'; rec->orgname[0] = '\0'; rec->hostname[0] = '\0'; rec->whois_queried = 0; rec->rdns_queried = 0; } } ws->whois_bulk_done = 0; /* re-trigger bulk pWhoIs next cycle */ clear(); wnoutrefresh(stdscr); doupdate(); ws->interrupt_sleep = 1; /* start next trace immediately */ break; } case 'v': case 'V': ws->log_open = !ws->log_open; if (ws->log_open) { /* Always start at the live tail when opening the drawer */ ws->log_scroll_offset = 0; ws->log_anchor_nlines = 0; ws->log_last_scroll = 0; } break; /* Log drawer scroll — only active when the drawer is open */ case KEY_UP: case 'k': if (ws->log_open) { ws->log_scroll_offset++; ws->log_last_scroll = time(NULL); } break; case KEY_DOWN: case 'j': if (ws->log_open) { if (ws->log_scroll_offset > 0) { ws->log_scroll_offset--; ws->log_last_scroll = time(NULL); } if (ws->log_scroll_offset == 0) ws->log_anchor_nlines = 0; /* back to live; clear anchor */ } break; case 'w': case 'W': ws->whois_enabled = !ws->whois_enabled; /* If turning back on and somehow the bulk query never ran, do it now */ if (ws->whois_enabled && !ws->whois_bulk_done) watch_whois_bulk_query(ws); break; case 'h': case 'H': ws->show_hostnames = !ws->show_hostnames; break; case 'x': case 'X': watch_do_screen_capture(ws); break; case '+': case '=': if (ws->interval < 300) ws->interval++; break; case '-': case '_': if (ws->interval > 1) ws->interval--; break; default: break; } } /* ========================================================================= * Main entry point * ========================================================================= */ void WatchModeRun(lft_session_params *sess) { watch_session_t *ws; struct sigaction sa_winch, sa_old_winch; int orig_noisy; /* noisy level as passed on the CLI (before watch bumps it) */ /* Save verbosity level, then force noisy=2 for watch mode so the log * drawer ring buffer is always populated at -VV level regardless of * whether the user passed -V. The drawer auto-opens only if the user * explicitly asked for verbose output. */ orig_noisy = sess->noisy; if (sess->noisy < 2) sess->noisy = 2; /* ------------------------------------------------------------------ */ /* Validate terminal height before entering ncurses */ /* ------------------------------------------------------------------ */ { struct winsize tw; int rows = 0; const char *rows_env = getenv("LINES"); if (rows_env && atoi(rows_env) > 0) rows = atoi(rows_env); else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &tw) == 0) rows = tw.ws_row; if (rows > 0 && rows < WATCH_MIN_ROWS) { fprintf(stderr, "\nLFT: The minimum height required for --watch is %d rows," " expand your terminal window if possible.\n\n", WATCH_MIN_ROWS); return; } } /* ------------------------------------------------------------------ */ /* Allocate watch session state */ /* ------------------------------------------------------------------ */ ws = (watch_session_t *)calloc(1, sizeof(watch_session_t)); if (!ws) { fprintf(stderr, "LFT: out of memory in WatchModeRun\n"); return; } g_watch_ws = ws; /* expose to SIGALRM handler for cumulative totals */ ws->interval = sess->watch_interval > 0 ? sess->watch_interval : WATCH_DEFAULT_INTERVAL; ws->max_cycles = sess->watch_max_cycles; ws->probes_per_hop = (sess->rtt_stats > 0) ? sess->rtt_stats : WATCH_DEFAULT_PROBES; ws->protocol = sess->protocol; /* 0=TCP,1=UDP,2=ICMP,3=RFC1393 — for [REPLIED] label */ ws->current_view = 1; ws->running = 1; ws->first_cycle = 1; /* Whois column (ASN / NETNAME / ORGNAME) is shown when the user has * explicitly requested lookups with -A (do_aslookup) or -N (do_netlookup); * otherwise it stays off. 'w' key toggles at any time. */ ws->whois_enabled = (sess->do_aslookup || sess->do_netlookup) ? 1 : 0; ws->show_hostnames = (sess->hostnames_only != 0) ? 1 : 0; /* off by default; -h CLI or 'h' key enables */ /* Build target label string */ { const char *tgt = sess->hostname ? sess->hostname : "?"; switch (sess->protocol) { case 1: snprintf(ws->tgt_label, sizeof(ws->tgt_label), "udp://%s:%d", tgt, sess->dport); break; case 2: case 3: snprintf(ws->tgt_label, sizeof(ws->tgt_label), "icmp://%s", tgt); break; default: snprintf(ws->tgt_label, sizeof(ws->tgt_label), "tcp://%s:%d", tgt, sess->dport); break; } } /* ------------------------------------------------------------------ */ /* Run the initial trace with the live counter spinner animation. */ /* lft_o_spinner_start_prefix() arms SIGALRM + writes to /dev/tty, */ /* then stdout/stderr are redirected to /dev/null so LFT's normal */ /* text output doesn't appear. lft_o_spinner_stop() erases the */ /* entire animation line cleanly before ncurses initialises. */ /* ------------------------------------------------------------------ */ { int saved_stdout, saved_stderr; /* Start the live counter animation using the same "Tracing proto://host:port " * prefix as -o mode. Writes to /dev/tty via SIGALRM so it survives the * stdout→/dev/null redirect that follows. */ lft_o_spinner_start(sess); { int init_pipe[2]; int init_use_pipe = (sess->noisy >= 1); init_pipe[0] = init_pipe[1] = -1; saved_stdout = dup(STDOUT_FILENO); saved_stderr = dup(STDERR_FILENO); if (init_use_pipe && pipe(init_pipe) == 0) { fcntl(init_pipe[1], F_SETFL, fcntl(init_pipe[1], F_GETFL) | O_NONBLOCK); dup2(init_pipe[1], STDOUT_FILENO); dup2(init_pipe[1], STDERR_FILENO); close(init_pipe[1]); init_pipe[1] = -1; } else { int dn = open("/dev/null", O_WRONLY); init_use_pipe = 0; if (dn >= 0) { dup2(dn, STDOUT_FILENO); dup2(dn, STDERR_FILENO); close(dn); } } setOutputStyle(0); /* suppress EVT_ON_EXIT re-entry */ /* S1: watch_mode stays 1 — socket + pcap reused across cycles */ /* Ensure rtt_stats is set so ASCIIOutput (view 1) has chart data */ if (sess->rtt_stats <= 0) sess->rtt_stats = ws->probes_per_hop; LFTExecute(sess); fflush(stdout); fflush(stderr); dup2(saved_stdout, STDOUT_FILENO); dup2(saved_stderr, STDERR_FILENO); close(saved_stdout); close(saved_stderr); if (init_use_pipe && init_pipe[0] >= 0) { char chunk[4096]; ssize_t nr; fcntl(init_pipe[0], F_SETFL, O_NONBLOCK); while ((nr = read(init_pipe[0], chunk, sizeof(chunk) - 1)) > 0) { chunk[nr] = '\0'; watch_log_append(ws, chunk); } close(init_pipe[0]); } } setOutputStyle(4); /* exit_state == -100 means EVT_ON_EXIT fired (normal trace completion). * Only bail out for actual socket/pcap open errors (-1 through -36). */ if (sess->exit_state >= -36 && sess->exit_state < 0) { lft_o_spinner_stop(); /* erase animation before printing error */ fprintf(stderr, "\nLFT: --watch: failed to open raw socket or pcap (exit_state=%d)." " Try: sudo chown root lft && sudo chmod u+s lft\n", sess->exit_state); goto cleanup; } /* Capture results from the initial trace */ ws->cycle = 1; ws->first_cycle = 0; if (sess->hop_info) watch_capture_from_sess(ws, sess); /* Whois is on by default — run the bulk pWhoIs query now so the * very first ncurses render has complete ASN/NETNAME/ORGNAME data. * The spinner continues animating during the whois query. */ if (ws->whois_enabled && !ws->whois_bulk_done) watch_whois_bulk_query(ws); /* Accumulate initial trace counter values. */ ws->total_replies += lft_o_get_replies(); ws->total_sent += lft_o_get_sent(); if (lft_o_get_hops() > ws->max_hops_seen) ws->max_hops_seen = lft_o_get_hops(); if (lft_o_get_target()) ws->target_ever_reached = 1; /* Track current path length (not historical peak); see comment in * watch_capture_from_sess update. */ ws->max_hops_seen = ws->n_hops; /* Erase the startup animation line completely before ncurses init. */ lft_o_spinner_stop(); } /* ------------------------------------------------------------------ */ /* Install SIGWINCH handler for terminal resize */ /* ------------------------------------------------------------------ */ memset(&sa_winch, 0, sizeof(sa_winch)); sa_winch.sa_handler = watch_sigwinch; sigemptyset(&sa_winch.sa_mask); sigaction(SIGWINCH, &sa_winch, &sa_old_winch); /* ------------------------------------------------------------------ */ /* Initialize ncurses */ /* ------------------------------------------------------------------ */ if (watch_init_ncurses(ws) < 0) { endwin(); fprintf(stderr, "\nLFT: The minimum height required for --watch is %d rows," " expand your terminal window if possible.\n\n", WATCH_MIN_ROWS); goto cleanup; } /* ------------------------------------------------------------------ */ /* Install SIGINT/SIGTERM/SIGHUP/SIGQUIT handlers */ /* ncurses hides the cursor and puts the terminal in raw mode; without */ /* these handlers a Ctrl-C leaves the terminal in a broken state. */ /* ------------------------------------------------------------------ */ { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = watch_sig_fatal; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; /* no SA_RESTART — want interrupted syscalls to fail fast */ sigaction(SIGINT, &sa, &g_old_sa_sigint_watch); sigaction(SIGTERM, &sa, &g_old_sa_sigterm_watch); sigaction(SIGHUP, &sa, &g_old_sa_sighup_watch); sigaction(SIGQUIT, &sa, &g_old_sa_sigquit_watch); g_watch_ncurses_active = 1; } if (!g_watch_atexit) { atexit(watch_atexit); g_watch_atexit = 1; } /* Auto-open the log drawer only when the user explicitly passed -V or more * on the command line. The buffer is always populated at -VV level, but * we don't force the drawer open unless the user asked for verbosity. */ if (orig_noisy >= 1) ws->log_open = 1; /* ------------------------------------------------------------------ */ /* Main watch loop — cycle 1 already run above; loop starts from cycle 2 */ /* ------------------------------------------------------------------ */ watch_render(ws, sess, ws->spinner_frame); /* show cycle 1 results */ while (ws->running) { if (ws->max_cycles > 0 && ws->cycle >= ws->max_cycles) break; /* Inter-cycle idle: 4 sub-steps per second so the spinner animates * smoothly at ~4 fps instead of freezing for a whole second. * * Keys that set ws->interrupt_sleep break out immediately (≤250ms * latency) to start the next trace without waiting the full interval. * Any other key triggers a full redraw so view/log changes appear at * once without waiting for the next trace. */ { int sub, total_steps = ws->interval * 4; ws->interrupt_sleep = 0; for (sub = 0; sub < total_steps && ws->running; sub++) { int ch, any_key = 0; ws->spinner_frame = (ws->spinner_frame + 1) % WATCH_SPINNER_FRAMES; while ((ch = getch()) != ERR) { watch_handle_key(ws, ch); any_key = 1; } if (!ws->running || ws->interrupt_sleep) break; /* Sleep first on sub=0: watch_render() already drew at cycle * end so re-drawing immediately would cause a visible flash. */ usleep(250000); /* 250 ms */ if (any_key) watch_render(ws, sess, ws->spinner_frame); else { watch_render_status_bar(ws, ws->spinner_frame); wnoutrefresh(stdscr); doupdate(); } } } if (!ws->running) break; if (ws->max_cycles > 0 && ws->cycle >= ws->max_cycles) break; if (!ws->paused) { ws->cycle++; ws->in_trace = 1; /* SIGALRM owns the status bar during the trace — no pre-trace * status bar write needed here; it would just cause an extra * visible flicker on the footer row. */ /* Run one full trace cycle */ watch_run_one_cycle(ws, sess); ws->in_trace = 0; /* spinner_frame was advanced by the SIGALRM handler during the * trace; sync it back into ws so the idle loop continues from * where the alarm left off. */ ws->spinner_frame = (int)g_alarm_frame; } /* First render: show current trace data immediately so the user * never sits watching a blank screen while pWhoIs blocks. */ watch_render(ws, sess, ws->spinner_frame); if (!ws->paused) { /* Re-run bulk pWhoIs query if it hasn't completed yet (e.g. after * [c] clear reset whois_bulk_done to 0); otherwise just update any * new hop IPs that appeared since the last bulk query. */ if (ws->whois_enabled && !ws->whois_bulk_done) watch_whois_bulk_query(ws); else if (ws->whois_enabled && ws->whois_bulk_done) watch_whois_update_new_hops(ws); /* Second render only needed when whois data was updated */ if (ws->whois_enabled) watch_render(ws, sess, ws->spinner_frame); } } /* Restore termination signal handlers before teardown — normal exit path */ if (g_watch_ncurses_active) { sigaction(SIGINT, &g_old_sa_sigint_watch, NULL); sigaction(SIGTERM, &g_old_sa_sigterm_watch, NULL); sigaction(SIGHUP, &g_old_sa_sighup_watch, NULL); sigaction(SIGQUIT, &g_old_sa_sigquit_watch, NULL); g_watch_ncurses_active = 0; } watch_teardown_ncurses(); cleanup: g_watch_ws = NULL; /* Free per-IP annotation cache */ watch_ip_cache_free(&ws->ip_cache); /* Free pWhoIs session if allocated (F2) */ if (ws->watch_whois_wsess) { w_close(ws->watch_whois_wsess); ws->watch_whois_wsess = NULL; } /* Restore SIGWINCH */ sigaction(SIGWINCH, &sa_old_winch, NULL); free(ws); } #endif /* HAVE_NCURSES */ lft-3.98/lft_icmptrace.h000644 000765 000024 00000001157 15164617366 015176 0ustar00vicstaff000000 000000 #ifndef LFT_ICMPTRACE_H #define LFT_ICMPTRACE_H #include "lft_lib.h" #define ICMP_TRACE 30 int generateICMPPacket( lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt, struct icmp_trace_packet_s * packet, u_char ttlval, u_short unique_id, u_short seq); void icmp_trace_main_loop(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt); #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) void pcap_icmp_process_packet(u_char *user_data, const struct pcap_pkthdr *hdr, const u_char *packet); unsigned int icmp_pmtud_send_verify_hop(lft_session_params *sess, short nhop); #endif #endif lft-3.98/lft_ifname.h000644 000765 000024 00000001171 11053131665 014444 0ustar00vicstaff000000 000000 /* * lft_ifname.h * Layer Four Traceroute * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * */ #ifndef LFT_IFNAME_H #define LFT_IFNAME_H #ifdef __cplusplus extern "C" { #endif #define STRNCPY(dst, src, len) { \ (dst)[(len)-1] = '\0'; \ strncpy((dst),(src),(len)-1); \ } extern u_long lft_getifaddr (const char *); extern char * lft_getifname (struct in_addr); #ifdef __cplusplus } #endif #endif /*LFT_IFNAME_H*/ lft-3.98/whob.8000644 000765 000024 00000012131 15165341545 013225 0ustar00vicstaff000000 000000 .Dd April 7, 2026 .Dt WHOB 8 .Os WHOB .Sh NAME .Nm whob .Nd display whois-type information of interest to Internet operators .Sh SYNOPSIS .Nm whob .Op Fl AaCcgNnOopPRrstuVv .Op Fl h Ar server .Op Fl w Ar server .Op Fl f Ar file .Ar query | me | whoami .Sh DESCRIPTION .Nm queries various sources of whois information for data of interest to network operators and their tracing and debugging tools. .Pp .Nm output is designed to be easily parsed, or better yet, its functionality can be added directly into your programs (see whois.h). .Pp The only mandatory parameter is the target host name or IP address. Options toggle the display of additional data or change the sources used to obtain it. .Pp One key advantage of .Nm is its lookup of ASN information derived from the global Internet routing table itself, as opposed to relying solely on what has been registered in the RADB/IRR (see below). This data is, by default, sourced from the global pWhoIs service. See www.pwhois.org. .Pp Options: .Bl -tag -width Ds .It Fl A Ar ASN Display all routing advertisements transiting the respective ASN. The ASN may be supplied as the target argument, or a hostname or IP address may be supplied and .Nm will resolve the ASN automatically. .It Fl a Ar ASN Display all routing advertisements made by the respective Origin-AS. The Origin-AS may be supplied as the target argument, or a hostname or IP address may be supplied and .Nm will resolve the ASN automatically. .It Fl P Ar prefix Display all routing advertisements related to the CIDR prefix supplied. .It Fl N Ar ASN Display all networks registered to the ASN supplied. .It Fl O Ar ASN Display all contact information on file for the ASN supplied. .It Fl g Disable GIGO mode. By popular request, .Nm takes input directly from the command line and passes it without modification to pWhoIs or whatever whois server is requested .Pq Fl h . The exact output is returned without any parsing. To enable parsing and the other useful options, disable GIGO mode with .Fl g . .It Fl R Display the Origin-AS on record at the RADB/IRR (Routing Arbiter Database/Internet Routing Registry) in addition to the Origin-AS provided by the prefix-based whois data source. .It Fl n Display the network name on record with the IP network allocation registry (ARIN, RIPE, APNIC, etc.). .It Fl o Suppress display of the organization name on file at the registrar. By default the organization name is shown; this flag hides it. .It Fl p Display the AS-Path from the perspective of the current pWhoIs server. The pWhoIs server may automatically exclude the initial, least-specific ASN received from the operator of the network to which it is connected (unless that ASN is the only/origin ASN or has multiple peers). This AS-Path is subjective. If you rely on it and want AS-Paths that correspond to your own network infrastructure, consider running your own pWhoIs server. See the .Fl w option and www.pwhois.org. .It Fl t Display the date the route was last cached by the pWhoIs server. .It Fl u Display dates in UTC/GMT instead of local time. Implies .Fl t (enables cache-date display as a side effect). .It Fl h Ar server .It Fl w Ar server Override the source of prefix-based whois data. Both flags are equivalent: each sets the pWhoIs-compatible server to query instead of the default pWhoIs host. Accepts a hostname or IP address. .It Fl f Ar file Read from .Ar file and submit its contents as bulk input to pWhoIs. Pass .Sq - as .Ar file to read from stdin. Input is buffered and subject to the constraints of the current pWhoIs server. Output is written to stdout (which may be redirected) and will not be parsed. Additional instructions to pWhoIs may be placed at the beginning of the file, but will only apply to the first buffer of input. The first (left-most) field on each line must be the IP address; lines may be up to 255 characters long. .It Fl c Use Cymru's whois server instead of pWhoIs for interactive queries. When used with .Fl f , uses Cymru for bulk file resolution. See www.cymru.com for details. .It Fl C Use Cymru's whois server for bulk file resolution (implies .Fl f ) . This is the explicit bulk-Cymru mode; interactive queries are unaffected. .It Fl r Use RIPE NCC RIS instead of pWhoIs. When used with .Fl f , uses RIPE NCC riswhois for bulk file resolution. See www.ripe.net/projects/ris/. .It Fl s Display the version and status of the pWhoIs server and exit. .It Fl V Display verbose/debug output. Repeat for additional verbosity .Pq Fl VV , Fl VVV . .It Fl v Display version information and exit. .It Ar me | whoami Display this host's public IP address and exit. .El .Sh EXAMPLES Basic query returning ASN, prefix, net name, and org name: .Pp .Dl whob 1.2.3.4 .Pp Query with AS-Path and net name displayed, GIGO mode disabled: .Pp .Dl whob -gnp 1.2.3.4 .Pp Bulk file resolution using Cymru: .Pp .Dl whob -Cf iplist.txt .Pp Display this host's public IP: .Pp .Dl whob me .Sh SEE ALSO .Xr whois 1 , .Xr lft 8 .Sh HISTORY The .Nm command first appeared in 2004. This whois framework has been a component of LFT since 2002. .Sh AUTHORS .An Victor Oppleman and .An Eugene Antsilevitch . .Sh BUGS To report bugs, send e-mail to .Aq whob@oppleman.com . lft-3.98/lft_btcptrace.h000644 000765 000024 00000000255 11053131665 015156 0ustar00vicstaff000000 000000 #ifndef LFT_BTCPTRACE_H #define LFT_BTCPTRACE_H #include "lft_lib.h" void tcp_base_trace_main_loop(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt); #endif lft-3.98/README000644 000765 000024 00000002020 15165341545 013051 0ustar00vicstaff000000 000000 # This file is part of LFT. # # The LFT software provided in this Distribution is # Copyright (c) 2001-2026 VOSTROM Holdings, Inc. # # The full text of our legal notices is contained in the file called # COPYING, included with this Distribution. 1) LFT stands for "Layer Four Traceroute" 2) We advise you to acquire the latest version of LFT before installing LFT's web site is located at https://pwhois.org/lft/ 3) Please read the "INSTALL" file for instructions on installing LFT 4) Please read the LFT manual page for instructions on using LFT (lft.8) 5) LFT also includes "whob" (a whois client for Internet operators) WhoB has its own manual page (whob.8). 6) The authors may be reached via e-mail at lft@oppleman.com We welcome your feedback. We also welcome your contributions. If you're interested in helping us make LFT better, please contact us. 7) Our road map for where we're taking the software may be found in the "TODO" file. 8) LFT's history of changes can be found in the "CHANGELOG" ENJOY! lft-3.98/TODO000644 000765 000024 00000001052 15171551114 012655 0ustar00vicstaff000000 000000 LFT TO-DO --------- Features to ADD (not in any particular order) - IPv6 support, or AF-independent source (getaddrinfo/getnameinfo refactor done in 3.95) - consider implementing per-hop platform fingerprinting to identify router and firewall OS/platform at each hop (Cisco IOS, Junos, Linux, etc.) - ECMP Diamond detection: enumerate multiple parallel paths through load-balanced networks; display the full multipath topology rather than a single linear trace - Fix: TCP-based tracing on windows doesn't work because of "security features" lft-3.98/lft_lib.h000644 000765 000024 00000063603 15173526114 013767 0ustar00vicstaff000000 000000 /* * lft_lib.h * Layer Four Traceroute * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * */ #ifndef LFT_LIB_H #define LFT_LIB_H #include "lft_types.h" /* c-ares async DNS -- present only when detected at configure time */ #ifdef HAVE_CARES #include #endif #define LFT_DNS_CACHE_SIZE 64 /* max unique hop IPs per trace (ttl_limit+2) */ #define LFT_ARES_MAX_FDS 16 /* max simultaneous c-ares sockets (typically 1-2) */ /* not available in earlier darwin systems */ #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif /* As the trace progresses, each hope will attempt to work through the states one by one until it receives an answer (2 attempts per state). Whatever state "works" - will be then set up on following hops to continue from. */ #define HS_SEND_FIN 0x00 #define HS_SEND_SYN 0x01 #define HS_SEND_SYN_FIN 0x02 #define HS_SEND_RST 0x04 #define HS_SEND_SYN_ACK 0x12 #define HS_SEND_ACK 0x16 #define HS_MAX (HS_SEND_SYN) #define HF_ENDPOINT 0x01 /* default timeout value */ #define DEFAULT_TIMEOUT_MS 250 /* Common EtherType values */ #ifndef ETHERTYPE_IP #define ETHERTYPE_IP 0x0800 /* IP protocol */ #endif #ifndef ETHERTYPE_ARP #define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */ #endif #ifndef ETHERTYPE_REVARP #define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */ #endif #ifndef ETHERTYPE_VLAN #define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tagging */ #endif #ifndef ETHERTYPE_IPV6 #define ETHERTYPE_IPV6 0x86dd /* IPv6 */ #endif /* Sometimes-missing BPF values */ #ifndef DLT_RAW #define DLT_RAW 101 /* Raw IP */ #endif #ifndef DLT_PPP_SERIAL #define DLT_PPP_SERIAL 50 /* PPP with HDLC encapsulation */ #endif #ifndef DLT_PPP_ETHER #define DLT_PPP_ETHER 51 /* PPP over Ethernet */ #endif #ifndef DLT_LINUX_SLL #define DLT_LINUX_SLL 113 /* Linux cooked capture */ #endif #ifndef DLT_PPP #define DLT_PPP 9 /* PPP over Ethernet */ #endif /* ToS (type of service) bits we can set on the IP datagram */ #define TOSMINDELAY 0x10 #define TOSMAXTHROUGH 0x08 #define TOSMAXRELIABLE 0x04 #define TOSMINCOST 0x02 /*Errors and warnings codes*/ #define WRN_CANT_SETUP_FIN -1 #define WRN_CANT_DISP_HOST_NAMES -2 #define WRN_ADAPTIVE_DISABLED_BY_UDP -3 #define WRN_FIN_DISABLED_BY_UDP -4 #define WRN_ONLY_ONE_ASN_LOOKUP -5 #define WRN_UDP_PORT_TOO_HIGH -6 #define WRN_PACKET_LENGTH_TOO_HIGH -7 #define WRN_PACKET_LENGTH_TOO_LOW -8 #define WRN_CANT_DISABLE_RESOLVER -9 #define WRN_ALREADY_RANDOM_SPORT -10 #define WRN_ADAPTIVE_DISABLED_BY_FIN -12 #define ERR_DEVNAME_TOO_LONG -13 #define WRN_UNABLE_SETUP_UTC -14 #define WRN_GETIFFORREMOTE_SOCKET -15 #define WRN_GETIFFORREMOTE_CONNECT -16 #define WRN_GETIFFORREMOTE_SOCKNAME -17 #define ERR_UNKNOWN_HOST -18 #define ERR_RAW_SOCKET -19 #define ERR_SOCKET_BIND -20 #define WRN_WSAIOCTL -21 #define ERR_IP_HDRINCL -22 #define ERR_NOT_ENOUGH_MEM -23 #define ERR_RAW_TCP_DISABLED -24 typedef struct _badhopstateparam { const struct hop_info_s *h; short nhop; }WrnBadHopStateParam; #define WRN_BAD_HOP_STATE -25 #define WRN_NS_LOOKUP_FAILED -26 #define ERR_WIN_SELECT -27 #define ERR_WIN_RECV -28 #define ERR_WIN_WSASTARTUP -29 #define ERR_PCAP_ERROR -30 #define ERR_DISCOVER_INTERFACE -31 #define ERR_UNKNOWN_INTERFACE -32 #define ERR_UNKNOWN_SEND_INTERFACE -32 #define ERR_PCAP_DEV_UNAVAILABLE -33 #define WRN_BIOCIMMEDIATE -34 #define ERR_PCAP_NONBLOCK_ERROR -35 #define ERR_PCAP_ACTIVATE_ERROR -36 #define WRN_PCAP_ACTIVATE -37 /*Events codes and their params structures*/ #define EVT_AUTOCONFIGURED_TO_PORTS 1 #define EVT_ADDRESS_INITIALIZED 2 typedef struct _sentpacketparams { short nhop; unsigned int tseq; unsigned char flags; unsigned short tttl; }EvtSentPacketParam; #define EVT_SENT_PACKET 3 #define EVT_SHOW_PAYLOAD 4 #define EVT_SHOW_UDP_CHECKSUM 5 #define EVT_SHOW_TCP_CHECKSUM 6 #define EVT_SHOW_HOPS 7 #define EVT_SHOW_NUM_HOPS 8 #define EVT_TRACE_COMPLETED 9 #define EVT_ON_RESOLUTION 10 #define EVT_TRACE_REPORT_START 11 typedef struct _rptnoreplyparams { int hopno; int noreply; }EvtNoReplyParam; #define EVT_RPT_NO_REPLY 12 #define EVT_RPT_FRW_INSPECT_PACKS 13 #define EVT_RPT_FRW_STATE_FILTER 14 #define EVT_RPT_BSD_BUG 15 #define EVT_RPT_HOP_INFO_START 16 typedef struct _packetinfoevtparam { int asnumber; const char * netname; struct in_addr last_hop; int is_asseam; int is_netseam; int seam_traced; int is_open; int is_filtered; const struct trace_packet_info_s * tp; uint16_t path_mtu; /* 0 = none; >0 = probe size when hop was flagged */ int pmtud_blackhole; /* 1 = silent DF dropper (no PTB returned) */ int pmtud_icmp_suppressor; /* 1 = suppresses TTL-exceeded for large probes but forwards them */ }EvtPacketInfoParam; #define EVT_RPT_PACKET_INFO 17 #define EVT_RPT_PACKET_LIST_END 18 #define EVT_RPT_NO_HOPS 19 #define EVT_RPT_TIME_TRACE 20 #define EVT_ON_EXIT 21 #define EVT_TTL_NO_REPLY 22 #define EVT_PROGRESS_NO_REPLY 23 #define EVT_TTL_TOUT_RESEND 24 #define EVT_TTL_TOUT_GIVINGUP 25 typedef struct _debugchkpoint1 { int last_return; int no_reply; int need_reply; }EvtDebugCheckpoint1Param; #define EVT_DBG_CHECKPOINT1 26 #define EVT_CANT_RELIABLY_RTRIP 27 #define EVT_HAVE_UNANSWERRED_HOPS 28 #define EVT_TOO_FAR_AHEAD 29 #define EVT_HAVE_GAPS 30 #define EVT_EITHER_RESP_OR_TOUT 31 #define EVT_LOOKFOR_UNINC_ACK 32 #define EVT_LOOKFOR_OFF_BY_LEN 33 #define EVT_LOOKFOR_LAST_RESORT 34 #define EVT_SKIP_PACKET 35 typedef struct _nonseqpack { struct in_addr ipaddr; const struct trace_packet_info_s * tp; }EvtNonSeqPacketParam; #define EVT_ACK_WAS_NOT_INC 36 #define EVT_RST_REL_TO_ISN 37 #define EVT_ACK_WAS_WAY_OFF 38 #define EVT_DUPLICATE_PACKET 39 #define EVT_PROGRESS_DUPLICATE 40 typedef struct _recvpacket { struct in_addr ipaddr; struct trace_packet_info_s * tp; unsigned int seq; }EvtRecvPacketParam; #define EVT_RECV_PACKET 41 #define EVT_PROGRESS_OK 42 #define EVT_TCP_PORT_CLOSED 43 #define EVT_TCP_PORT_OPEN 44 #define EVT_PROCESS_PACKET_START 45 #define EVT_UDP_NOT_FOR_US 46 typedef struct _incomudpicmp { const struct ip * ip; const struct ip * orig_ip; const struct udphdr *udp; const struct icmp *icmp; }EvtIncomingICMPUDPParam; #define EVT_INCOMING_ICMP_UDP 47 #define EVT_RCVD_ICMP_UDP 48 typedef struct _incomtcpicmp { const struct ip * ip; const struct ip * orig_ip; const struct tcphdr *tcp; const struct icmp *icmp; }EvtIncomingICMPTCPParam; #define EVT_INCOMING_ICMP_TCP 49 #define EVT_RCVD_ICMP_TCP 50 #define EVT_RCVD_TCP 51 #define EVT_RCVD_UNKNOWN 52 #define EVT_DEVICE_SELECTED 53 #define EVT_SHOW_INITIAL_SEQNUM 54 #define EVT_TRACE_START 55 #define EVT_DBG_CHECKPOINT2 56 #define EVT_DBG_LOG_MESSAGE 57 #define EVT_PROGRESS_SKIP_PACKET 58 #define EVT_OPEN_CHECK_RESULT 59 #define ERR_BTCP_PROBE_PORT_IS_BUSY 60 #define ERR_BTCP_WRONG_PORT_VALUE 61 #define EVT_OCHECK_START 62 #define WRN_OCHECK_OPEN_SOCK 63 #define WRN_OCHECK_IOCTL 64 #define WRN_OCHECK_SELECT 65 #define WRN_OCHECK_GETERROR 66 #define WRN_OCHECK_SOCKERROR 67 #define WRN_OCHECK_TIMEOUT 68 #define EVT_OCHECK_OPEN 69 #define WRN_OCHECK_FCNTLGET 70 #define WRN_OCHECK_FCNTLSET 71 #define WRN_OCHECK_CONNECTERR 72 typedef struct _incomechoreplyicmp { const struct ip * ip; const struct icmp_echo_header_s * echo; }EvtIncomingICMPEchoParam; #define EVT_INCOMING_ICMP_Echo 73 #define EVT_RCVD_ICMP_Echo 74 typedef struct _incomicmpicmp { const struct ip * ip; const struct icmp * icmp; const struct ip * orig_ip; const struct icmp_echo_header_s * echo; }EvtIncomingICMPICMPParam; #define EVT_INCOMING_ICMP_ICMP 75 #define EVT_RCVD_ICMP_ICMP 76 #if defined(BSD_IP_STACK) && !defined(OPENBSD) && !defined(__FreeBSD__) #define SCREWED_IP_LEN #endif typedef struct btcpmapentry { int nhop; int port; int sentcount; }BasicTCPMapEntry; #ifdef __cplusplus extern "C" { #endif typedef struct _btcp_debug_info { int type; int hop; int phop; int port; struct in_addr ip; }btcp_debug_info; /* Session parameters */ typedef struct _lft_session_params { struct timeval ts_last_sent; struct timeval now; double scatter_ms; /* milleseconds between sends */ int ttl_min; /* user may request to start at a higher TTL */ int hop_info_length; unsigned short ip_id; /*not used*/ unsigned char tcp_flags; int use_fins; int seq_start; /* generate ISN internally by default */ int dport; /* set default destination to tcp/80 HTTP */ int sport; /* set default source to tcp/53 dns-xfer */ int auto_ports; /* enable port autoselection by default */ int random_source; /* disable random source port by default */ int set_tos; /* disable set ToS bit by default */ int userlen; /* user-requested packet length */ int payloadlen; /* the final probe payloadlength */ int win_len; int timeout_ms; /* timeout between retries */ int retry_max; /* number of retries before giving up */ int retry_min; /* minimum number of checks per hop */ int ahead_limit; /* number of probes we can send * without replies if we don't know * the number of hops */ int dflag; int ttl_limit; /* max # hops to traverse (highest TTL) */ /* LFT-AUTOTUNE — topology inheritance for multi-cycle traces (--watch). * After cycle 1 the actual path length and RTT envelope are known; * subsequent cycles use this to narrow ttl_limit, expand ahead_limit * to the full path width, and shrink the cloaked-hop timeout to * (8 × max_rtt). Only engages once stable_cycles >= 3. Disable at * runtime with --no-learn. See build/autotune_rollback.md for design * notes and rollback recipe. */ struct { int valid; /* 1 = at least one cycle's data captured */ int num_hops; /* path length observed last cycle */ int max_rtt_ms; /* highest per-hop avg RTT observed */ int min_rtt_ms; /* lowest per-hop avg RTT observed */ int stable_cycles; /* consecutive cycles with same num_hops */ int last_protocol; /* if protocol changes, invalidate */ } learned; int no_autotune; /* 1 = --no-learn passed; auto_tune is a no-op */ int learn_margin; /* TTL probe margin above learned_num_hops (default 2) */ int break_on_icmp; /* break on icmp other than time exceeded */ int noisy; /* disable verbose debug by default */ int nostatus; /* print status bar by default */ int userdevsel; /* by default, we'll select the device */ int senddevsel; /* by default, we'll select the device */ int resolve_names; /* dns resolution enabled by default */ int hostnames_only; /* disable printing of IP addresses */ int timetrace; /* disable tracer timing by default */ int adaptive; /* disable state engine by default */ int protocol; /* 0 - TCP, 1 - UDP, 2 - ICMP base, 3 - ICMP RFC 1393, 4 - TCP basic */ int do_netlookup; /* disable netname lookup by default */ int do_aslookup; /* disable asn lookup by default */ int use_radb; /* use RADB instead of pwhois */ int use_cymru; /* use Cymru instead of pwhois */ int use_ris; /* use RIPE NCC RIS instead of pwhois */ char *payload; int send_sock; int skip_header_len; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) int recv_sock; int wsastarted; #else pcap_t * pcapdescr; #endif int UseLocalTime; int num_hops; /*int num_sent;*/ int num_rcvd; int target_open; int target_filtered; int target_anomaly; char *hostname; char *hostname_lsrr[9]; int hostname_lsrr_size; struct in_addr local_address; struct in_addr remote_address; struct timeval begin_time, trace_done_time; /* The actual packet data (one of..)*/ struct trace_packet_s trace_packet; struct icmp_trace_packet_s icmp_packet; /* ICMP flow-hash stabilization: keep checksum constant across probes. * icmp_initial_seq records the sequence number of the first probe so * that each subsequent probe can embed a 2-byte compensation word in * its payload, making (compensation + seq) constant and therefore * keeping the ICMP checksum identical for every probe in the session. * Routers that include the ICMP checksum in their ECMP hash then always * forward all probes along the same path. */ u_short icmp_initial_seq; /* seq from first ICMP probe (the reference value) */ int icmp_seq_anchored; /* set to 1 after icmp_initial_seq is recorded */ /* Packet container with additional info */ /* struct trace_packet_info_s * trace_packet_info;*/ /* indexed by dport - dport NOT USED*/ /* list of packet containers */ SLIST_HEAD(packets_s, trace_packet_info_s) trace_packets; int trace_packets_num; /* Map of ports for basic TCP trace */ BasicTCPMapEntry * btcpmap; int latestmapchoice; int btcpmapsize; int btcpdpucnt; int trg_probe_is_sent; /* btcp_debug_info debugmap[1000]; */ /* int debugmapidx; */ /* hop information, by ttl */ struct hop_info_s * hop_info; const char * pcap_dev; /* data link type as in pcap_datalink() */ int pcap_datalink; const char * pcap_send_dev; const char * userdev; const char * senddev; /*WHOIS parameters*/ whois_session_params * wsess; /*User's data*/ void * UsersDataCookie; /* GraphViz subquery. Disables any output. */ int is_graphviz_subquery; int check_seam; char * graphviz_icon_path; /*Exit status. When this field has value <0 lft will end work as soon as possible*/ int exit_state; /* -Q flag: disable c-ares at runtime even if compiled in. * Always present so the flag can be parsed gracefully in all builds. */ int async_dns_disabled; /* Path MTU Discovery (-K / --mtu) */ int pmtud; /* 1 = active PMTUD mode enabled */ uint16_t pmtud_current_mtu; /* current probe size (steps down on PTB) */ uint16_t pmtud_initial_mtu; /* probe size at trace start (interface MTU) */ int pmtud_ptb_pending; /* reserved for future step-down state */ uint16_t pmtud_ptb_mtu; /* MTU value from last PTB (0 if none) */ int pmtud_verifying; /* 1 = inside lft_pmtud_verify_blackholes(), suppress finish() */ /* RTT statistics (-j N / --stats=N) */ int rtt_stats; /* 0 = off; >0 = per-hop probe count for stats mode */ int no_ascii_chart; /* 1 = suppress ASCII art chart even when -o active */ /* internal: hop index being reported (set by EVT_RPT_HOP_INFO_START) */ int _rtt_stat_hopno; /* Per-trace set of IPs already annotated with ASN/netname at an earlier hop. * Suppresses redundant labels when the same router IP responds at multiple * TTL levels (e.g. a CGNAT/firewall that rejects the probe at every hop). * Zeroed by calloc() in LFTSessionOpen(); reset by watch_reset_session(). */ struct in_addr shown_asn_ips[256]; int n_shown_asn_ips; #ifdef HAVE_NCURSES /* Watch / continuous mode (-W N / --watch[=N] / --continuous[=N]) */ int watch_mode; /* 1 = ncurses continuous monitoring enabled */ int watch_interval; /* seconds between trace cycles (default 5) */ int watch_max_cycles; /* 0 = run indefinitely */ #endif #ifdef HAVE_CARES /* Per-IP hostname cache populated during the trace by c-ares callbacks. * print_host() reads this at display time — no blocking call needed. */ struct { struct in_addr addr; char name[NI_MAXHOST]; /* empty = pending or no PTR; filled by callback */ } dns_cache[LFT_DNS_CACHE_SIZE]; int dns_cache_count; ares_channel ares_chan; /* c-ares channel; NULL if not initialized */ int ares_pending; /* count of outstanding PTR queries */ double ares_wait_ms; /* ms spent in post-trace lft_ares_wait() drain */ /* fd tracking table maintained by lft_ares_sock_state_cb(). * Replaces the deprecated ares_fds() fd_set approach. */ struct { ares_socket_t fd; unsigned int events; /* mask of ares_fd_eventflag_t */ } ares_fd_table[LFT_ARES_MAX_FDS]; int ares_nfds; /* number of active entries in ares_fd_table */ #endif /* HAVE_CARES */ }lft_session_params; extern const char * icmp_messages[]; extern const char *version; extern const char *appname; extern const int maxpacklen; /* Filtered-repeat helpers (lft_lib.c) — used by both finish() and * tcp_base_finish() to compress consecutive same-IP filtered hops. */ int hop_is_filtered_repeat (const lft_session_params *sess, int hopno); struct in_addr hop_first_rcvd_ip (const lft_session_params *sess, int hopno); int hop_first_rcvd_icmp_type (const lft_session_params *sess, int hopno); void print_filtered_run (int start_ttl, int end_ttl, struct in_addr ip, int icmp_type); #ifdef HAVE_CARES /* Queue an async PTR lookup for addr (deduped); safe to call from any trace path. */ void lft_ares_queue(lft_session_params *sess, struct in_addr addr); /* Non-blocking harvest of any completed c-ares PTR replies. */ void lft_ares_poll(lft_session_params *sess); /* Blocking drain of all pending PTR queries (up to 1.2 s); call before display. */ void lft_ares_wait(lft_session_params *sess); #endif /*--------------------------- Callbacks definition ---------------------------*/ /* Paramaters: lft_session_params * sess - session handle, int code - code of error or event, const void * param - additional parameters, depend on code */ typedef void (*LFT_CALLBACK)(lft_session_params *, int, const void *); /*----------------------------------------------------------------------------*/ void LFTInitializeCallbacks(LFT_CALLBACK error_handler, LFT_CALLBACK event_handler); lft_session_params * LFTSessionOpen(void); void LFTSessionClose(lft_session_params * sess); double timediff_ms (struct timeval prior, struct timeval latter); unsigned int get_address(lft_session_params * sess, const char *host); #ifndef SCREWED_IP_LEN u_int32_t ip_cksum (const struct ip *ip); #endif u_int32_t tcp_cksum (struct ip *ip, struct tcphdr *tcp, const char * payload, int payload_len); int hop_state_up (lft_session_params * sess, short nhop); int hop_state_copy(lft_session_params * sess, short nhop); unsigned int new_seq(lft_session_params * sess); /*----------------------------------------------------------------------------*/ /* Safe setting of parameters */ /*----------------------------------------------------------------------------*/ /*Use TCP FIN packets exclusively (defaults are SYN)*/ int LFTSetupFIN(lft_session_params * sess); /*Display hosts symbolically; suppress IP address display*/ int LFTSetupDispSymbHost(lft_session_params * sess); /* IANA special-purpose address registry lookup. * Returns 1 if addr matches an IANA special-purpose prefix. * rfc_out <- the RFC reference (e.g. "RFC1918"), may be NULL * name_out <- the registry name (e.g. "Private-Use"), may be NULL */ /* LFT-AUTOTUNE — apply learned-path knowledge to trace knobs before a cycle. * No-op when sess->no_autotune is set, when sess->learned.valid is 0, or when * learned.stable_cycles < 3. Sets ttl_limit / ahead_limit / timeout_ms / * scatter_ms based on the prior cycle's observed path length and RTT bounds. */ void lft_auto_tune_from_learned(lft_session_params *sess); int lft_iana_v4_lookup(struct in_addr addr, char *rfc_out, size_t rfc_sz, char *name_out, size_t name_sz); int lft_iana_v6_lookup(const struct in6_addr *addr, char *rfc_out, size_t rfc_sz, char *name_out, size_t name_sz); /*Use traditional UDP (probes) for tracing instead of TCP*/ int LFTSetupUDPMode(lft_session_params * sess); #define ASN_LOOKUP_RIS 0 #define ASN_LOOKUP_RADB 1 #define ASN_LOOKUP_CYMRU 2 /*Use RIPE NCC's RIS to resolve ASNs instead of Prefix WhoIs*/ int LFTSetupRISLookup(lft_session_params * sess); /*Use the RADB to resolve ASNs instead of Prefix WhoIs*/ int LFTSetupRADBLookup(lft_session_params * sess); /*Use Cymru to resolve ASNs instead of Prefix WhoIs*/ int LFTSetupCYMRULookup(lft_session_params * sess); /*Destination port number (same as using target:port as target)*/ int LFTSetupDestinationPort(lft_session_params * sess, char * userport); /*Set the length of the probe packet in bytes*/ int LFTSetupLengthOfPacket(lft_session_params * sess, int plen); /*Display hosts numerically; disable use of the DNS resolver*/ int LFTSetupDisableResolver(lft_session_params * sess); /*Source port number*/ int LFTSetupSourcePort(lft_session_params * sess, int port); /*Use LFT's stateful engine to detect firewalls and path anomalies*/ int LFTSetupAdaptiveMode(lft_session_params * sess); /*Use a specific device by name or IP address (\"en1\" or \"1.2.3.4\")*/ int LFTSetupDevice(lft_session_params * sess,char * udev); /*Use a specific device by name or IP address (\"en1\" or \"1.2.3.4\")*/ int LFTSetupSendDevice(lft_session_params * sess,char * sdev); /*Enable active Path MTU Discovery (-K / --mtu): probes grow to interface MTU and step down on PTB */ int LFTSetupPMTUD(lft_session_params * sess); /*Enable RTT statistics mode (-j N / --stats=N): collect N probes per hop, show min/avg/max/stddev*/ int LFTSetupRTTStats(lft_session_params * sess, int n); /* Handle an ICMP PTB (type 3 code 4): reduce probe size and record MTU at the responding hop. * Called from process_packet() and tcp_base_recv_packet() before recv_packet(). */ void lft_pmtud_handle_ptb(lft_session_params *sess, const struct icmp *icmp, unsigned int seq, struct in_addr from); /* Handle a PTB for ICMP probe mode: same logic but resizes via sess->userlen. */ void lft_pmtud_icmp_handle_ptb(lft_session_params *sess, const struct icmp *icmp, unsigned int seq, struct in_addr from); #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) /* Verify tentative PMTUD black holes with a small probe; demote plain non-responders. */ void lft_pmtud_verify_blackholes(lft_session_params *sess); #endif /*Display all times in UTC (GMT0). Activates -T option automatically*/ int LFTSetupUTCTimes(lft_session_params * sess); /*----------------------------------------------------------------------------*/ int lft_resolve_port (lft_session_params * sess, const char *strport); void LFTExecute(lft_session_params * sess); void lft_o_spinner_start(lft_session_params *sess); /* -o live probe counter */ void lft_o_spinner_start_prefix(const char *prefix);/* variant with caller-supplied prefix */ void lft_o_spinner_stop(void); /* erase spinner, disarm SIGALRM */ void lft_o_counters_activate(void); /* enable g_o_* counters, no SIGALRM */ void lft_o_counters_deactivate(void); /* disable g_o_* counters */ int lft_o_get_replies(void); /* read g_o_replies */ int lft_o_get_sent(void); /* read g_o_sent */ int lft_o_get_hops(void); /* read g_o_hops */ int lft_o_get_target(void); /* read g_o_target */ int lft_o_consume_tfail(void); /* read + clear g_o_timeout_flash */ void lft_o_probe_sent(void); /* call from every send path */ void lft_printf(lft_session_params * sess, const char *templ, ...); /*----------------------------------------------------------------------------*/ void setOutputStyle(int nstyle); /* 0 - ordinary output, 1 - xml output */ int outputStyleIsXML(void); int outputStyleIsGraphViz(void); int outputStyleIsASCII(void); int getOutputStyle(void); /*----------------------------------------------------------------------------*/ /* Returns 1 if the given ICMP Unreachable code warrants halting the trace. * Esoteric codes (need-fragment, source-route-fail, TOS variants, precedence, * src-isolated, routing oddities) are still recorded but do not stop probing. * Use --ignore-unreachables (-i) or adaptive mode (-E) to suppress all stops. */ static inline int lft_icmp_stops_trace(int code) { switch (code) { case 0: /* net unreachable */ case 1: /* host unreachable */ case 2: /* protocol unreachable */ case 3: /* port unreachable */ case 9: /* net administratively prohibited */ case 10: /* host administratively prohibited */ case 13: /* communication administratively prohibited */ return 1; default: return 0; } } /*----------------------------------------------------------------------------*/ #ifdef __cplusplus } #endif #endif /*LFT_LIB_H*/ lft-3.98/COPYING000644 000765 000024 00000006153 15165341545 013237 0ustar00vicstaff000000 000000 VOSTROM Public License for Open Source ---------- Copyright (c) 2001-2026 VOSTROM Holdings, Inc. This VOSTROM Holdings, Inc. (VOSTROM) Distribution (code and documentation) is made available to the open source community as a public service by VOSTROM. Contact VOSTROM at license@vostrom.com for information on other licensing arrangements (e.g. for use in proprietary applications). Under this license, this Distribution may be modified and the original version and modified versions may be copied, distributed, publicly displayed and performed provided that the following conditions are met: 1. Modified versions are distributed with source code and documentation and with permission for others to use any code and documentation (whether in original or modified versions) as granted under this license; 2. if modified, the source code, documentation, and user run-time elements should be clearly labeled by placing an identifier of origin (such as a name, initial, or other tag) after the version number; 3. users, modifiers, distributors, and others coming into possession or using the Distribution in original or modified form accept the entire risk as to the possession, use, and performance of the Distribution; 4. this copyright management information (software identifier and version number, copyright notice and license) shall be retained in all versions of the Distribution; 5. VOSTROM may make modifications to the Distribution that are substantially similar to modified versions of the Distribution, and may make, use, sell, copy, distribute, publicly display, and perform such modifications, including making such modifications available under this or other licenses, without obligation or restriction; 6. modifications incorporating code, libraries, and/or documentation subject to any other open source license may be made, and the resulting work may be distributed under the terms of such open source license if required by that open source license, but doing so will not affect this Distribution, other modifications made under this license or modifications made under other VOSTROM licensing arrangements; 7. no permission is granted to distribute, publicly display, or publicly perform modifications to the Distribution made using proprietary materials that cannot be released in source format under conditions of this license; 8. the name of VOSTROM may not be used in advertising or publicity pertaining to Distribution of the software without specific, prior written permission. This software is made available "as is", and VOSTROM DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL VOSTROM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. lft-3.98/icons/000755 000765 000024 00000000000 15174131662 013307 5ustar00vicstaff000000 000000 lft-3.98/whois.h000644 000765 000024 00000007777 15163651453 013523 0ustar00vicstaff000000 000000 /* * whois.h * Layer Four Traceroute * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * */ #ifndef WHOIS_H #define WHOIS_H struct ip_list_array { struct in_addr ipaddr[1024]; int asn[1024]; char netName[1024][32]; char orgName[1024][100]; char application[1024]; int numItems; }; struct ext_ip_list_array { struct in_addr ipaddr[1024]; int asn[1024]; char prefix[1024][20]; char netName[1024][32]; char orgName[1024][100]; char application[1024]; double latitude[1024]; double longitude[1024]; char country[1024][50]; char state[1024][50]; char city[1024][50]; char asOrgNameSource[1024][100]; char orgNameSource[1024][100]; char netNameSource[1024][100]; int geoavailable; int numItems; }; #ifdef __cplusplus extern "C" { #endif /* Forward-DNS cache entry used when c-ares is compiled in */ #ifdef HAVE_CARES struct w_dns_entry { const char *host; /* pointer to the static server-name string */ struct in_addr addr; /* resolved IPv4 address (zero if unresolved) */ int resolved; /* set to 1 by callback (success or failure) */ }; #endif typedef struct _whoissessionparams { int w_noisy; /* Don't show debug msgs by default */ char pw_serv[256]; /* User can specify his own pwhois server */ char consolidated_asn[256]; /* ASN returned from pwhois */ char consolidated_asp[256]; /* AS-PATH returned from pwhois */ char consolidated_route[256]; /* Prefix returned from pwhois */ char consolidated_orgname[256]; /* OrgName returned from pwhois */ char consolidated_netname[256]; /* NetName returned from pwhois */ char tbuf[128]; time_t tval; void * logprintfCookie; #ifdef HAVE_CARES struct w_dns_entry w_dns_cache[8]; /* pre-resolved whois server addresses */ int w_dns_cache_count; /* number of entries populated */ #endif }whois_session_params; /* must be called BEFORE making any queries */ whois_session_params * w_init(void); whois_session_params * w_reinit(whois_session_params * wsess); void w_close(whois_session_params * wsess); /* return the origin-asn according to the RADB in "3356" format */ int w_lookup_as(whois_session_params * wsess, char *); /* return the origin-asn according to Cyrmu in "3356" format */ int w_lookup_as_cymru(whois_session_params * wsess, char *); /* return the origin-asn according to the RIPE RIS in "3356" format */ int w_lookup_as_riswhois(whois_session_params * wsess, char *); /* return the origin-asn according to pwhois in "3356" format */ int w_lookup_as_pwhois(whois_session_params * wsess, char *); /* return the network name from the registrar in a string */ char *w_lookup_netname(whois_session_params * wsess, char *); /* return the organization name from the registrar in a string */ char *w_lookup_orgname(whois_session_params * wsess, char *); /* return a pointer to an ip_list_array (see above) containing an 'asn' to each corresponding 'ipaddr' according to Cymru */ int w_lookup_as_cymru_bulk(whois_session_params * wsess, struct ip_list_array*); /* return a pointer to an ip_list_array (see above) containing all ip_list_array vars to each corresponding 'ipaddr' according to pwhois */ int w_lookup_all_pwhois_bulk(whois_session_params * wsess, struct ip_list_array*); int w_lookup_all_pwhois_bulk_ext(whois_session_params * wsess, struct ext_ip_list_array *iplist); /* return a pointer to an ip_list_array (see above) containing all ip_list_array vars to each corresponding 'ipaddr' according to RIS whois */ int w_lookup_all_riswhois_bulk(whois_session_params * wsess, struct ip_list_array*); int w_lookup_all_pwhois(whois_session_params * wsess, char *addr); int w_lookup_all_riswhois(whois_session_params * wsess, char *addr); #ifdef __cplusplus } #endif #endif lft-3.98/lft_icmptrace.c000644 000765 000024 00000116040 15171551114 015151 0ustar00vicstaff000000 000000 #include "lft_icmptrace.h" static LFT_CALLBACK LFTErrHandler = NULL; static LFT_CALLBACK LFTEvtHandler = NULL; unsigned int icmp_rfc_send_request(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt); unsigned int icmp_base_send_hop(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt, short nhop); static void icmp_finish(lft_session_params *sess); static u_short in_cksum(u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register u_short answer; register int sum = 0; while(nleft > 1) { sum += *w++; nleft -= 2; } if(nleft == 1) { u_short u = 0; *(u_char *)(&u) = *(u_char *)w; sum += u; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return answer; } /* struct icmp_trace_packet_s { char * packet; int packet_len; struct ip * ip_hdr; struct rfc1393_ip_option_s * icmp_trace_opt; struct ip_lsrr * lsrr; struct icmp_echo_header_s * echo;; char * payload; int payload_len; }; */ #ifndef IPDEFTTL #define IPDEFTTL 200 #endif static const char payload_fill[]="VOSTROM"; int generateICMPPacket( lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt, struct icmp_trace_packet_s * packet, u_char ttlval, u_short unique_id, u_short seq) { char * currptr; int i,j; int userlen; (void)evt; if(packet->packet) free(packet->packet); //size of ip header packet->packet_len=20; //initial ip header if(sess->protocol==3) packet->packet_len+=12; //trace option if(sess->hostname_lsrr_size) packet->packet_len+=(sess->hostname_lsrr_size + 1)*4; //lsrr option //size of icmp header packet->packet_len+=sizeof(struct icmp_echo_header_s); //icmp header //size of payload /* Always reserve 2 bytes immediately after the ICMP echo header for a * flow-hash compensation word (see below). User payload, if any, * follows after those 2 bytes. Alignment padding applies only to the * user portion so that the overall packet layout is unchanged except * for the mandatory 2-byte prefix. */ userlen=sess->userlen; if(userlen) { userlen -= packet->packet_len + 2; /* account for the 2 adj bytes */ if(userlen<0) userlen = 0; if(userlen%4) userlen+=4-(userlen%4); } packet->packet_len += 2 + userlen; /* 2 adj bytes + optional user payload */ if(!(packet->packet=malloc(packet->packet_len))) { err(sess, ERR_NOT_ENOUGH_MEM, NULL); return 0; } memset(packet->packet, 0,packet->packet_len); //initialize ip header currptr = packet->packet; packet->ip_hdr = (struct ip *)(void *)packet->packet; packet->ip_hdr->ip_v = 4; packet->ip_hdr->ip_hl = 5; if(sess->hostname_lsrr_size) packet->ip_hdr->ip_hl += sess->hostname_lsrr_size + 1; if(sess->protocol == 3) packet->ip_hdr->ip_hl += 3; packet->ip_hdr->ip_id = unique_id; if(sess->set_tos) packet->ip_hdr->ip_tos = TOSMINDELAY; else packet->ip_hdr->ip_tos = 0; packet->ip_hdr->ip_off = IP_DF; packet->ip_hdr->ip_len = packet->packet_len; #ifndef SCREWED_IP_LEN packet->ip_hdr->ip_off = htons(packet->ip_hdr->ip_off); packet->ip_hdr->ip_len = htons(packet->ip_hdr->ip_len); #endif packet->ip_hdr->ip_p = IPPROTO_ICMP; packet->ip_hdr->ip_src = sess->local_address; packet->ip_hdr->ip_dst = sess->remote_address; if(sess->protocol==3) packet->ip_hdr->ip_ttl = IPDEFTTL; else packet->ip_hdr->ip_ttl = ttlval; currptr += sizeof(struct ip); //initialize ip option for rfc1393 trace /* struct rfc1393_ip_option_s { u_char optcode; //=82 u_char optlength; //=12 u_short id; //number to identify icmp trace messages u_short ohc; //outbound hop count u_short rhc; //return hop count struct in_addr origip; //originator ip address }; */ if(sess->protocol == 3) { packet->icmp_trace_opt = (struct rfc1393_ip_option_s *)(void *)currptr; packet->icmp_trace_opt->optcode = 82; packet->icmp_trace_opt->optlength = 12; packet->icmp_trace_opt->id = seq; packet->icmp_trace_opt->ohc = 0; packet->icmp_trace_opt->rhc = 0xFFFF; packet->icmp_trace_opt->origip = sess->local_address; currptr+=sizeof(struct rfc1393_ip_option_s); } else packet->icmp_trace_opt = NULL; /* struct ip_lsrr { u_int8_t ipl_code; u_int8_t ipl_len; u_int8_t ipl_ptr; u_int32_t data[9]; char padding[1]; } __attribute__((packed)); */ if(sess->hostname_lsrr_size) { packet->lsrr=(struct ip_lsrr *)(void *)currptr; currptr+=(sess->hostname_lsrr_size + 1)*4; for(i = 0; i < sess->hostname_lsrr_size; i++) packet->lsrr->data[i] = get_address(sess, sess->hostname_lsrr[i]); packet->lsrr->ipl_code = IPOPT_LSRR; packet->lsrr->ipl_len = sess->hostname_lsrr_size * 4 + 3; packet->lsrr->ipl_ptr = 4; } packet->ip_hdr->ip_sum = 0; #ifndef SCREWED_IP_LEN packet->ip_hdr->ip_sum = ip_cksum(packet->ip_hdr); #endif /* struct icmp_echo_header_s { u_char type; u_char code; u_short checksum; u_short id; u_short sequence; }; */ packet->echo=(struct icmp_echo_header_s *)(void *)currptr; currptr+=sizeof(struct icmp_echo_header_s); packet->echo->type=ICMP_ECHO; packet->echo->code=0; packet->echo->checksum=0; packet->echo->id=unique_id; packet->echo->sequence=seq; /* Flow-hash compensation word — always 2 bytes immediately after the * ICMP echo header. * * ECMP routers hash flows using fixed packet fields. For ICMP echo, * the fields most commonly included in that hash are: source IP, * destination IP, protocol, type, code, identifier, and (on some * implementations) the ICMP checksum. Type, code, and identifier are * already constant for the session. The sequence number must vary per * probe so that TTL-exceeded replies can be matched to the probe that * triggered them — but a varying sequence causes the checksum to vary. * * Fix: maintain a 2-byte compensation word `adj` at the start of the * payload such that (adj + seq) equals a fixed reference value in * 16-bit one's-complement arithmetic. Because the ICMP checksum is a * one's-complement sum over all header and payload bytes, the checksum * is then identical for every probe in the session regardless of seq. * * Reference value: the sequence number of the very first probe. On * that probe adj = 0x0000. On every subsequent probe: * adj = reference_seq - seq (one's-complement 16-bit subtraction) * = reference_seq + ~seq (with carry folded back into LSB) */ packet->payload = currptr; /* payload starts at adj word */ if (!sess->icmp_seq_anchored) { sess->icmp_initial_seq = seq; sess->icmp_seq_anchored = 1; /* adj = 0 for the first probe */ ((u_char *)currptr)[0] = 0; ((u_char *)currptr)[1] = 0; } else { uint32_t tmp = (uint32_t)sess->icmp_initial_seq + (uint32_t)(~seq & 0xFFFFu); uint16_t adj = (uint16_t)((tmp & 0xFFFFu) + (tmp >> 16)); ((u_char *)currptr)[0] = (adj >> 8) & 0xFF; ((u_char *)currptr)[1] = adj & 0xFF; } currptr += 2; packet->payload_len = 2; if(userlen) { for(i=0,j=0;ipayload_len += userlen; } sess->payload = NULL; sess->payloadlen = packet->payload_len; packet->echo->checksum=in_cksum((u_short *)(void *)packet->echo, sizeof(struct icmp_echo_header_s) + packet->payload_len); /*#ifndef SCREWED_IP_LEN packet->echo->checksum = htons(packet->echo->checksum); #endif*/ return packet->packet_len; } static int icmp_check_timeouts (lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt) { int nhop; int need_reply = 0; int no_reply = 0; int last_return = 0; LFTErrHandler=err; LFTEvtHandler=evt; gettimeofday (&(sess->now), NULL); if (timediff_ms(sess->ts_last_sent, sess->now) < sess->scatter_ms) return 0; /* not ready to send another packet yet */ if(sess->protocol==2) { for(nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if(!sess->hop_info[nhop].num_sent) { icmp_base_send_hop(sess, err, evt, nhop); return 0; } } for(nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if(sess->hop_info[nhop].num_sent <= sess->retry_max && !sess->hop_info[nhop].ts_last_recv.tv_sec) { if(sess->noisy > 4) { evt(sess,EVT_TTL_NO_REPLY,&nhop); if(sess->exit_state<0) return 0; } if(timediff_ms(sess->hop_info[nhop].ts_last_sent, sess->now) >= sess->timeout_ms) { /* we timed out waiting for this hop -- retry if we have any * more tries */ if(sess->hop_info[nhop].num_sent < sess->retry_max) { if(!sess->noisy && !sess->nostatus) evt(sess,EVT_PROGRESS_NO_REPLY,NULL); if(sess->noisy > 2) evt(sess,EVT_TTL_TOUT_RESEND,&nhop); if(sess->exit_state<0) return 0; icmp_base_send_hop(sess, err, evt, nhop); return 0; } else { /* PMTUD: flag this hop as a tentative black hole */ if (sess->pmtud && sess->protocol == 2) { sess->hop_info[nhop].pmtud_blackhole = 1; sess->hop_info[nhop].path_mtu = sess->pmtud_current_mtu; } no_reply++; } } else need_reply++; /* we have to wait for this one to timeout */ } else /* have reply */ last_return = nhop; } } else { if(!sess->hop_info[255].num_sent) { icmp_rfc_send_request(sess, err, evt); return 0; } if(sess->hop_info[255].num_sent <= sess->retry_max && !sess->hop_info[255].ts_last_recv.tv_sec) { if(sess->noisy > 4) { evt(sess,EVT_TTL_NO_REPLY,&nhop); if(sess->exit_state<0) return 0; } if(timediff_ms(sess->hop_info[255].ts_last_sent, sess->now) >= sess->timeout_ms) { /* we timed out waiting for this hop -- retry if we have any * more tries */ if(sess->hop_info[255].num_sent < sess->retry_max) { if(!sess->noisy && !sess->nostatus) evt(sess,EVT_PROGRESS_NO_REPLY,NULL); if(sess->noisy > 2) evt(sess,EVT_TTL_TOUT_RESEND,&nhop); if(sess->exit_state<0) return 0; icmp_rfc_send_request(sess, err, evt); return 0; } else no_reply++; } else need_reply++; /* we have to wait for this one to timeout */ } else /* have reply */ last_return = 0; } if(sess->noisy > 4) { EvtDebugCheckpoint1Param edcp; edcp.last_return=last_return; edcp.need_reply=need_reply; edcp.no_reply=no_reply; evt(sess,EVT_DBG_CHECKPOINT1,&edcp); if(sess->exit_state<0) return 0; } if(no_reply >= sess->ahead_limit) { /* we timed out. */ if((last_return + 3) * 2 < sess->hop_info_length) { if (sess->pmtud_verifying) return 0; /* re-entrant call during verification — do nothing */ if((need_reply < 3) && (sess->num_rcvd < 2)) evt(sess,EVT_CANT_RELIABLY_RTRIP,NULL); if(sess->exit_state<0) return 0; #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) if (sess->pmtud && sess->protocol == 2) lft_pmtud_verify_blackholes(sess); #endif icmp_finish(sess); return 1; } } if((!sess->num_hops || sess->hop_info_length < sess->num_hops || need_reply) && sess->hop_info_length < sess->ttl_limit) { if(sess->noisy > 4) evt(sess,EVT_HAVE_UNANSWERRED_HOPS,NULL); if(need_reply >= sess->ahead_limit){ if(sess->noisy > 4) evt(sess,EVT_TOO_FAR_AHEAD,NULL); return 0; /* wait for some replies before we go on */ } if(sess->exit_state<0) return 0; if(sess->num_hops > 0 && sess->hop_info_length >= sess->num_hops) { if(sess->noisy > 3) evt(sess,EVT_HAVE_GAPS,NULL); return 0; /* we know how long the path is - wait to fill in the blanks */ } nhop = sess->hop_info_length++; if(sess->protocol==2) icmp_base_send_hop(sess, err, evt, nhop); else { icmp_rfc_send_request(sess, err, evt); } } else { if (sess->noisy >= 4) { evt(sess, EVT_EITHER_RESP_OR_TOUT, NULL); if(sess->exit_state < 0) return 0; } if(sess->protocol == 2) { for(nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (sess->hop_info[nhop].num_sent < sess->retry_min && sess->hop_info[nhop].num_sent <= sess->retry_max) { icmp_base_send_hop(sess, err, evt, nhop); return 0; } } } else { if(sess->hop_info[255].num_sent < sess->retry_min && sess->hop_info[255].num_sent <= sess->retry_max) { icmp_rfc_send_request(sess, err, evt); return 0; } } if (sess->pmtud_verifying) return 0; /* re-entrant call during verification — do nothing */ #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) if (sess->pmtud && sess->protocol == 2) lft_pmtud_verify_blackholes(sess); #endif icmp_finish(sess); return 1; } return 0; } unsigned int icmp_rfc_send_request(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt) { struct trace_packet_info_s *pinfo = NULL; struct sockaddr_in dest; u_short rfc_unique_seq; unsigned int nseq; dest.sin_family = AF_INET; dest.sin_addr = sess->remote_address; dest.sin_port = 0; if(!(pinfo = (struct trace_packet_info_s *)malloc(sizeof(struct trace_packet_info_s)))) { err(sess, ERR_NOT_ENOUGH_MEM, NULL); return 0; } memset(pinfo, 0, sizeof(struct trace_packet_info_s)); nseq = new_seq(sess); nseq &= 0xFFFF; rfc_unique_seq = nseq; generateICMPPacket(sess, err, evt, &pinfo->u.icmp_packet, 0, sess->icmp_packet.echo->id, rfc_unique_seq); /* Packet is ready, fire away */ if(sendto(sess->send_sock, pinfo->u.icmp_packet.packet, pinfo->u.icmp_packet.packet_len, 0, (const struct sockaddr *)(const void *)&dest, sizeof (dest)) < 0) { LFTErrHandler(sess, ERR_RAW_TCP_DISABLED, NULL); free(pinfo); return 0; } pinfo->hopno = 0; pinfo->seq = rfc_unique_seq; pinfo->sent = sess->now; SLIST_INSERT_HEAD(&(sess->trace_packets), pinfo, next); sess->trace_packets_num++; /* we use special hop_info #255 */ SLIST_INSERT_HEAD(&(sess->hop_info[255].packets), pinfo, next_by_hop); sess->hop_info[255].num_sent++; sess->hop_info[255].all_sent++; sess->hop_info[255].ts_last_sent = sess->now; return 1; } unsigned int icmp_base_send_hop(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt, short nhop) { struct trace_packet_info_s *pinfo = NULL; struct sockaddr_in dest; u_short base_unique_seq; unsigned int nseq; EvtSentPacketParam espparam; dest.sin_family = AF_INET; dest.sin_addr = sess->remote_address; dest.sin_port = 0; nseq = new_seq(sess); nseq &= 0xFFFF; base_unique_seq = nseq; sess->ts_last_sent = sess->now; if(!(pinfo = (struct trace_packet_info_s *)malloc(sizeof(struct trace_packet_info_s)))) { err(sess, ERR_NOT_ENOUGH_MEM, NULL); return 0; } memset(pinfo, 0, sizeof(struct trace_packet_info_s)); espparam.flags=0; espparam.nhop=nhop; espparam.tseq=base_unique_seq; espparam.tttl=nhop+1; lft_o_probe_sent(); /* spinner: count in-flight probe */ if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_SENT_PACKET, &espparam); if(sess->exit_state <0 ) { free(pinfo); return 0; } } generateICMPPacket(sess, err, evt, &pinfo->u.icmp_packet, nhop+1, sess->icmp_packet.echo->id, base_unique_seq); /* Packet is ready, fire away */ { int _sret = sendto(sess->send_sock, pinfo->u.icmp_packet.packet, pinfo->u.icmp_packet.packet_len, 0, (const struct sockaddr *)(const void *)&dest, sizeof (dest)); if (_sret < 0) { /* printf("errno=%d\n",errno); */ LFTErrHandler(sess, ERR_RAW_TCP_DISABLED, NULL); free(pinfo); return 0; } } pinfo->hopno = nhop; pinfo->seq = base_unique_seq; pinfo->sent = sess->now; SLIST_INSERT_HEAD(&(sess->trace_packets), pinfo, next); sess->trace_packets_num++; if(nhop != -1) { SLIST_INSERT_HEAD(&(sess->hop_info[nhop].packets), pinfo, next_by_hop); sess->hop_info[nhop].num_sent++; sess->hop_info[nhop].all_sent++; sess->hop_info[nhop].ts_last_sent = sess->now; } return 1; } static int icmp_recv_packet (lft_session_params * sess, unsigned int seq, struct in_addr ipaddr, int icmp_type, int icmp_code, const void * pack, const struct pcap_pkthdr *hdr) { struct trace_packet_info_s *tp = NULL, *pinfo = NULL; const struct icmp_trace_reply_header_s *icmpheader = (const struct icmp_trace_reply_header_s *)pack; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) gettimeofday (&(sess->now), NULL); #else sess->now.tv_sec = hdr->ts.tv_sec; sess->now.tv_usec = hdr->ts.tv_usec; #endif /* First, search every probe to find an exact sequence match */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if(tp->seq == seq) { break; } } if(!tp) { if (sess->noisy) LFTEvtHandler(sess,EVT_SKIP_PACKET,NULL); else if (!sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_SKIP_PACKET,NULL); return 0; } if (tp->recv.tv_sec) { if (sess->noisy) LFTEvtHandler(sess,EVT_DUPLICATE_PACKET, NULL); else if (!sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_DUPLICATE,NULL); return 0; } /* Encode and set tp->icmp_type BEFORE firing EVT_RECV_PACKET so the * handler reads the correct value (race-condition fix). * Encoding mirrors lft_lib.c's recv_packet() convention: * -2 = TTL Exceeded (ICMP_TIMXCEED, type 11 code 0) * -1 = target reached (ICMP echo reply) * 0-15 = ICMP type-3 UNREACH sub-code (icmp_code) */ if (icmp_type == ICMP_TIMXCEED) tp->icmp_type = -2; else if (icmp_type == ICMP_ECHOREPLY) tp->icmp_type = -1; else if (icmp_type == ICMP_UNREACH) tp->icmp_type = icmp_code; /* preserve the actual sub-code */ else tp->icmp_type = icmp_type; /* ICMP_TRACE and any other raw types */ if (sess->noisy > 1) { EvtRecvPacketParam erpp; erpp.ipaddr=ipaddr; erpp.seq=seq; erpp.tp=tp; LFTEvtHandler(sess,EVT_RECV_PACKET,&erpp); } else { if (!sess->nostatus) { LFTEvtHandler(sess,EVT_PROGRESS_OK,NULL); } } if(sess->exit_state < 0) return 0; /* increment received packet counter */ sess->num_rcvd++; tp->recv = sess->now; if (tp->hopno != -1) { if(sess->protocol==3 && icmp_type == ICMP_TRACE) { tp->hopno=icmpheader->ohc; sess->hop_info[tp->hopno].num_sent = sess->hop_info[255].num_sent; sess->hop_info[tp->hopno].all_sent = sess->hop_info[255].all_sent; sess->hop_info[tp->hopno].ts_last_sent = sess->hop_info[255].ts_last_sent; sess->hop_info[tp->hopno].state = sess->hop_info[255].state; sess->hop_info[tp->hopno].flags = sess->hop_info[255].flags; } sess->hop_info[tp->hopno].ts_last_recv = sess->now; sess->hop_info[tp->hopno].all_rcvd++; /* indicate this hop has a sequence anomaly */ if(icmp_type==ICMP_UNREACH || icmp_type==ICMP_TIMXCEED) sess->hop_info[tp->hopno].flags |= HF_ENDPOINT; } tp->recv = sess->now; tp->hopaddr = ipaddr; #ifdef HAVE_CARES lft_ares_queue(sess, ipaddr); /* queue PTR lookup for this hop (deduped) */ #endif /* tp->icmp_type already set above, before EVT_RECV_PACKET */ if(icmp_type == ICMP_ECHOREPLY && ipaddr.s_addr == sess->remote_address.s_addr) { if(sess->protocol==3) { tp->hopno=sess->num_hops+1; sess->hop_info[tp->hopno].num_sent = sess->hop_info[255].num_sent; sess->hop_info[tp->hopno].all_sent = sess->hop_info[255].all_sent; sess->hop_info[tp->hopno].ts_last_sent = sess->hop_info[255].ts_last_sent; sess->hop_info[tp->hopno].state = sess->hop_info[255].state; sess->hop_info[tp->hopno].flags = sess->hop_info[255].flags; sess->hop_info[tp->hopno].ts_last_recv = sess->now; sess->hop_info[tp->hopno].all_rcvd++; } if(!sess->num_hops) { sess->num_hops = tp->hopno; if(!sess->num_hops) sess->num_hops=1; } tp->is_done = 1; } else { if(icmp_type == ICMP_UNREACH || icmp_type == ICMP_TIMXCEED) return 1; if(sess->protocol == 3 && icmp_type == ICMP_TRACE) { if(icmpheader->rhc == 0xFFFF) /* outbound packet */ { if(!(pinfo = (struct trace_packet_info_s *)malloc(sizeof(struct trace_packet_info_s)))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); return 0; } memcpy(pinfo, tp, sizeof(struct trace_packet_info_s)); SLIST_INSERT_HEAD(&(sess->hop_info[tp->hopno].packets), pinfo, next_by_hop); } else return 1; //while ignore return packets } } return 1; } static void icmp_process_packet(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt, const u_char *packet, const struct pcap_pkthdr *hdr) { const struct ip *ip, *orig_ip; const struct icmp *icmp; const struct icmp_echo_header_s *orig_echo, *resp_echo; LFTErrHandler=err; LFTEvtHandler=evt; if(sess->noisy > 4) { LFTEvtHandler(sess,EVT_PROCESS_PACKET_START,NULL); if(sess->exit_state<0) return; } icmp_check_timeouts(sess, err, evt); if(sess->exit_state<0) return; packet += sess->skip_header_len; ip = (const void *) packet; packet += 4 * ip->ip_hl; switch(ip->ip_p){ case IPPROTO_ICMP: orig_ip = ip; icmp = (const void *) packet; if(icmp->icmp_type != ICMP_ECHOREPLY && icmp->icmp_type != ICMP_UNREACH && !(sess->protocol==2 && icmp->icmp_type == ICMP_TIMXCEED) && !(sess->protocol==3 && icmp->icmp_type == ICMP_TRACE)) { return; } if(icmp->icmp_type == ICMP_ECHOREPLY) { resp_echo=(const struct icmp_echo_header_s *)icmp; if(resp_echo->id != sess->icmp_packet.echo->id) return; if (sess->noisy > 2) { EvtIncomingICMPEchoParam echo; echo.ip=orig_ip; echo.echo=resp_echo; LFTEvtHandler(sess,EVT_INCOMING_ICMP_Echo,&echo); if(sess->exit_state<0) return; } if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RCVD_ICMP_Echo,resp_echo); if(sess->exit_state<0) return; } icmp_recv_packet(sess, resp_echo->sequence, orig_ip->ip_src, ICMP_ECHOREPLY, 0, NULL, hdr); return; } if(icmp->icmp_type == ICMP_UNREACH || icmp->icmp_type == ICMP_TIMXCEED) { ip = &icmp->icmp_ip; if(ip->ip_p != IPPROTO_ICMP) return; packet = (const u_char *) ip; packet += 4 * ip->ip_hl; orig_echo = (const struct icmp_echo_header_s *)packet; if(orig_echo->type != ICMP_ECHO) return; if(sess->icmp_packet.echo->id != orig_echo->id) { return; } /* PMTUD: intercept PTB (type 3 code 4) before marking the hop answered */ if (sess->pmtud && sess->protocol == 2 && icmp->icmp_type == ICMP_UNREACH && icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { lft_pmtud_icmp_handle_ptb(sess, icmp, orig_echo->sequence, orig_ip->ip_src); return; } if (sess->noisy > 2) { EvtIncomingICMPICMPParam icmpicmp; icmpicmp.ip=orig_ip; icmpicmp.icmp=icmp; icmpicmp.orig_ip=ip; icmpicmp.echo=orig_echo; LFTEvtHandler(sess,EVT_INCOMING_ICMP_ICMP,&icmpicmp); if(sess->exit_state<0) return; } if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RCVD_ICMP_ICMP,icmp); if(sess->exit_state<0) return; } icmp_recv_packet(sess, orig_echo->sequence, orig_ip->ip_src, icmp->icmp_type, icmp->icmp_code, NULL, hdr); return; } if(icmp->icmp_type == ICMP_TRACE) { const struct icmp_trace_reply_header_s * itrh=(const struct icmp_trace_reply_header_s *)icmp; icmp_recv_packet(sess, itrh->id, orig_ip->ip_src, itrh->type, 0, itrh, hdr); return; } default: if(sess->noisy > 3) LFTEvtHandler(sess,EVT_RCVD_UNKNOWN,ip); } } #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) void win_icmp_process(lft_session_params * sess) { fd_set fds; struct timeval tm; tm.tv_sec = 0; tm.tv_usec = 100000; FD_ZERO(&fds); FD_SET(sess->recv_sock, &fds); if (select(sess->recv_sock+1, &fds, 0, 0, &tm) < 0) { LFTErrHandler(sess, ERR_WIN_SELECT, NULL); return; } if (FD_ISSET(sess->recv_sock, &fds)) { /* read packet */ char packetbuf[2048]; int nread; memset(packetbuf, 0, sizeof(packetbuf)); nread = recv(sess->recv_sock, packetbuf, sizeof(packetbuf), 0); if (nread <= 0) { LFTErrHandler(sess, ERR_WIN_RECV, NULL); return; } icmp_process_packet(sess, LFTErrHandler, LFTEvtHandler, packetbuf, NULL); } } #else void pcap_icmp_process_packet(u_char * user_data, const struct pcap_pkthdr *hdr, const u_char * packet) { lft_session_params * sess=(lft_session_params *)(void *)user_data; if(sess->exit_state<0) return; icmp_process_packet(sess, LFTErrHandler, LFTEvtHandler, packet, hdr); } #endif #if !defined(__CYGWIN__) && !defined(WIN32) && !defined(_WIN32) /* * Send a single ICMP verification probe to hop nhop. * Called by lft_pmtud_verify_blackholes() with sess->userlen already set * to the small verification MTU so generateICMPPacket() builds a tiny packet. */ unsigned int icmp_pmtud_send_verify_hop(lft_session_params *sess, short nhop) { return icmp_base_send_hop(sess, LFTErrHandler, LFTEvtHandler, nhop); } #endif void icmp_trace_main_loop(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt) { LFTErrHandler=err; LFTEvtHandler=evt; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) while(1) { win_icmp_process(sess); if(sess->exit_state < 0) break; if(icmp_check_timeouts(sess, err, evt)) break; if(sess->exit_state < 0) break; } #else while(pcap_dispatch(sess->pcapdescr, -1, pcap_icmp_process_packet, (u_char *)sess) >= 0) { if(sess->exit_state < 0) break; if(sess->noisy > 6) { LFTEvtHandler(sess, EVT_DBG_CHECKPOINT2, NULL); if(sess->exit_state < 0) break; } if(icmp_check_timeouts(sess,err,evt)) break; if(sess->exit_state<0) break; } #endif } static void icmp_finish(lft_session_params *sess) { int hopno; int maxhop; int reply, noreply; int as_for_hop = 0; struct trace_packet_info_s *tp; char *netname; /* int ocres; */ char *myApp = (char *)malloc((strlen(version) * sizeof(char)) + 1 + (strlen(appname) * sizeof(char))); struct ip_list_array *ipaslist = (struct ip_list_array *)malloc(sizeof(struct ip_list_array)); /* Variables for seam detection */ int icmpcode; int asseam_hopno=-1; struct in_addr asseam_hopaddr; int netseam_hopno=-1; struct in_addr netseam_hopaddr; struct in_addr classbmask; struct in_addr masked_target; int prevasn=-1; struct in_addr prevasn_hopaddr; int prevasn_hopno; int lastishole; int netreached=0; int isseam; /* ---------------------------- */ inet_aton("255.255.0.0", &classbmask); masked_target.s_addr=sess->remote_address.s_addr & classbmask.s_addr; EvtPacketInfoParam ehip; memset(ipaslist, 0, sizeof(struct ip_list_array)); gettimeofday (&(sess->trace_done_time), NULL); /* ocres=open_check(sess); LFTEvtHandler(sess,EVT_OPEN_CHECK_RESULT,&ocres); if(ocres==1) sess->target_open=1; else sess->target_open=0; */ if (sess->noisy > 3) { LFTEvtHandler(sess, EVT_SHOW_NUM_HOPS, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } if(sess->num_hops) { maxhop = sess->num_hops; /* display all packets we received from this host */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if (tp->is_done) { tp->hopno = maxhop; break; } } } else { maxhop = sess->hop_info_length - 1; } LFTEvtHandler(sess,EVT_TRACE_COMPLETED, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } /* if user wants ASN resolution from pwhois/cymru/riswhois, do it in bulk */ if (sess->do_aslookup || sess->do_netlookup) { if(sess->noisy > 1) { LFTEvtHandler(sess,EVT_ON_RESOLUTION, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } if (!sess->use_radb) { /* populate bulk ip_addr_list structure */ for (hopno = sess->ttl_min; hopno <= maxhop; hopno++) { SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_usec) { (*ipaslist).ipaddr[as_for_hop] = tp->hopaddr; as_for_hop++; (*ipaslist).numItems = (as_for_hop); break; } } } if (sess->use_cymru) { /* use cymru bulk service */ if (w_lookup_as_cymru_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } else if (sess->use_ris) { /* use RIPE NCC RIS service */ if (w_lookup_all_riswhois_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } else { /* use pwhois bulk service */ if ((strlen(version) * sizeof(char)) + 1 + (strlen(appname) * sizeof(char)) < 254) { *myApp = '\0'; strcat(myApp, appname); strcat(myApp, " "); strcat(myApp, version); strncpy((*ipaslist).application, myApp, 511); } if (w_lookup_all_pwhois_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } } free(myApp); #ifdef HAVE_CARES lft_ares_wait(sess); /* drain any pending PTR queries before display */ #endif LFTEvtHandler(sess,EVT_TRACE_REPORT_START, &maxhop); if(sess->exit_state < 0){ free(ipaslist); return; } /* seam detection */ as_for_hop=0; for(hopno = sess->ttl_min; hopno < sess->hop_info_length; hopno++) { struct in_addr last_hop; icmpcode=-100; last_hop.s_addr = 0; if(sess->hop_info[hopno].all_rcvd) { lastishole=0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if(tp->recv.tv_sec) { if(hopno<=maxhop) icmpcode=tp->icmp_type; if(last_hop.s_addr != tp->hopaddr.s_addr) { if((tp->hopaddr.s_addr & classbmask.s_addr) == masked_target.s_addr) netreached=1; else { netseam_hopno=hopno; netseam_hopaddr=tp->hopaddr; } if(sess->do_aslookup || sess->do_netlookup) { if (sess->use_radb) { /* using RADB/IRR */ tp->asnumber = w_lookup_as(sess->wsess, inet_ntoa(tp->hopaddr)); } else { /* using pwhois by default */ tp->asnumber = (*ipaslist).asn[as_for_hop]; } if(prevasn==-1) { if(tp->asnumber) { prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } else { if(tp->asnumber) { if(tp->asnumber!=prevasn) { asseam_hopno=prevasn_hopno; asseam_hopaddr=prevasn_hopaddr; } prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } } last_hop=tp->hopaddr; } } } as_for_hop++; } else lastishole=1; if(icmpcode==-1) break; } if(!netreached) netseam_hopno=-1; if(lastishole) asseam_hopno=-1; /* -------------- */ noreply = 0; reply = 0; as_for_hop = 0; /* this correlates the hopno to the asn stored in ipaslist */ for (hopno = sess->ttl_min; hopno <= maxhop; hopno++) { struct in_addr last_hop; if (sess->hop_info[hopno].all_rcvd != 0) { if (noreply >= 1) { if (sess->pmtud && getOutputStyle() == 0) { /* Per-hop PMTUD output. * [ICMP filtered]: suppresses TTL-exceeded for large probes * but forwards them (confirmed by a downstream response). * [MTU Limit]: did not respond; packet forwarding unconfirmed. * [neglected]: ignored probes of all sizes. */ int bh; for (bh = hopno - noreply; bh < hopno; bh++) { if (sess->hop_info[bh].pmtud_icmp_suppressor) printf("** [ICMP filtered] hop %d suppressed TTL-exceeded" " for DF probe (%u bytes) — packet confirmed" " forwarded\n", bh + 1, (unsigned)sess->hop_info[bh].path_mtu); else if (sess->hop_info[bh].pmtud_blackhole) printf("** [MTU Limit] hop %d did not respond to DF probe" " (%u bytes, no PTB received)\n", bh + 1, (unsigned)sess->hop_info[bh].path_mtu); else printf("** [neglected] no reply packets received" " from TTL %d\n", bh + 1); } } else { EvtNoReplyParam nrp; nrp.hopno=hopno; nrp.noreply=noreply; LFTEvtHandler(sess,EVT_RPT_NO_REPLY, &nrp); if(sess->exit_state < 0){ free(ipaslist); return; } } } } last_hop.s_addr = 0; if ((sess->hop_info[hopno].state == HS_SEND_FIN) && (sess->hop_info[hopno+1].state == HS_SEND_SYN) && (sess->hop_info[hopno+1].ts_last_recv.tv_sec)) { LFTEvtHandler(sess,EVT_RPT_FRW_INSPECT_PACKS, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if ((sess->hop_info[hopno].state != HS_SEND_SYN_ACK) && (sess->hop_info[hopno+1].state == HS_SEND_SYN_ACK) && (hopno == (sess->num_hops - 1))) { LFTEvtHandler(sess,EVT_RPT_FRW_STATE_FILTER, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if ((sess->hop_info[hopno].flags & HF_ENDPOINT) && (noreply >= ((maxhop - sess->ttl_min)/2)) && sess->num_hops > 3) { LFTEvtHandler(sess,EVT_RPT_BSD_BUG, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if (sess->hop_info[hopno].all_rcvd == 0) { reply = 0; } else { LFTEvtHandler(sess,EVT_RPT_HOP_INFO_START,&hopno); if(sess->exit_state < 0){ free(ipaslist); return; } //printf("hopno=%d all_rcvd=%d",hopno,sess->hop_info[hopno].all_rcvd); SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec) { reply = 1; if (last_hop.s_addr != tp->hopaddr.s_addr) { ehip.asnumber = 0; /* init/clear the ASN */ if (sess->do_aslookup) { if (sess->use_radb) { /* using RADB/IRR */ ehip.asnumber = w_lookup_as(sess->wsess, inet_ntoa(tp->hopaddr)); } else { /* using pwhois by default */ ehip.asnumber = (*ipaslist).asn[as_for_hop]; } } tp->asnumber=ehip.asnumber; ehip.netname=NULL; if (sess->do_netlookup) { if (!sess->do_aslookup || (sess->do_aslookup && !sess->use_cymru && !sess->use_radb)) { netname = (*ipaslist).netName[as_for_hop]; } else { netname = w_lookup_netname(sess->wsess, inet_ntoa(tp->hopaddr)); } ehip.netname = netname; } if(ehip.netname) strncpy(tp->netname, ehip.netname, 511); else tp->netname[0]=0; /* Orgname — pwhois bulk only (mirrors lft_lib.c path). * Fire whenever pwhois ran: do_aslookup OR do_netlookup. */ tp->orgname[0] = 0; if ((sess->do_aslookup || sess->do_netlookup) && !sess->use_cymru && !sess->use_radb) strncpy(tp->orgname, (*ipaslist).orgName[as_for_hop], sizeof(tp->orgname) - 1); } ehip.last_hop = last_hop; tp->last_hop=ehip.last_hop; last_hop = tp->hopaddr; } ehip.tp = tp; /* PMTUD per-hop annotations */ ehip.path_mtu = sess->hop_info[hopno].path_mtu; ehip.pmtud_blackhole = sess->hop_info[hopno].pmtud_blackhole; /* seam processing */ isseam=0; ehip.is_asseam=0; ehip.is_netseam=0; ehip.is_open=0; ehip.is_filtered=0; ehip.seam_traced=0; if(sess->check_seam && hopno==asseam_hopno && tp->hopaddr.s_addr==asseam_hopaddr.s_addr) { isseam=1; ehip.is_asseam=1; } if(sess->check_seam && hopno==netseam_hopno && tp->hopaddr.s_addr==netseam_hopaddr.s_addr) { isseam=1; ehip.is_netseam=1; } if(isseam) { if(sess->check_seam) { int curroutputstyle=getOutputStyle(); char hostname[100]; ehip.seam_traced=1; setOutputStyle(2); lft_session_params * subsess=LFTSessionOpen(); strncpy(hostname, inet_ntoa(tp->hopaddr),100); subsess->senddevsel = 1; subsess->senddev = sess->pcap_send_dev; subsess->userdevsel = 1; subsess->userdev = sess->pcap_dev; subsess->auto_ports=0; subsess->dport=179; subsess->seq_start=30; subsess->retry_min=1; subsess->retry_max=1; subsess->resolve_names=0; subsess->ahead_limit=1; subsess->break_on_icmp = 0; subsess->is_graphviz_subquery=1; subsess->hostname=hostname; subsess->hostname_lsrr_size = 0; LFTExecute(subsess); ehip.is_open=subsess->target_open; ehip.is_filtered=subsess->target_filtered; LFTSessionClose(subsess); setOutputStyle(curroutputstyle); } } /* --------------- */ LFTEvtHandler(sess,EVT_RPT_PACKET_INFO,&ehip); if(sess->exit_state < 0){ free(ipaslist); return; } } LFTEvtHandler(sess,EVT_RPT_PACKET_LIST_END,NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if (reply) { noreply = 0; as_for_hop++; } else noreply++; reply = 0; } /* for(...) */ if (!sess->num_hops) { LFTEvtHandler(sess,EVT_RPT_NO_HOPS,&maxhop); } if (sess->timetrace) { LFTEvtHandler(sess,EVT_RPT_TIME_TRACE,NULL); } LFTEvtHandler(sess,EVT_ON_EXIT,NULL); free(ipaslist); return; } lft-3.98/lft_lsrr.h000644 000765 000024 00000001265 11053131665 014173 0ustar00vicstaff000000 000000 /* * lft_lsrr.h * Layer Four Traceroute * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * */ #ifndef LFT_LSRR_H #define LFT_LSRR_H #if defined(sun) typedef uint8_t u_int8_t; typedef uint32_t u_int32_t; #endif struct ip_lsrr { u_int8_t ipl_code; /* IPOPT_TS */ u_int8_t ipl_len; /* size of structure (variable) */ u_int8_t ipl_ptr; /* index of current entry */ u_int32_t data[9]; char padding[1]; } __attribute__((packed)); #define IP_LSRR_DEST 8 #endif lft-3.98/lft_watch.h000644 000765 000024 00000021245 15171551242 014321 0ustar00vicstaff000000 000000 /* * lft_watch.h -- ncurses continuous monitoring mode for LFT * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. */ #ifndef LFT_WATCH_H #define LFT_WATCH_H #ifdef HAVE_NCURSES #include "lft_lib.h" #include "whois.h" /* ------------------------------------------------------------------------- * Tunables * ------------------------------------------------------------------------- */ #define WATCH_DEFAULT_INTERVAL 5 /* seconds between trace cycles */ #define WATCH_DEFAULT_PROBES 3 /* probes per hop when -j not given */ #define WATCH_MIN_ROWS 24 /* minimum terminal height */ #define WATCH_MAX_HOPS 256 #define WATCH_HISTORY_LEN 512 /* per-hop RTT history slots (circular) */ #define WATCH_MAX_ALT_IPS 4 /* multipath IPs per TTL */ #define WATCH_LOG_BUF 131072 /* log drawer ring buffer (bytes) — ~10 traces at -VV */ #define WATCH_PATH_FLASH_SECS 3 /* seconds to show PATH CHANGE notice */ #define WATCH_SPINNER_FRAMES 8 /* braille spinner frame count */ /* ------------------------------------------------------------------------- * Per-IP annotation cache * * A session-wide hash table keyed by IP address (IPv4 or IPv6) that stores * pWhoIs / reverse-DNS data. Every unique hop address gets exactly one * record here regardless of how many hops share it (multipath) or how many * cycles it is seen. The cache is the authoritative "have we queried this?" * source — watch_hop_t.asn/netname/orgname/hostname are just display copies * populated from the cache. * ------------------------------------------------------------------------- */ #include /* struct in_addr, struct in6_addr */ /* 16-byte address union — IPv4 occupies the first 4 bytes, rest are zero */ typedef union { struct in_addr v4; /* AF_INET */ struct in6_addr v6; /* AF_INET6 */ uint8_t raw[16]; /* for hashing / comparison */ } watch_ipaddr_t; /* One record per unique IP address */ typedef struct watch_ip_record_s { int af; /* AF_INET or AF_INET6 */ watch_ipaddr_t addr; /* hash key */ char asn[32]; /* e.g. "AS6939" */ char netname[64]; /* e.g. "HURRICANE-1" */ char orgname[64]; /* e.g. "Hurricane Electric" */ char hostname[256]; /* reverse-DNS PTR name */ int whois_queried; /* 1 = pWhoIs attempted */ int rdns_queried; /* 1 = rDNS attempted */ struct watch_ip_record_s *next; /* collision chain */ } watch_ip_record_t; #define WATCH_IP_CACHE_BUCKETS 256 /* power of 2; plenty for any trace */ typedef struct { watch_ip_record_t *buckets[WATCH_IP_CACHE_BUCKETS]; int count; /* total records allocated */ } watch_ip_cache_t; /* ------------------------------------------------------------------------- * Per-hop rolling accumulator * ------------------------------------------------------------------------- */ typedef struct { char host[64]; /* primary IP or hostname */ char alt_hosts[WATCH_MAX_ALT_IPS][64]; /* alternate IPs (multipath) */ int n_alt; /* number of alt_hosts populated */ int cycles_seen; /* cycles in which this hop replied */ int cycles_total; /* total cycles run when this hop was active */ double rtt_min; /* all-time minimum RTT across all cycles (ms) */ double rtt_avg; /* running average RTT (ms) */ double rtt_max; /* all-time maximum RTT (ms) */ double rtt_jitter; /* rtt_max - rtt_min this cycle */ double rtt_last; /* most recent RTT reading (ms) */ double rtt_sum; /* accumulator for running average */ double rtt_history[WATCH_HISTORY_LEN]; /* circular buffer, 0 = no reply */ int history_head; /* next write index */ int history_count; /* samples stored (capped at WATCH_HISTORY_LEN) */ /* Display copies — populated from the session's ip_cache on demand. * The cache is authoritative; these are just what the renderer reads. */ char asn[32]; /* AS number string, e.g. "AS7922" */ char netname[64]; /* network name from pWhoIs/ARIN */ char orgname[64]; /* organisation name from pWhoIs (F2) */ char hostname[256]; /* reverse-DNS name for hop IP (F4) */ } watch_hop_t; /* ------------------------------------------------------------------------- * Overall watch session state * ------------------------------------------------------------------------- */ typedef struct { watch_hop_t hops[WATCH_MAX_HOPS]; int n_hops; /* highest TTL seen */ int cycle; /* current cycle number (1-based) */ int max_cycles; /* 0 = run indefinitely */ int interval; /* seconds between traces */ int probes_per_hop; /* -j N value (probes per hop per cycle) */ int paused; /* 1 = traces suspended */ int running; /* 0 = quit requested */ int current_view; /* 1-4 */ int log_open; /* 1 = log drawer visible */ int whois_enabled; /* 1 = show ASN/NetName columns */ /* Cycling: during a trace, is it the first cycle or a re-run? */ int first_cycle; /* Log drawer ring buffer (captures -VV style output) */ char log_buf[WATCH_LOG_BUF]; int log_head; /* oldest byte position (ring-buffer head) */ int log_len; /* bytes currently stored */ /* Log drawer scroll state */ int log_scroll_offset; /* lines scrolled up from frozen anchor; 0 = live */ int log_anchor_nlines; /* total lines when user first scrolled (frozen anchor); * 0 means live — cleared when returning to live mode */ time_t log_last_scroll; /* wall time of last ↑/↓/k/j keypress (30 s timeout) */ /* Path change detection */ char path_snapshot[WATCH_MAX_HOPS][64]; int path_changed; time_t path_change_time; /* Terminal geometry */ int term_rows; int term_cols; /* In-flight trace state (set by event callback) */ int in_trace; /* 1 while LFTExecute is running */ int last_hop_flashed; /* most recent TTL that was flashed */ /* Target description for header line */ char tgt_label[128]; /* e.g. "tcp://208.74.248.40:443" */ /* Target reach state (updated each cycle) */ int target_hopno; /* hop index of the target (0 = not reached) */ int target_open; /* 1 = OPEN (SYN-ACK) */ int target_filtered; /* 1 = FILTERED (ICMP port unreachable) */ int protocol; /* LFT probe protocol (0=TCP, 1=UDP, 2=ICMP, 3=ICMP-RFC1393) */ /* TCP: !open && !filtered → [CLOSED] (RST); ICMP: !open && !filtered → [REPLIED] (Echo Reply) */ /* Per-IP annotation cache (pWhoIs + rDNS); keyed by address */ watch_ip_cache_t ip_cache; /* pWhoIs live lookup state (F2) */ whois_session_params *watch_whois_wsess; /* allocated on first 'w' press */ int whois_bulk_done; /* 1 = bulk query already completed */ /* Hostname display toggle (F4) */ int show_hostnames; /* 1 = show PTR names instead of IPs */ /* Spinner animation frame counter (0..WATCH_SPINNER_FRAMES-1) */ int spinner_frame; /* Set by key handlers that require immediate trace start (breaks idle loop) */ int interrupt_sleep; /* Cumulative probe counters — accumulate across all cycles; reset with 'r'. * max_hops_seen and target_ever_reached are sticky-max / sticky-true. */ int total_replies; /* Σ probe replies received */ int total_sent; /* Σ probes sent */ int max_hops_seen; /* peak hop count ever seen */ int target_ever_reached; /* 1 once target replied in any cycle */ /* Transient status bar message (F3 — shown for ~3 s after a save) */ char status_msg[192]; /* if non-empty and not expired, shown in status bar */ time_t status_msg_time; /* time() when the message was set */ } watch_session_t; /* ------------------------------------------------------------------------- * Public interface — called from lft_lib.c EVT_ON_EXIT handler * ------------------------------------------------------------------------- */ void WatchModeRun(lft_session_params *sess); #endif /* HAVE_NCURSES */ #endif /* LFT_WATCH_H */ lft-3.98/lft.8000644 000765 000024 00000106202 15173526114 013052 0ustar00vicstaff000000 000000 .Dd April 7, 2026 .Dt LFT 8 .Os LFT .Sh NAME .Nm lft .Nd display the route packets take to a network host/socket using one of several layer-4 protocols and methods; optionally show heuristic network information in transitu .Sh SYNOPSIS .Nm lft .Op Fl d Ar dport .Op Fl s Ar sport .Op Fl m Ar retry min .Op Fl M Ar retry max .Op Fl a Ar ahead .Op Fl c Ar scatter ms .Op Fl t Ar timeout ms .Op Fl l Ar min ttl .Op Fl H Ar max ttl .Op Fl L Ar length .Op Fl q Ar ISN .Op Fl D Ar device .Op Fl f Ar device .Op Fl G Ar icons path .Op Fl ACEINPRSTUVbeghinpruvxyzK .Ar [ <...>] .Ar target:dport .Sh DESCRIPTION The Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking the route one's packets follow (or finding the miscreant gateway that's discarding your packets) can be difficult. (from traceroute(8)) .Pp .Nm was developed to automate a solution to the above, taking into account that modern networks contain many configurations of load balancers, proxies, and stateful firewalls. .Pp .Nm implements numerous network tracing methods and strategies. Generally, .Nm sends various types of layer-4 probes utilizing the IP protocol `time to live' field and attempts to elicit an .Tn ICMP `time exceeded in transit' response from each gateway along the path to some host. RFC 1393 Traceroute Using an IP Option is also available as one of several tracing methods. .Pp .Nm additionally listens for various messages along the way to assist network managers in ascertaining per-protocol heuristic routing information. .Nm can optionally retrieve various information about the networks it traverses using a variety of sources such as registries, routing arbiters, etc. .Pp The only mandatory parameter is the target host name or IP number. Options toggle the display of more interesting data or change the variables of the trace itself. The (-E/-e) adaptive option tries several combinations of TCP states (changing flags inside the probes it sends) in order to improve the chances of a successful trace and expose stateful packet filters. .Pp Other options are: .Bl -tag -width Ds .It Fl d Ar dport , Fl -dport Ar dport Set .Ar dport as the destination TCP port of the probes LFT generates. Default is 443. This option is useful to see if packets follow a different route based on protocol destination, a likely scenario when load balancers or proxies are involved. This option may also bypass less sophisticated packet filter configurations. .It Fl s Ar sport , Fl -sport Ar sport Set .Ar sport as the origin TCP port of the probes LFT generates. By default, LFT selects a random port from the IANA dynamic port range (49152\(en65535). This option is useful to see if packets follow a different route based on protocol source. This option may also bypass less sophisticated packet filter configurations. .It Fl z , Fl -random-sport Re-randomize the source port, selecting a new random port from the IANA dynamic range (49152\(en65535) for this invocation. Useful when you want to ensure a fresh port selection rather than reusing one from a prior run or a local packet filter or proxy that may restrict specific source port values. .It Fl m Ar min , Fl -min-probes Ar min Set .Ar min as the minimum number of probes to send per hop. Default is 1. .It Fl M Ar max , Fl -max-probes Ar max Set .Ar max as the maximum number of probes to send per hop. Default is 2. .It Fl a Ar ahead , Fl -ahead Ar ahead Set .Ar ahead as the number of hops forward to query before waiting for a response. Default is 5. .It Fl c Ar scatter ms , Fl -scatter Ar scatter ms Set .Ar scatter ms as the minimum number of milliseconds to wait between sending probes. Default is 20. .It Fl t Ar timeout ms , Fl -timeout Ar timeout ms Set .Ar timeout ms as the maximum number of milliseconds to wait before assuming a probe was lost/discarded. Default is 250. .It Fl l Ar min ttl , Fl -min-ttl Ar min ttl , Fl -first-hop Ar min ttl Set .Ar min ttl as the minimum TTL (time-to-live) on outgoing probes (essentially, the first hop in the line that you want to display). Default is 1. .It Fl q Ar ISN , Fl -isn Ar ISN Set .Ar ISN as the ISN (initial sequence number) of the first probe. If unset, one will be automatically generated using a pseudo-random, time-seeded algorithm. .It Fl w Ar window , Fl -tcp-window Ar window Set .Ar window as the TCP window size advertised in outgoing probe packets, in bytes. This option applies to TCP-based traces only. Default is 32768. .It Fl L Ar length , Fl -length Ar length , Fl -bytes Ar length Set .Ar length as the size of probe packets in bytes. This includes layer-3 and layer-4 headers, but does not include layer-2 headers. For example, setting the length to 1500 would create a 1500-byte probe packet which would result in a 1514-byte frame on an Ethernet network. This option is silently ignored when .Fl K is active; probe size is managed automatically by PMTUD. .It Fl K , Fl -mtu , Fl -path-mtu Enable active Path MTU Discovery (PMTUD). When this flag is set, .Nm queries the local interface MTU via .Xr ioctl 2 .Dv SIOCGIFMTU and starts probing at that size (typically 1500 bytes for Ethernet, up to 9000 for jumbo frames). All probes carry the DF (Don't Fragment) bit, which is already .Nm Ns 's default. .Pp When a router cannot forward a probe because it exceeds an outbound link MTU, it returns an ICMP .Dq fragmentation needed message (type\ 3, code\ 4) containing the next-hop MTU. This class of message is colloquially called a .Dq PTB .Pq Packet Too Big , a term that strictly refers to ICMPv6 Type\ 2 but is widely used for both IPv4 and IPv6. .Nm records the MTU at the responding hop and reduces subsequent probe sizes accordingly. .Pp If a router silently drops an oversized DF probe without returning an ICMP message .Pq a PMTUD black hole , .Nm flags the hop as .Dq [MTU Limit] and steps down to the next RFC\ 1191 plateau value to continue probing beyond the black hole. .Pp PMTUD measures the forward-path MTU only; the return path may differ. Discovery stops at the first hop that limits the path MTU; hops beyond it see reduced-size probes that pass freely. .Pp .Sy Probe design and known limitations. .Nm implements PMTUD by inflating the payload of its existing TCP, UDP, and ICMP probes to the current MTU estimate. For TCP probes, strictly speaking, SYN, FIN, and RST segments should carry no payload; some deep-packet-inspection firewalls enforce this and will drop oversized probes before they reach a bottleneck link. UDP datagrams and ICMP echo requests may carry any payload size without protocol violation. This is a known constraint of the technique for TCP mode. .Pp An alternative approach \(em sending ICMP echo requests exclusively for MTU probing, as .Xr ping 8 does with .Fl M Ar do \(em was considered but rejected. ICMP traffic is routinely deprioritized in router control planes and discarded wholesale by enterprise firewalls, making it a less faithful representation of real traffic and no more likely to traverse a restricted path than a TCP probe. .Pp The payload-inflation approach is preferred because it exercises the same forwarding path as real application traffic. PTB messages are generated by routers at the IP layer, before any TCP inspection occurs, so the bottleneck is still correctly identified even when the probe carries an atypical payload. On the public Internet backbone \(em where MTU constraints most commonly arise \(em core routers perform IP-layer forwarding without TCP payload inspection, and the technique works reliably. Paths that traverse DPI firewalls configured to reject TCP-with-payload would equally reject any active PMTUD probing strategy; this is not a limitation specific to .Nm . .It Fl D Ar device , Fl -device Ar device , Fl -receive-device Ar device Set .Ar device as the network device or address to receive traffic. (e.g., "en1" or "1.2.3.4") If unset, .Nm will attempt to determine and acquire the appropriate interface based on routing. .It Fl f Ar device , Fl -from-device Ar device , Fl -source-device Ar device Set .Ar device as the network device or address to transmit traffic. (e.g., "en1" or "1.2.3.4") If unset, .Nm will attempt to determine and acquire the appropriate interface based on routing. This serves to operate .Nm in a passive mode where you may transmit from a (potentially) spoofed IP address on one interface, yet receive on another. This allows you to trace from a different IP address whose traffic you can see in order to intercept replies. .It Fl H Ar ttl , Fl -max-hops Ar ttl , Fl -ttl-max Ar ttl Set .Ar ttl as the maximum TTL, essentially the maximum route traversal distance in hops. Default is 30. .It Fl G Ar icons path , Fl -graphviz-icons Ar icons path Set .Ar icons path as the path to GraphViz icons in connection with GraphViz output. .It Fl I , Fl -minimize-delay Set the ToS (Type of Service) bit on outgoing IP datagrams. The ToS will be set to the differentiated services request minimize-delay. .It Fl i , Fl -ignore-unreachables Ignore ICMP unreachable messages; the trace continues through firewalls and other devices that send administratively-prohibited or host/net unreachable responses. By default, LFT stops the trace upon receiving any of the following ICMP unreachable codes: net unreachable (0), host unreachable (1), protocol unreachable (2), port unreachable (3), net administratively prohibited (9), host administratively prohibited (10), or communication administratively prohibited (13). This flag is implied by adaptive mode .Pq Fl E . .It Fl n , Fl -numeric Print addresses numerically rather than symbolically and numerically. Disables use of the DNS resolver completely. .It Fl h , Fl -hostnames-only Print addresses symbolically rather than symbolically and numerically. If the DNS resolver fails to resolve an address, the address is printed numerically. .It Fl E | Fl e , Fl -adaptive Enable use of the adaptive engine which tries several combinations of TCP states (changing flags inside the probes it sends) in order to improve the chances of a successful trace. The engine also displays other useful information such as stateful inspection firewalls or broken IP stacks encountered along the way. Adaptive mode automatically implies .Fl i .Pq Fl -ignore-unreachables , allowing the trace to continue through devices that send ICMP unreachable responses rather than halting. .It Fl F , Fl -fin Enable use of TCP packets with the FIN flag set. This strategy fools unsophisticated packet filters that don't maintain a proper state table. Such devices will forward the packet to its destination rather than filter it, assuming a handshake has already taken place and the probes are part of an existing and valid TCP stream. .It Fl u , Fl -udp Enable use of UDP-based probes instead of TCP-based probes. Unlike the traditional traceroute method, which increments the UDP destination port with each TTL to identify replies, LFT sends all UDP probes to the same destination port and embeds a pseudorandom session-unique identifier in the IP .Em ID field of each probe. Replies are matched to probes via the .Em ID value echoed back in ICMP time-exceeded messages, keeping the five-tuple (source IP, destination IP, protocol, source port, destination port) constant across all TTLs. This ensures every probe follows the same path through ECMP load-balanced networks. Many of LFT's other options (such as source and destination port selection) are still available. By default, LFT's UDP probes carry a small payload (unlike TCP probes, which carry none). .It Fl N , Fl -netname Enable lookup and display of network or AS names (e.g., [GNTY-NETBLK-4]). This option queries Prefix WhoIs, RIPE NCC, or the RADB (as requested). In the case of Prefix WhoIs or RADB, the network name is displayed. In the case of RIPE NCC, the AS name is displayed. .It Fl P Enable RFC 1393 tracing method using ICMP and an IP option. RFC 1393 (Traceroute Using an IP Option), published in January 1993, defined an elegant alternative to the traditional Van Jacobson traceroute method: rather than relying on TTL expiry and ICMP Time Exceeded messages, a single outbound packet carries a Traceroute IP option (type 82) that instructs each router in the path to send an ICMP Traceroute message back to the originator. In theory, this requires only n+1 packets instead of the 2n packets used by traditional traceroute. .Pp In practice, no major router vendor \(em Cisco, Juniper, Nokia, Huawei, or any other \(em ever implemented RFC 1393 in their forwarding path. The mechanism suffered a chicken-and-egg problem: it required every router in the path to support the new IP option, whereas Van Jacobson's 1987 traceroute exploited TTL-expiry behavior already present in all routers and was universally deployed by the time RFC 1393 arrived. IP options processing also carries security risks, as options are handled in the router's slow path (control plane) and can be exploited for DoS amplification. Over time, ISPs and enterprises increasingly configured equipment to strip or drop packets carrying IP options entirely. .Pp RFC 1393 never advanced beyond Experimental status. It was formally deprecated by RFC 6814 in November 2012, which moved it to Historic. RFC 7126 (February 2014) further recommends that routers SHOULD drop packets containing the Traceroute IP option, noting zero operational impact from doing so. This option is retained in .Nm for completeness, but is unlikely to elicit responses from any production network equipment. .It Fl p , Fl -icmp Enable use of ICMP-based probes instead of TCP-based probes. This strategy is sometimes the fastest, however firewalls commonly filter ICMP at network borders. ICMP probes are echo request (ping) packets. .It Fl b , Fl -basic-tcp Enable TCP basic tracing method. Unlike the default method, the basic method generates TCP probes without relying upon sequence numbers being conveyed correctly. This makes LFT more comptabile with networks employing NAT, but is slower than the default method. TCP basic may also be used with adaptive mode (-E). .It Fl A , Fl -asn Enable lookup and display of AS (autonomous system) numbers (e.g., [1]). This option queries one of several whois servers (see options 'C' and 'r') in order to ascertain the origin ASN of the IP address in question. By default, LFT uses the pWhoIs service whose ASN data tends to be more accurate and more timely than using the RADB as it is derived from the Internet's global routing table. See www.pwhois.org .It Fl r , Fl -ripe-riswhois Force use of the RIPE NCC RIS whois service to lookup ASNs. This is an alternative source of timely ASN-related information built using the Internet's global routing table. See www.ripe.net/projects/ris .It Fl C , Fl -cymru Force use of the Cymru whois service to lookup ASNs. This is an alternative source of timely ASN-related information built using the Internet's global routing table. See www.cymru.com .It Fl R , Fl -radb Force use of the RADB whois service to lookup ASNs. This tends to be quick, but incomplete and usually inaccurate with regard to the 'actual' Internet routing table. See www.radb.net .It Fl T , Fl -time-keeping Enable display of LFT's execution timer. This option places timers on the trace itself and on lookups and name resolution to show where LFT is spending its time, waiting on resolvers, or processing trace packets. Use with -V (verbose) to display additional detail. .It Fl U , Fl -time-keeping-utc Display all times in UTC/GMT0. This option also enables the -T option automatically. .It Fl S , Fl -quiet , Fl -silent Suppress display of the real-time status bar. This option makes LFT show its completed trace output only, no-frills. .It Fl x , Fl -xml Enable XML output and suppress all other output to stdout. .It Fl g , Fl -graphviz Enable GraphViz output and suppress all other output to stdout. .It Fl o , Fl -ascii Display the completed trace as a horizontal ASCII art hop diagram. Each hop occupies a column showing (top to bottom): the hop header, hostname, IP address, AS number (when .Fl A is used), network name (when .Fl N is used), round-trip time, and a feature annotation line for seam, firewall, or target-state information. .Pp In .Fl n mode the IP address appears on both the hostname and address lines for layout consistency. In .Fl h mode the IP address line is suppressed; TTL information for cloaked hops moves to the AS number line so it shares a row with AS data from other hops rather than occupying a mostly-blank line of its own. .Pp Cloaked hops, seam boundaries, firewall anomalies, and target state are each marked with a distinct feature label. In Unicode mode the labels are: .Sq \&\[u21F6] Stateful FW (stateful packet-inspecting firewall), .Sq \&\[u2691] Flag FW (flag-based packet filter), .Sq \&\[u21C4] BSD Bug (errant TTL reuse anomaly), .Sq \&\[u2573] ASN Seam (autonomous system boundary crossing), .Sq \&\[u2573] NET Seam (network boundary crossing), .Sq \&\[u2591]cloaked\[u2591] (no-reply hop), .Sq [OPEN] , .Sq [FILTERED] , and .Sq [CLOSED] (target port state). Plain ASCII equivalents are used on terminals that do not support Unicode. .Pp LFT automatically enables Unicode box-drawing characters and ANSI color when the terminal supports them (detected via .Ev TERM , .Ev COLORTERM , and .Xr isatty 3 ) . Set .Ev NO_COLOR to suppress color output. When the path is too wide for the terminal, the diagram wraps onto additional rows. .It Fl j Ar N , Fl -stats Ar N Collect .Ar N round-trip time samples per hop and display a candlestick RTT chart above the hop diagram (implies .Fl o ) . Each hop column renders a box-and-whisker bar: the solid block marks the average RTT; the top cap (\[u252C]) and bottom cap (\[u2534]) mark the maximum and minimum RTTs; the thin shaft (\[u2502]) between them shows the spread. Hops with no reply are rendered as a full-width column of shade blocks (\[u2591]). A Y-axis with labeled RTT ticks appears at the left edge; the X-axis footer shows the end-to-end RTT range derived from the target hop's own min/max samples and the destination in .Ar proto\[u200B]://\[u200B]address\[u200B]:\[u200B]port format (e.g.\& .Sq End-to-End RTT 15\[u2013]81ms, 6 hops to tcp://208.74.248.40:443 ) . If the target was not reached the footer reads .Sq N hops traced, target unreachable . Columns with a jitter spread exceeding 25\~ms are highlighted in yellow with a .Sq \&\[u26A1] Nms \[u0394] annotation. .It Fl -no-ascii-chart , Fl -no-async-chart Suppress the candlestick RTT chart produced by .Fl j while still collecting and displaying per-hop RTT statistics. Useful when the terminal is too narrow for the chart, when output is piped to another tool, or when only the numerical summary (min/avg/max/jitter) is needed. Has no effect unless .Fl j .Ar N is also specified. .It Fl W Ar N , Fl -watch Ns Op = Ns Ar N , Fl -continuous Ns Op = Ns Ar N Enable continuous monitoring mode, repeating the trace every .Ar N seconds (default:\~5). LFT runs in an interactive full-screen .Xr ncurses 3 display that shows per-hop RTT history, loss percentages, and network annotation (ASN, network name, organisation) in real time. .Pp Subsequent cycles auto-tune the probe range and timeouts from the previously learned path; see .Sy Topology inheritance below. .Pp .Sy View\ 1 (default) shows the .Fl o candlestick chart on the upper half of the screen with a rolling statistics table below it. .Sy View\ 2 replaces the chart with proportional RTT bar columns. .Sy View\ 3 is a full-height statistics table that shows more hops simultaneously. Switch views with .Sy 1 , .Sy 2 , or .Sy 3 . .Pp The statistics table columns are: .Bl -tag -width "NETNAME" -compact .It HOP TTL value. .It HOST IP address of the responding router (or PTR hostname when .Fl h is active; see below). .It LOSS% Packet loss percentage across all cycles observed. .It MIN / AVG / MAX Minimum, mean, and maximum round-trip time in milliseconds. .It JIT Jitter (max \- min over all cycles). .It ASN Autonomous system number from pWhoIs (e.g.\& 7922). Private and special-purpose addresses (RFC\~1918, RFC\~6598, loopback, link-local, etc.\&) are labelled with their IANA RFC tag (e.g.\& .Sq RFC1918 ) instead of a pWhoIs lookup. .It NETNAME Network name from pWhoIs (e.g.\& COMCAST-7922). .It ORGNAME Organisation name from pWhoIs (e.g.\& Comcast Cable). .It Sparkline Per-hop RTT history plotted as Unicode block characters, oldest sample on the left and newest on the right. Green indicates RTT within 20% of the hop average; yellow between 20% and 50% above average; magenta more than 50% above average; red indicates a lost probe. .El .Pp Intermediate hops that do not respond are shown as .Sq * in the HOST column. .Pp If .Fl j is not specified, each cycle collects 3 RTT samples per hop. .Pp During each trace a live counter line is displayed showing .Pq in order : replies received .Pq \[u2193]N , probes sent .Pq \[u2191]N , unique responding hops .Pq \[u2B21]\~N , and target reached status .Pq \[u2714] or \[u2717] . Before ncurses initialises this line appears on the terminal directly; once ncurses is running it occupies the bottom status row of every view. .Pp Keyboard controls: .Bl -tag -width "[+/-]" -compact .It Ic 1 \(en 3 Switch between the three display views. .It Ic p Pause or resume trace cycling. While paused, the status bar shows .Dq Paused... and no new traces run until .Ic p is pressed again. .It Ic r Reset all accumulated statistics (RTT history, loss counters, cycle count) and restart from a clean baseline. .It Ic c Clear all statistics, flush the DNS and pWhoIs cache, and redraw. This forces fresh lookups on the next cycle. .It Ic w Toggle pWhoIs annotation columns (ASN / NETNAME / ORGNAME). Annotation is enabled by default; a bulk pWhoIs query runs at startup and is refreshed as new hop addresses are discovered across cycles. Pressing this key shows or hides the annotation columns without discarding the cached data. .It Ic h Toggle hostname display: when on, the HOST column shows the PTR reverse-DNS name for each hop rather than its IP address. .It Ic v Open or close the log drawer, a panel at the bottom of the screen that shows the raw LFT verbose output captured during each trace cycle. .It Ic x Save the current screen to a file. LFT prompts for a filename (pre-filled with a timestamp) and writes both a plain-text .Pa .txt version and an ANSI-color .Pa .ansi version to the current directory. .It Ic + / Ic \- Increase or decrease the interval between cycles by one second. .It Ic q Quit watch mode and exit. .El .Pp Path changes are detected automatically: when the set of responding hop addresses changes between cycles the change is highlighted in the status bar and held for three seconds. .Pp For multi-cycle traces, watch mode preserves the per-protocol ECMP flow-identifier across cycles so every cycle traverses the same ECMP branch on multi-path networks: TCP source port, UDP IP-ID base, and the ICMP sequence-number anchor are all held constant for the life of the watch session. See .Xr lft 8 notes on ECMP flow-hash stability for the underlying mechanism. .Pp .Sy Topology inheritance .Pp After the first cycle, watch mode learns the path length and RTT envelope and uses them to accelerate subsequent cycles. Once the same number of hops has been observed for three consecutive cycles, the TTL probe range narrows to .Sy learned_num_hops + .Fl -learn-margin , the in-flight probe pipeline widens to the full known path width (capped at 16), and the cloaked-hop timeout drops to .Sy max(8 \(mu max_observed_rtt, 200 ms) . A cycle that misses the target or a change in probe protocol resets the learned state, reverting to broad-scan defaults until the path stabilizes again. .Bl -tag -width "--learn-margin" .It Fl -no-learn Disable watch-mode topology inheritance. Every cycle probes the full default TTL ladder with the conventional ahead-limit and timeout. .It Fl -learn-margin Ar N TTL probe margin above the learned path length (default\~2; clamped 0\~..\~10). A larger margin notices path lengthening sooner at the cost of probing empty TTLs. .El .Pp This option requires ncurses support compiled into LFT. If ncurses was not detected at build time, .Fl -watch prints an explanatory error and exits. The minimum supported terminal height is 24 rows. Superuser privileges (via .Xr sudo 8 or a setuid-root installation) are required. .It Fl y , Fl -verify-seam Enable network seam testing in connection with GraphViz output. .It Fl V , Fl -verbose Display verbose output. Use more V's for more info. .It Fl v , Fl -version Display version information, then exit(1). .It Fl Q , Fl -no-async-dns Disable asynchronous DNS resolution. When LFT is built with c-ares support (the default when the library is present at compile time), it transmits a reverse-DNS (PTR) query for each IP address revealed by an incoming ICMP time-exceeded response contemporaneously with the remaining probe traffic. By the time the trace completes, most or all hostnames have already been resolved, so lookups incur little or no additional time. The .Fl Q flag reverts to the traditional synchronous .Xr getnameinfo 3 path, which resolves each hop in sequence after the trace completes. This flag is useful for debugging DNS issues or for environments where parallel DNS queries are undesirable. If LFT was built without c-ares, this flag is accepted but has no effect. .It Fl -help Display a brief usage summary and exit. Long-form option equivalents (e.g., .Fl -dport , .Fl -min-probes , .Fl -ignore-unreachables ) are available for all short options and are documented in this manual page alongside their short counterparts. .El .Pp Any hosts listed after these options and before the final target host will comprise a Loose Source and Record Route .Pq LSRR . .Nm encodes the specified intermediate addresses into the IP options field of every probe, using option type 131 as defined in RFC\ 791 (the original IPv4 specification). .Pp .Sy LSRR on the modern Internet. The original purpose of source routing was to allow a sender to specify the path a packet should take through the network, useful in early research networks for debugging routing policy and deliberately routing around link failures. However, source routing can be exploited to bypass access controls, conduct reflective attacks, and probe otherwise-isolated network segments. As a consequence, virtually all ISPs and enterprise network operators block or silently strip packets carrying source route options at network ingress. RFC\ 7126 (February 2014) formally codified this practice, recommending that routers .Sy SHOULD drop packets containing LSRR or SSRR IP options, and noting zero operational impact from doing so because no production traffic legitimately uses them. .Pp .Sy Why LSRR is retained in .Nm Ns . The feature remains for controlled network environments where intermediate devices are operator-managed and IP options processing is known to be enabled: lab networks, private AS testing, research testbeds, and similar settings. In such environments, specifying intermediate waypoints can verify specific forwarding paths that are otherwise difficult to isolate. On any public Internet path it will produce no additional hops in the output. .Pp .Sy Caution: accidental LSRR activation. Because .Fl p .Pq ICMP probe mode takes no argument, any extra token appearing between .Fl p and the target is silently parsed as an LSRR intermediate hop rather than being flagged as an error. For example, .Bd -literal -offset indent lft -p 192.168.1.1 target.example.com .Ed .Pp would add 192.168.1.1 as a source route waypoint and embed a 12-byte LSRR IP option in every probe, almost certainly producing no responses from any modern router. The trace will appear to run normally but all hops will be cloaked. Verify command syntax carefully when using ICMP mode. .Sh EXAMPLES A sample use and output might be: .Bd -literal [edge.lax]$ lft -S 4.2.2.2 Hop LFT trace to vnsc-bak.sys.gtei.net (4.2.2.2):80/tcp 1 ln-gateway.centergate.com (206.117.161.1) 0.5ms 2 isi-acg.ln.net (130.152.136.1) 2.3ms 3 isi-1-lngw2-atm.ln.net (130.152.180.21) 2.5ms 4 gigabitethernet5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249) 3.0ms 5 p6-0.lsanca1-cr6.bbnplanet.net (4.24.4.2) 3.4ms 6 p6-0.lsanca2-br1.bbnplanet.net (4.24.5.49) 3.3ms 7 p15-0.snjpca1-br1.bbnplanet.net (4.24.5.58) 10.9ms 8 so-3-0-0.mtvwca1-br1.bbnplanet.net (4.24.7.33) 11.1ms 9 p7-0.mtvwca1-dc-dbe1.bbnplanet.net (4.24.9.166) 11.0ms 10 vlan40.mtvwca1-dc1-dfa1-rc1.bbnplanet.net (128.11.193.67) 11.1ms ** [neglected] no reply packets received from TTLs 11 through 20 ** [4.2-3 BSD bug] the next gateway may errantly reply with reused TTLs 21 [target] vnsc-bak.sys.gtei.net (4.2.2.2) 11.2ms .Ed .Pp The (-S) option was used to suppress the real-time status bar for clean output. LFT's "**" notifiers in between hops 10 and 21 represent additional useful information: the first is a "[neglected]" indicator that lets us know that none of the probes sent with the TTLs indicated elicited responses. This could be for a variety of reasons, but the cause of this specific occurrence is described in the next informative message which indicates that this is likely the result of a bug in the 4.[23] .Tn BSD network code (and its derivatives): BSD 4.x (x < 3) sends an unreachable message using whatever TTL remains in the original datagram. Since, for gateways, the remaining TTL is zero, the .Tn ICMP "time exceeded" is guaranteed to not make it back to us. LFT does its best to identify this condition rather than print lots and lots of hops that don't exist (trying to reach a high enough TTL). .Pp Now, using the adaptive engine option: .Bd -literal [edge.lax]$ lft -E -S 4.2.2.1 Hop LFT trace to vnsc-pri.sys.gtei.net (4.2.2.1):80/tcp 1 ln-gateway.centergate.com (206.117.161.1) 0.5/0.5ms 2 isi-acg.ln.net (130.152.136.1) 2.1/2.3ms 3 isi-1-lngw2-atm.ln.net (130.152.180.21) 2.6/7.1ms 4 gigabitethernet5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249) 6.1/3.9ms ** [firewall] the next gateway may statefully inspect packets 5 p0-0-0.lsanca1-csr1.bbnplanet.net (4.24.4.10) 155.4/3.7ms 6 [target] vnsc-pri.sys.gtei.net (4.2.2.1) 22.6/3.7/*/*/*/*/*ms .Ed .Pp In the scenario above, the adaptive engine was able to identify a stateful, packet-inspecting firewall in the path. Another example with more options: .Bd -literal [edge.lax]$ lft -S -A -T -m 2 -d 80 -s 53 www.yahoo.com Hop LFT trace to w9.scd.yahoo.com (66.218.71.88):80/tcp 1 [226] ln-gateway.centergate.com (206.117.161.1) 1 ms 2 [226] isi-acg.ln.net (130.152.136.1) 2 ms 3 [226] isi-1-lngw2-atm.ln.net (130.152.180.21) 3 ms 4 [1] gigether5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249) 3 ms 5 [1] p6-0.lsanca1-cr6.bbnplanet.net (4.24.4.2) 5 ms 6 [1] p6-0.lsanca2-br1.bbnplanet.net (4.24.5.49) 3 ms 7 [1] p1-0.lsanca2-cr2.bbnplanet.net (4.25.112.1) 3 ms 8 [16852] pos4-0.core1.LosAngeles1.Level3.net (209.0.227.57) 3 ms 9 [3356] so-4-0-0.mp1.LosAngeles1.Level3.net (209.247.10.193) 3 ms 10 [3356] so-3-0-0.mp2.SanJose1.Level3.net (64.159.1.130) 11 ms 11 [3356] gige10-0.ipcolo4.SanJose1.Level3.net (64.159.2.42) 11 ms 12 [3356] cust-int.level3.net (64.152.81.62) 52 ms 13 [10310] vl17.bas2.scd.yahoo.com (66.218.64.150) 53 ms 14 [10310] w9.scd.yahoo.com (66.218.71.88) [target] 54 ms .Pp LFT's trace took 5.23 seconds. Resolution required 3.58 seconds. .Ed .Pp Note the -Ar above displays ASNs using the RADB as a whois source. A better option may have been to use the -A alone or perhaps -AC. .Pp And why not request netblock lookups? .Bd -literal [edge.lax]$ lft -S -N www.microsoft.com Hop LFT trace to www.us.microsoft.com (207.46.197.113):80/tcp 1 [LOS-NETTOS-BLK4] ln-gateway.centergate.com (206.117.161.1) 2 ms 2 [LOS-NETTOS] isi-acg.ln.net (130.152.136.1) 3 ms 3 [LOS-NETTOS] isi-1-lngw2-pos.ln.net (130.152.80.30) 5 ms 4 [GNTY-4-0] gigether5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249) 4 ms 5 [GNTY-4-0] p6-0.lsanca1-cr6.bbnplanet.net (4.24.4.2) 3 ms 6 [GNTY-4-0] p6-0.lsanca2-br1.bbnplanet.net (4.24.5.49) 3 ms 7 [GNTY-4-0] p15-0.snjpca1-br1.bbnplanet.net (4.24.5.58) 10 ms 8 [GNTY-4-0] p9-0.snjpca1-br2.bbnplanet.net (4.24.9.130) 11 ms 9 [GNTY-4-0] so-1-0-0.sttlwa2-br1.bbnplanet.net (4.0.3.229) 27 ms 10 [GNTY-4-0] so-0-0-0.sttlwa1-hcr1.bbnplanet.net (4.24.11.202) 28 ms 11 [GNTY-4-0] so-7-0-0.sttlwa1-hcr2.bbnplanet.net (4.24.10.234) 28 ms 12 [GNTY-4-0] p1-0.sttlwa1-cr2.bbnplanet.net (4.24.10.241) 29 ms 13 [GNTY-4-0] p2-0.msseattle.bbnplanet.net (4.25.89.6) 32 ms 14 [MICROSOFT-GLOBAL-NET] 207.46.154.9 32 ms 15 [MICROSOFT-GLOBAL-NET] 207.46.155.17 33 ms 16 [MICROSOFT-GLOBAL-NET] 207.46.129.51 [prohibited] 35 ms .Ed .Pp .Sh TROUBLESHOOTING If traces don't appear to go anywhere, there are a number of things to try. If you are receiving an error related to permissions, .Nm requires elevated privileges to open raw sockets and capture packets via .Xr pcap 3 . On Linux, the preferred approach is to grant file capabilities rather than installing a setuid root binary: .Pp .Dl setcap cap_net_raw,cap_net_admin=eip /usr/sbin/lft .Pp This requires the .Ic libcap2-bin package on Debian/Ubuntu or .Ic libcap on RHEL/Fedora. The .Ic make install target attempts this automatically and falls back to setuid root if .Xr setcap 8 is not available. On FreeBSD, .Nm must be installed setuid root. .Pp On macOS, .Nm must be installed setuid root. This is because .Nm uses two separate privilege paths: .Xr pcap 3 (via .Pa /dev/bpf* devices) for .Em capturing response packets, and a raw socket .Pq Dv SOCK_RAW for .Em sending probe packets. BPF device permissions (e.g., the ChmodBPF mechanism used by Wireshark) only cover the capture side \(em they do not grant the ability to create raw sockets, which on macOS requires root. Unlike Linux, macOS has no .Xr setcap 8 equivalent to selectively grant raw socket capability to a binary. .Pp To install setuid root: .Pp .Dl sudo chown root lft && sudo chmod u+s lft .Pp .Ic make install performs this step automatically when run as root. This is standard practice for network diagnostic tools on macOS; .Xr ping 8 and .Xr traceroute 8 ship setuid root for the same reason. .Pp If you do not receive permissions-related errors, but traces still don't go anywhere, first activate verbose output by adding -VV to your command line options. Then, reading the verbose output, if you see trace probes going out, but no replies being detected (as indicated by "RCVD" tags), you may: Use the TCP basic (-b) method if you wish to use TCP probes and you fear NAT may be causing your trace to fail. Alternatively, select a different trace method and protocol such as UDP (-u) or ICMP (-p). .Pp If you are attempting to use RFC 1393 (-P) and your trace is failing, this is likely because network equipment somewhere in the path does not conform to RFC 1393. Your only option is to select an alternative tracing method or protocol. .Pp If you are attempting to utilize adaptive mode (-E/-e) and traces fail, first try enabling NAT compatibility using TCP basic (-b). If traces still fail, the most likely reason is a close-proximity stateful firewall in your network, which prevents this feature from working. .Pp .Sh AUTHORS Victor Oppleman, Eugene Antsilevitch, Sergey Kondryukov and other helpers around the world. .Sh REPORTING BUGS To report bugs, send e-mail to .Sh SEE ALSO .Xr traceroute 8 , .Xr netstat 1 , .Xr whois 1 , .Xr whob 8 .Sh HISTORY The .Nm command first appeared in 1998 as 'fft'. Renamed as a result of confusion with fast fourier transforms, .Nm stands for 'layer four traceroute.' Thanks also to Nils McCarthy for writing 'FFT', LFT's predecessor. lft-3.98/Makefile.in000644 000765 000024 00000006142 15174131552 014242 0ustar00vicstaff000000 000000 # # This file is part of LFT. # # The LFT software provided in this Distribution is # Copyright 2007 VOSTROM Holdings, Inc. # # The full text of our legal notices is contained in the file called # COPYING, included with this Distribution. # Directories where LFT will be installed: prefix=@prefix@ datarootdir = @datarootdir@ datadir = @datadir@ exec_prefix=@exec_prefix@ bindir=@bindir@ mandir=@mandir@ INSTALL=@INSTALL@ LN=@LN_S@ # Commands/References CC=@CC@ CFLAGS=@CFLAGS@ LIBS=@LIBS@ LDFLAGS=@LDFLAGS@ CAT=cat CD=cd MKDIR=mkdir -p BUILDTYPE=build RM=rm -rf SH=sh SYSTEM=unix TOOLS=tools TOUCH=touch STRIP ?= strip @SET_MAKE@ # Specifics WATCH_OBJS = @WATCH_OBJS@ OBJS = lft.o lft_ifname.o whois.o lft_lib.o lft_icmptrace.o lft_btcptrace.o $(WATCH_OBJS) # whob is a CLI-only tool — it never uses ncurses, so strip the ncurses # libs out of its link line. c-ares stays (whois.c uses it for the # server-prefetch async resolver path). $(filter-out ...) is supported # by GNU make and BSD make. WHOB_LIBS = $(filter-out -lncurses -lncursesw,$(LIBS)) all: lft whob lft: $(OBJS) $(CC) $(CFLAGS) -o lft $(OBJS) $(LDFLAGS) $(LIBS) lft_watch.o: lft_watch.c lft_watch.h $(CC) $(CFLAGS) -c -o lft_watch.o lft_watch.c whob: whois.o $(CC) $(CFLAGS) -o whob whois.c -DSTANDALONE $(LDFLAGS) $(WHOB_LIBS) install: lft lft.8 whob whob.8 @echo "LFT and WhoB" @echo " \_Stripping binaries" @$(STRIP) lft whob @echo " \_Copying files to their intended destinations" @test -d $(DESTDIR)$(bindir)/. || $(MKDIR) $(DESTDIR)$(bindir) $(INSTALL) lft $(DESTDIR)$(bindir)/lft $(INSTALL) whob $(DESTDIR)$(bindir)/whob @ if [ "$$(uname -s)" = "Linux" ]; then \ if [ "$$(id -u)" = "0" ]; then \ echo " \_Granting setcap() permission to avoid setuid requirement"; \ if setcap cap_net_raw,cap_net_admin=eip $(DESTDIR)$(bindir)/lft 2>/dev/null; then \ echo " \_Done (cap_net_raw,cap_net_admin=eip granted)"; \ else \ echo " \_setcap failed or not available, falling back to setuid root"; \ ( chown root $(DESTDIR)$(bindir)/lft && chmod u+s $(DESTDIR)$(bindir)/lft ) || \ echo "*** WARNING: could not grant capabilities or set $(DESTDIR)$(bindir)/lft setuid root"; \ fi; \ else \ echo "*** WARNING: not running as root; cannot grant setcap or setuid permissions"; \ echo " Re-run 'make install' as root or with sudo to grant required privileges."; \ fi; \ else \ echo " \_Setting setuid root (platform does not support setcap)"; \ ( chown root $(DESTDIR)$(bindir)/lft && chmod u+s $(DESTDIR)$(bindir)/lft ) || \ echo "*** WARNING: could not set $(DESTDIR)$(bindir)/lft setuid root"; \ fi @test -d $(DESTDIR)$(mandir)/man8/. || $(MKDIR) $(DESTDIR)$(mandir)/man8 @echo "Installing manual pages" $(INSTALL) lft.8 $(DESTDIR)$(mandir)/man8/lft.8 $(INSTALL) whob.8 $(DESTDIR)$(mandir)/man8/whob.8 clean: $(RM) *.o core* lft whob *~ *.dSYM distclean: $(RM) Makefile config.log config.status config/acconfig.h autom4te.cache better: @echo "Sorry, this is the best I can do." work: @echo "Sorry, I didn't write this. I'm only a Makefile." love: @echo "What do you think I was doing before you bothered me?" lft-3.98/lft_btcptrace.c000644 000765 000024 00000103631 15171551114 015153 0ustar00vicstaff000000 000000 #include "lft_btcptrace.h" #include "lft_lib.h" static LFT_CALLBACK LFTErrHandler=0; static LFT_CALLBACK LFTEvtHandler=0; static int getFreePort(lft_session_params * sess, int startfrom) { int i; if(sess->btcpmap == NULL) { if(!(sess->btcpmap = malloc(sizeof(BasicTCPMapEntry) * (sess->ttl_limit * sess->retry_max + 1)))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); return 0; } sess->btcpmapsize=sess->ttl_limit * sess->retry_max + 1; for(i=0;ibtcpmapsize;i++) { sess->btcpmap[i].nhop = -1; sess->btcpmap[i].port = sess->dport + i; sess->btcpmap[i].sentcount=0; } sess->latestmapchoice=0; /*sess->debugmapidx=0;*/ } for(i=(startfrom && sess->latestmapchoice>=startfrom)?sess->latestmapchoice+1:startfrom;ibtcpmapsize;i++) { if(sess->btcpmap[i].nhop==-1) { if(startfrom) sess->latestmapchoice=i; return sess->btcpmap[i].port; } } return sess->dport; } static int probeIsSent(lft_session_params * sess, int port, int nhop) { int i = port - sess->dport; if(i<0 || i>=sess->btcpmapsize) { LFTErrHandler(sess, ERR_BTCP_WRONG_PORT_VALUE, NULL); return 0; } if(sess->btcpmap[i].nhop!=-1 && sess->btcpmap[i].nhop!=nhop) { LFTErrHandler(sess, ERR_BTCP_PROBE_PORT_IS_BUSY, NULL); return 0; } sess->btcpmap[i].nhop=nhop; sess->btcpmap[i].sentcount++; return sess->btcpmap[i].sentcount; } static int probeIsRecvd(lft_session_params * sess, int port) { int nhop; int i = port - sess->dport; if(i<0 || i>=sess->btcpmapsize) { LFTErrHandler(sess, ERR_BTCP_WRONG_PORT_VALUE, NULL); return 0; } nhop=sess->btcpmap[i].nhop; sess->btcpmap[i].sentcount--; if(sess->btcpmap[i].sentcount<1) { sess->btcpmap[i].nhop=-1; sess->btcpmap[i].sentcount=0; } return nhop; } #if 0 static int hopByPort(lft_session_params * sess, int port) { int nhop; int i = port - sess->dport; if(i<0 || i>=sess->btcpmapsize) return -1; nhop=sess->btcpmap[i].nhop; if(sess->btcpmap[i].port != port || sess->btcpmap[i].sentcount<1) return -1; return nhop; } #endif static unsigned int tcp_base_send_hop(lft_session_params * sess, short nhop, int searchfrom) { struct sockaddr_in dest; unsigned int tseq=0; unsigned short tttl=0; char * buf; char* bptr = NULL; int blen = 0; int port; EvtSentPacketParam espparam; struct trace_packet_info_s *pinfo = NULL; struct trace_packet_s *packet = NULL; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) buf=(char *)_alloca(maxpacklen+64); #else buf=(char *)alloca(maxpacklen+64); #endif if (!(pinfo = (struct trace_packet_info_s *)malloc(sizeof(struct trace_packet_info_s)))) { LFTErrHandler(sess, ERR_NOT_ENOUGH_MEM, NULL); return 0; } memset(pinfo, 0, sizeof(struct trace_packet_info_s)); packet = &(pinfo->u.packet); memcpy(packet, &(sess->trace_packet), sizeof(struct trace_packet_s)); bptr = buf; dest.sin_family = AF_INET; dest.sin_addr = sess->remote_address; port=getFreePort(sess, searchfrom); dest.sin_port = htons(port); tseq = new_seq(sess); tttl = nhop + 1; sess->ts_last_sent = sess->now; packet->ip_hdr.ip_ttl = tttl; packet->ip_hdr.ip_src = sess->local_address; packet->ip_hdr.ip_dst = sess->remote_address; espparam.flags=sess->tcp_flags; if(sess->adaptive) { struct hop_info_s *h = &(sess->hop_info[nhop]); if(h->state == HS_SEND_FIN) espparam.flags = TH_FIN; else if(h->state == HS_SEND_SYN) espparam.flags = TH_SYN; else if(h->state == HS_SEND_SYN_ACK) espparam.flags = HS_SEND_SYN_ACK; else { WrnBadHopStateParam wbhsp; wbhsp.h=h; wbhsp.nhop=nhop; LFTErrHandler(sess, WRN_BAD_HOP_STATE, &wbhsp); } } espparam.nhop=nhop; espparam.tseq=tseq; espparam.tttl=tttl; lft_o_probe_sent(); /* spinner: count in-flight probe */ if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_SENT_PACKET, &espparam); if(sess->exit_state<0) { free(pinfo); return 0; } } packet->ip_hdr.ip_sum = 0; #if !defined(SCREWED_IP_LEN) packet->ip_hdr.ip_sum = ip_cksum (&packet->ip_hdr); #endif memcpy(bptr, &(packet->ip_hdr), sizeof(struct ip)); bptr += sizeof(struct ip); if (packet->lsrr.ipl_len > 0) { memcpy(bptr, &(packet->lsrr), packet->lsrr.ipl_len + 1); bptr += (packet->lsrr.ipl_len + 1); /* PADDING !!! */ } /* Layer-4 preparation */ /* Construct TCP (no payload needed) */ if (sess->noisy > 5) { LFTEvtHandler(sess,EVT_SHOW_PAYLOAD, packet); if(sess->exit_state < 0) { free(pinfo); return 0; } } packet->tcp_hdr.th_dport = dest.sin_port; packet->tcp_hdr.th_seq = htonl (tseq); packet->tcp_hdr.th_sport = htons (sess->sport); packet->tcp_hdr.th_flags = espparam.flags; #if defined(SOLARIS_LENGTH_IN_CHECKSUM) packet->tcp_hdr.th_sum = htons (sizeof (struct tcphdr)) + packet->payload_len; #else packet->tcp_hdr.th_sum = 0; packet->tcp_hdr.th_sum = tcp_cksum (&packet->ip_hdr, &packet->tcp_hdr, packet->payload, packet->payload_len); #endif if (sess->noisy > 5) { LFTEvtHandler(sess,EVT_SHOW_TCP_CHECKSUM, packet); if(sess->exit_state<0) { free(pinfo); return 0; } } memcpy(bptr, &(packet->tcp_hdr), sizeof(struct tcphdr)); bptr += sizeof(packet->tcp_hdr); memcpy(bptr, packet->payload, packet->payload_len); blen = sizeof(struct ip) + packet->lsrr.ipl_len + sizeof(struct tcphdr) + packet->payload_len; /* Packet is ready, fire away */ if (sendto (sess->send_sock, buf, blen, 0, (const struct sockaddr *)(const void *)&dest, sizeof (dest)) < 0) { LFTErrHandler(sess, ERR_RAW_TCP_DISABLED, NULL); free(pinfo); return 0; } /*sess->debugmap[sess->debugmapidx].type=0; sess->debugmap[sess->debugmapidx].hop=nhop; sess->debugmap[sess->debugmapidx++].port=port;*/ probeIsSent(sess, port, nhop); pinfo->hopno = nhop; pinfo->seq = tseq; pinfo->sent = sess->now; SLIST_INSERT_HEAD(&(sess->trace_packets), pinfo, next); sess->trace_packets_num++; if (nhop != -1) { SLIST_INSERT_HEAD(&(sess->hop_info[nhop].packets), pinfo, next_by_hop); sess->hop_info[nhop].num_sent++; sess->hop_info[nhop].all_sent++; sess->hop_info[nhop].ts_last_sent = sess->now; } return tseq; } static void tcp_base_recv_packet (lft_session_params * sess, unsigned int seq, struct in_addr ipaddr, int icmp_type, const struct pcap_pkthdr *hdr) { double ms; struct trace_packet_info_s *tp = NULL; EvtNonSeqPacketParam ensp; /* Depending on the platform, we can use * the pcap header's timeval or we must call gettimeofday() for each packet */ #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) gettimeofday (&(sess->now), NULL); #else sess->now.tv_sec = hdr->ts.tv_sec; sess->now.tv_usec = hdr->ts.tv_usec; /* gettimeofday (&now, NULL); */ #endif /* First, search every probe to find an exact sequence match */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if(tp->seq == seq) break; } #if 0 if(tp == NULL) { /* Second, search every probe to find an exact port match */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if(hopByPort(sess, htons(tp->packet.tcp_hdr.th_dport)) == tp->hopno) break; } } #endif /* Last resort. Catch any response from the target */ if (tp == NULL) { if (sess->noisy > 3) { LFTEvtHandler(sess,EVT_LOOKFOR_LAST_RESORT,NULL); if(sess->exit_state < 0) return; } SLIST_FOREACH(tp, &(sess->trace_packets), next) { /* Special case: look for a response to our SYN_ACK */ if (tp->u.packet.tcp_hdr.th_flags == HS_SEND_SYN_ACK) { if (!tp->recv.tv_sec) { break; } } /* Truly the last resort: packet from the target with a wacky ACK sequence */ if ((ipaddr.s_addr == sess->remote_address.s_addr) && (tp->hopaddr.s_addr == 0) && (icmp_type == -1)) { sess->target_anomaly = 1; } } } /* This packet is not even close, drop it and move on */ if (!tp) { if (sess->noisy) LFTEvtHandler(sess, EVT_SKIP_PACKET, NULL); else if (!sess->nostatus) LFTEvtHandler(sess, EVT_PROGRESS_SKIP_PACKET, NULL); return; } if (tp->seq != seq) { ensp.ipaddr=ipaddr; ensp.tp=tp; if (((tp->seq) == (seq + 1)) && (icmp_type == -1)) { if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_ACK_WAS_NOT_INC,&ensp); if(sess->exit_state<0) return; } /* return; */ } else if (((tp->seq) == (seq - sess->payloadlen)) && (icmp_type == -1)) { if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RST_REL_TO_ISN,&ensp); if(sess->exit_state<0) return; } /* return; */ } else if ((ipaddr.s_addr == sess->remote_address.s_addr) && (icmp_type == -1)) { if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_ACK_WAS_WAY_OFF,&ensp); if(sess->exit_state<0) return; } /* return; */ } } if (tp->recv.tv_sec) { if (sess->noisy) LFTEvtHandler(sess,EVT_DUPLICATE_PACKET, NULL); else if (!sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_DUPLICATE,NULL); return; } /*sess->debugmap[sess->debugmapidx].type=1; sess->debugmap[sess->debugmapidx].hop=tp->hopno; sess->debugmap[sess->debugmapidx].phop=hopByPort(sess, htons(tp->packet.tcp_hdr.th_dport)); sess->debugmap[sess->debugmapidx].port=htons(tp->packet.tcp_hdr.th_dport); sess->debugmap[sess->debugmapidx++].ip=ipaddr;*/ probeIsRecvd(sess, htons(tp->u.packet.tcp_hdr.th_dport)); /* Set icmp_type before firing EVT_RECV_PACKET so the handler sees the * correct value (race-condition fix mirrors lft_lib.c). */ tp->icmp_type = icmp_type; if (sess->noisy > 1) { EvtRecvPacketParam erpp; erpp.ipaddr=ipaddr; erpp.seq=seq; erpp.tp=tp; LFTEvtHandler(sess,EVT_RECV_PACKET,&erpp); } else { if (!sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_OK,NULL); } if(sess->exit_state<0) return; /* increment received packet counter */ sess->num_rcvd++; tp->recv = sess->now; if (tp->hopno != -1) { sess->hop_info[tp->hopno].ts_last_recv = sess->now; sess->hop_info[tp->hopno].all_rcvd++; hop_state_copy(sess, tp->hopno); /* indicate this hop has a sequence anomaly */ if (icmp_type == -1) sess->hop_info[tp->hopno].flags |= HF_ENDPOINT; } tp->hopaddr = ipaddr; /* tp->icmp_type already set above, before EVT_RECV_PACKET */ #ifdef HAVE_CARES lft_ares_queue(sess, ipaddr); /* queue PTR lookup for this hop (deduped) */ #endif if (icmp_type != -2 && (!sess->num_hops || sess->num_hops > tp->hopno)) if ((sess->break_on_icmp && lft_icmp_stops_trace(icmp_type)) || (icmp_type == -1)) { if (tp->hopno != -1) { /* we set fake type -1 when we get actual * tcp packet in return - meaning destination */ sess->num_hops = tp->hopno; tp->is_done = 1; sess->hop_info[tp->hopno].done_packet = tp; if (sess->noisy > 1 && sess->target_open < 1) LFTEvtHandler(sess,EVT_TCP_PORT_CLOSED,NULL); else if (sess->noisy > 1 && sess->target_open > 0) LFTEvtHandler(sess,EVT_TCP_PORT_OPEN,NULL); if(sess->exit_state<0) return; } } /* adjust scatter if we have fast reply times */ ms = timediff_ms (tp->sent, tp->recv); sess->scatter_ms = (sess->scatter_ms * (sess->ahead_limit - 1) + ms) / sess->ahead_limit; } static void tcp_base_finish(lft_session_params * sess) { int hopno; int maxhop; int reply, noreply; int as_for_hop = 0; struct trace_packet_info_s *tp; char *netname; /*int ocres;*/ char *myApp = (char *)malloc((strlen(version) * sizeof(char)) + 1 + (strlen(appname) * sizeof(char))); struct ip_list_array *ipaslist = (struct ip_list_array *)malloc(sizeof(struct ip_list_array)); /* Variables for seam detection */ int icmpcode; int asseam_hopno=-1; struct in_addr asseam_hopaddr; int netseam_hopno=-1; struct in_addr netseam_hopaddr; struct in_addr classbmask; struct in_addr masked_target; int prevasn=-1; struct in_addr prevasn_hopaddr; int prevasn_hopno; int lastishole; int netreached=0; int isseam; /* ---------------------------- */ inet_aton("255.255.0.0", &classbmask); masked_target.s_addr=sess->remote_address.s_addr & classbmask.s_addr; EvtPacketInfoParam ehip; memset(ipaslist, '0', sizeof(struct ip_list_array)); gettimeofday (&(sess->trace_done_time), NULL); /*ocres=open_check(sess, LFTErrHandler, LFTEvtHandler); LFTEvtHandler(sess,EVT_OPEN_CHECK_RESULT,&ocres); if(ocres==1) { sess->target_open=1; sess->target_filtered=0; } else { sess->target_open=0; if(ocres<0) sess->target_filtered=0; else sess->target_filtered=1; }*/ if (sess->noisy > 3) { LFTEvtHandler(sess,EVT_SHOW_NUM_HOPS, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } if (sess->num_hops) { maxhop = sess->num_hops; /* display all packets we received from this host */ SLIST_FOREACH(tp, &(sess->trace_packets), next) { if (tp->is_done) { tp->hopno = maxhop; break; } } } else { maxhop = sess->hop_info_length - 1; } LFTEvtHandler(sess,EVT_TRACE_COMPLETED, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } /* if user wants ASN resolution from pwhois/cymru/riswhois, do it in bulk */ if (sess->do_aslookup || sess->do_netlookup) { if(sess->noisy > 1) { LFTEvtHandler(sess,EVT_ON_RESOLUTION, NULL); if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } if (!sess->use_radb) { /* populate bulk ip_addr_list structure */ for (hopno = sess->ttl_min; hopno <= maxhop; hopno++) { SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_usec) { (*ipaslist).ipaddr[as_for_hop] = tp->hopaddr; as_for_hop++; (*ipaslist).numItems = (as_for_hop); break; } } } if (sess->use_cymru) { /* use cymru bulk service */ if (w_lookup_as_cymru_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } else if (sess->use_ris) { /* use RIPE NCC RIS service */ if (w_lookup_all_riswhois_bulk(sess->wsess, &(*ipaslist)) != 0) if (sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } else { /* use pwhois bulk service */ if ((strlen(version) * sizeof(char)) + 1 + (strlen(appname) * sizeof(char)) < 254) { *myApp = '\0'; strcat(myApp,appname); strcat(myApp," "); strcat(myApp,version); strncpy((*ipaslist).application, myApp, 511); } if (w_lookup_all_pwhois_bulk(sess->wsess, &(*ipaslist)) != 0) if(sess->noisy) LFTErrHandler(sess, WRN_NS_LOOKUP_FAILED, NULL); } if(sess->exit_state < 0) { free(ipaslist); free(myApp); return; } } } free(myApp); LFTEvtHandler(sess,EVT_TRACE_REPORT_START, &maxhop); if(sess->exit_state < 0){ free(ipaslist); return; } /* seam detection */ as_for_hop=0; for(hopno = sess->ttl_min; hopno < sess->hop_info_length; hopno++) { struct in_addr last_hop; icmpcode=-100; last_hop.s_addr = 0; if(sess->hop_info[hopno].all_rcvd) { lastishole=0; SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if(tp->recv.tv_sec) { if(hopno<=maxhop) icmpcode=tp->icmp_type; if(last_hop.s_addr != tp->hopaddr.s_addr) { if((tp->hopaddr.s_addr & classbmask.s_addr) == masked_target.s_addr) netreached=1; else { netseam_hopno=hopno; netseam_hopaddr=tp->hopaddr; } if(sess->do_aslookup || sess->do_netlookup) { if (sess->use_radb) { /* using RADB/IRR */ tp->asnumber = w_lookup_as(sess->wsess, inet_ntoa(tp->hopaddr)); } else { /* using pwhois by default */ tp->asnumber = (*ipaslist).asn[as_for_hop]; } if(prevasn==-1) { if(tp->asnumber) { prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } else { if(tp->asnumber) { if(tp->asnumber!=prevasn) { asseam_hopno=prevasn_hopno; asseam_hopaddr=prevasn_hopaddr; } prevasn=tp->asnumber; prevasn_hopno=hopno; prevasn_hopaddr=tp->hopaddr; } } } last_hop=tp->hopaddr; } } } as_for_hop++; } else lastishole=1; if(icmpcode==-1) break; } if(!netreached) netseam_hopno=-1; if(lastishole) asseam_hopno=-1; /* -------------- */ noreply = 0; reply = 0; as_for_hop = 0; /* this correlates the hopno to the asn stored in ipaslist */ /* Filtered-run state (mirrors finish() in lft_lib.c) */ int filt_run_start = 0; int filt_run_end = 0; int filt_run_icmp_type = 3; struct in_addr filt_run_ip; filt_run_ip.s_addr = 0; for (hopno = sess->ttl_min; hopno <= maxhop; hopno++) { struct in_addr last_hop; /* Detect filtered-repeat hops and compress into a range line */ { int _is_filt = (sess->hop_info[hopno].all_rcvd != 0) && hop_is_filtered_repeat(sess, hopno); if (!_is_filt && filt_run_start > 0) { print_filtered_run(filt_run_start, filt_run_end, filt_run_ip, filt_run_icmp_type); filt_run_start = 0; } if (_is_filt) { if (noreply >= 1) { EvtNoReplyParam _nrp; _nrp.hopno = hopno; _nrp.noreply = noreply; LFTEvtHandler(sess, EVT_RPT_NO_REPLY, &_nrp); if (sess->exit_state < 0) { free(ipaslist); return; } } if (filt_run_start == 0) { filt_run_start = hopno; filt_run_ip = hop_first_rcvd_ip(sess, hopno); filt_run_icmp_type = hop_first_rcvd_icmp_type(sess, hopno); } filt_run_end = hopno; reply = 1; } else { /* !_is_filt — normal rendering path */ if (sess->hop_info[hopno].all_rcvd != 0) { if (noreply >= 1) { EvtNoReplyParam nrp; nrp.hopno=hopno; nrp.noreply=noreply; LFTEvtHandler(sess,EVT_RPT_NO_REPLY, &nrp); if(sess->exit_state < 0){ free(ipaslist); return; } } } last_hop.s_addr = 0; if ((sess->hop_info[hopno].state == HS_SEND_FIN) && (sess->hop_info[hopno+1].state == HS_SEND_SYN) && (sess->hop_info[hopno+1].ts_last_recv.tv_sec)) { LFTEvtHandler(sess,EVT_RPT_FRW_INSPECT_PACKS, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if ((sess->hop_info[hopno].state != HS_SEND_SYN_ACK) && (sess->hop_info[hopno+1].state == HS_SEND_SYN_ACK) && (hopno == (sess->num_hops - 1))) { LFTEvtHandler(sess,EVT_RPT_FRW_STATE_FILTER, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if ((sess->hop_info[hopno].flags & HF_ENDPOINT) && (noreply >= ((maxhop - sess->ttl_min)/2)) && sess->num_hops > 3) { LFTEvtHandler(sess,EVT_RPT_BSD_BUG, NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } if (sess->hop_info[hopno].all_rcvd == 0) { reply = 0; } else { LFTEvtHandler(sess,EVT_RPT_HOP_INFO_START,&hopno); if(sess->exit_state < 0){ free(ipaslist); return; } SLIST_FOREACH(tp, &(sess->hop_info[hopno].packets), next_by_hop) { if (tp->recv.tv_sec) { reply = 1; if (last_hop.s_addr != tp->hopaddr.s_addr) { ehip.asnumber = 0; /* init/clear the ASN */ if (sess->do_aslookup) { if (sess->use_radb) { /* using RADB/IRR */ ehip.asnumber = w_lookup_as(sess->wsess, inet_ntoa(tp->hopaddr)); } else { /* using pwhois by default */ ehip.asnumber = (*ipaslist).asn[as_for_hop]; } } tp->asnumber=ehip.asnumber; ehip.netname=NULL; if (sess->do_netlookup) { if (!sess->do_aslookup || (sess->do_aslookup && !sess->use_cymru && !sess->use_radb)) { netname = (*ipaslist).netName[as_for_hop]; } else { netname = w_lookup_netname(sess->wsess, inet_ntoa(tp->hopaddr)); } ehip.netname=netname; } if(ehip.netname) strncpy(tp->netname, ehip.netname, 511); else tp->netname[0]=0; /* Orgname — pwhois bulk only (mirrors lft_lib.c path). * Fire whenever pwhois ran: do_aslookup OR do_netlookup. */ tp->orgname[0] = 0; if ((sess->do_aslookup || sess->do_netlookup) && !sess->use_cymru && !sess->use_radb) strncpy(tp->orgname, (*ipaslist).orgName[as_for_hop], sizeof(tp->orgname) - 1); } ehip.last_hop=last_hop; tp->last_hop=ehip.last_hop; last_hop = tp->hopaddr; } ehip.tp=tp; /* seam processing */ isseam=0; ehip.is_asseam=0; ehip.is_netseam=0; ehip.is_open=0; ehip.is_filtered=0; ehip.seam_traced=0; if(sess->check_seam && hopno==asseam_hopno && tp->hopaddr.s_addr==asseam_hopaddr.s_addr) { isseam=1; ehip.is_asseam=1; } if(sess->check_seam && hopno==netseam_hopno && tp->hopaddr.s_addr==netseam_hopaddr.s_addr) { isseam=1; ehip.is_netseam=1; } if(isseam) { if(sess->check_seam) { int curroutputstyle=getOutputStyle(); char hostname[100]; ehip.seam_traced=1; setOutputStyle(2); lft_session_params * subsess=LFTSessionOpen(); strncpy(hostname, inet_ntoa(tp->hopaddr),100); subsess->senddevsel = 1; subsess->senddev = sess->pcap_send_dev; subsess->userdevsel = 1; subsess->userdev = sess->pcap_dev; subsess->auto_ports=0; subsess->dport=179; subsess->seq_start=30; subsess->retry_min=1; subsess->retry_max=1; subsess->resolve_names=0; subsess->ahead_limit=1; subsess->break_on_icmp = 0; subsess->is_graphviz_subquery=1; subsess->hostname=hostname; subsess->hostname_lsrr_size = 0; LFTExecute(subsess); ehip.is_open=subsess->target_open; ehip.is_filtered=subsess->target_filtered; LFTSessionClose(subsess); setOutputStyle(curroutputstyle); } } /* --------------- */ LFTEvtHandler(sess,EVT_RPT_PACKET_INFO,&ehip); if(sess->exit_state < 0){ free(ipaslist); return; } } LFTEvtHandler(sess,EVT_RPT_PACKET_LIST_END,NULL); if(sess->exit_state < 0){ free(ipaslist); return; } } } /* !_is_filt */ } /* filtered-repeat detection block */ if (reply) { noreply = 0; as_for_hop++; } else noreply++; reply = 0; } /* Flush any trailing filtered run */ if (filt_run_start > 0) print_filtered_run(filt_run_start, filt_run_end, filt_run_ip, filt_run_icmp_type); if (!sess->num_hops) { LFTEvtHandler(sess,EVT_RPT_NO_HOPS,&maxhop); } if (sess->timetrace) { LFTEvtHandler(sess,EVT_RPT_TIME_TRACE,NULL); } LFTEvtHandler(sess,EVT_ON_EXIT,NULL); if(ipaslist != NULL) free(ipaslist); } static int tcp_base_check_timeouts(lft_session_params * sess) { int nhop; int need_reply = 0; int no_reply = 0; int last_return = 0; gettimeofday (&(sess->now), NULL); if (timediff_ms (sess->ts_last_sent, sess->now) < sess->scatter_ms) return 0; /* not ready to send another packet yet */ for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (!sess->hop_info[nhop].num_sent) { tcp_base_send_hop(sess, nhop, 1); return 0; } } for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (sess->hop_info[nhop].num_sent <= sess->retry_max && !sess->hop_info[nhop].ts_last_recv.tv_sec) { if (sess->noisy > 4) { LFTEvtHandler(sess,EVT_TTL_NO_REPLY,&nhop); if(sess->exit_state<0) return 0; } if (timediff_ms (sess->hop_info[nhop].ts_last_sent, sess->now) >= sess->timeout_ms) { /* we timed out waiting for this hop -- retry if we have any * more tries */ if (sess->hop_info[nhop].num_sent < sess->retry_max) { if (!sess->noisy && !sess->nostatus) LFTEvtHandler(sess,EVT_PROGRESS_NO_REPLY,NULL); if (sess->noisy > 2) LFTEvtHandler(sess,EVT_TTL_TOUT_RESEND,&nhop); if(sess->exit_state<0) return 0; if(sess->hop_info[nhop].num_sent < sess->retry_max - 1) tcp_base_send_hop(sess, nhop, 1); else { sess->btcpdpucnt++; if(sess->btcpdpucnt>4) { sess->btcpdpucnt=0; sess->btcpmap[0].nhop=-1; sess->btcpmap[0].sentcount=0; tcp_base_send_hop(sess, nhop, 0); } } return 0; } else { if (!sess->adaptive || hop_state_up(sess, nhop)) { if (sess->noisy > 3) LFTEvtHandler(sess,EVT_TTL_TOUT_GIVINGUP,&nhop); if(sess->exit_state<0) return 0; no_reply++; } } } else { need_reply++; /* we have to wait for this one to timeout */ } } else { /* have reply */ last_return = nhop; } } if (sess->noisy > 4) { EvtDebugCheckpoint1Param edcp; edcp.last_return=last_return; edcp.need_reply=need_reply; edcp.no_reply=no_reply; LFTEvtHandler(sess,EVT_DBG_CHECKPOINT1,&edcp); if(sess->exit_state<0) return 0; } if (no_reply >= sess->ahead_limit) { /* we timed out. */ if ((last_return + 3) * 2 < sess->hop_info_length) { if ((need_reply < 3) && (sess->num_rcvd < 2)) LFTEvtHandler(sess,EVT_CANT_RELIABLY_RTRIP,NULL); if(sess->exit_state < 0) return 0; tcp_base_finish(sess); return 1; } } if ((!sess->num_hops || sess->hop_info_length < sess->num_hops || need_reply) && sess->hop_info_length < sess->ttl_limit) { if (sess->noisy > 4) LFTEvtHandler(sess,EVT_HAVE_UNANSWERRED_HOPS,NULL); if (need_reply >= sess->ahead_limit) { if (sess->noisy > 4) LFTEvtHandler(sess,EVT_TOO_FAR_AHEAD,NULL); return 0; /* wait for some replies before we go on */ } if(sess->exit_state<0) return 0; if (sess->num_hops > 0 && sess->hop_info_length >= sess->num_hops) { if (sess->noisy > 3) LFTEvtHandler(sess,EVT_HAVE_GAPS,NULL); return 0; /* we know how long the path is -- * wait to fill in the blanks */ } nhop = sess->hop_info_length++; tcp_base_send_hop(sess, nhop, 1); } else { if (sess->noisy >= 4) { LFTEvtHandler(sess,EVT_EITHER_RESP_OR_TOUT,NULL); if(sess->exit_state<0) return 0; } for (nhop = sess->ttl_min; nhop < sess->hop_info_length; nhop++) { if (sess->hop_info[nhop].num_sent < sess->retry_min) { tcp_base_send_hop(sess, nhop, 1); return 0; } if(sess->hop_info[nhop].done_packet) { if(sess->trg_probe_is_sentretry_min || (sess->trg_probe_is_sentretry_max && ntohs(sess->hop_info[nhop].done_packet->u.packet.tcp_hdr.th_dport)!=sess->dport)) { sess->btcpdpucnt++; if(sess->btcpdpucnt>4) { sess->btcpdpucnt=0; sess->btcpmap[0].nhop=-1; sess->btcpmap[0].sentcount=0; sess->trg_probe_is_sent++; tcp_base_send_hop(sess, nhop, 0); } return 0; } } } tcp_base_finish(sess); return 1; } return 0; } static void tcp_base_process_packet(lft_session_params * sess, const u_char *packet, const struct pcap_pkthdr *hdr) { const struct ip *ip, *orig_ip; const struct tcphdr *tcp; const struct icmp *icmp; if (sess->noisy > 4) { LFTEvtHandler(sess,EVT_PROCESS_PACKET_START,NULL); if(sess->exit_state<0) return; } tcp_base_check_timeouts(sess); if(sess->exit_state<0) return; packet += sess->skip_header_len; ip = (const void *) packet; packet += 4 * ip->ip_hl; switch (ip->ip_p) { case IPPROTO_ICMP: orig_ip = ip; icmp = (const void *) packet; if (icmp->icmp_type != ICMP_UNREACH && icmp->icmp_type != ICMP_TIMXCEED) return; ip = &icmp->icmp_ip; if (ip->ip_p != IPPROTO_TCP) return; /* not a response to our tcp probe */ packet = (const u_char *) ip; packet += 4 * ip->ip_hl; tcp = (const void *) packet; if (ip->ip_src.s_addr != sess->local_address.s_addr || ip->ip_dst.s_addr != sess->remote_address.s_addr) return; /* not for us */ if (sess->noisy > 2) { EvtIncomingICMPTCPParam eiitp; eiitp.icmp=icmp; eiitp.ip=ip; eiitp.orig_ip=orig_ip; eiitp.tcp=tcp; LFTEvtHandler(sess,EVT_INCOMING_ICMP_TCP,&eiitp); if(sess->exit_state<0) return; } if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RCVD_ICMP_TCP,tcp); if(sess->exit_state<0) return; } /* PMTUD: intercept PTB (type 3 code 4) before marking hop answered */ if (sess->pmtud && icmp->icmp_type == ICMP_UNREACH && icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { lft_pmtud_handle_ptb(sess, icmp, ntohl(tcp->th_seq), orig_ip->ip_src); return; } tcp_base_recv_packet(sess, ntohl (tcp->th_seq) , orig_ip->ip_src, (icmp->icmp_type == ICMP_TIMXCEED) ? -2 : icmp->icmp_code, hdr); return; case IPPROTO_TCP: tcp = (const void *) packet; if (!(tcp->th_flags & TH_RST) && !(tcp->th_flags & TH_ACK) && !(tcp->th_flags & TH_SYN)) return; /* not what we're looking for */ if (ip->ip_src.s_addr != sess->remote_address.s_addr || ip->ip_dst.s_addr != sess->local_address.s_addr) { return; /* not the right connection */ } if (sess->noisy > 1) { LFTEvtHandler(sess,EVT_RCVD_TCP,tcp); if(sess->exit_state<0) return; } if(ntohs(tcp->th_sport)==sess->dport) { /* Check for SYN,ACK in response to determine if target is listening */ if ((tcp->th_flags & TH_ACK) && (tcp->th_flags & TH_SYN) && !(tcp->th_flags & TH_RST)) sess->target_open++; if ((tcp->th_flags & TH_ACK) && !(tcp->th_flags & TH_SYN) && (tcp->th_flags & TH_RST)) sess->target_open = 0; } tcp_base_recv_packet(sess, ntohl (tcp->th_ack) - 1, ip->ip_src, -1, hdr); return; default: if (sess->noisy > 3) LFTEvtHandler(sess,EVT_RCVD_UNKNOWN,ip); } } #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) void win_tcp_base_process(lft_session_params * sess) { fd_set fds; struct timeval tm; tm.tv_sec = 0; tm.tv_usec = 100000; FD_ZERO(&fds); FD_SET(sess->recv_sock, &fds); if (select(sess->recv_sock+1, &fds, 0, 0, &tm) < 0) { LFTErrHandler(sess, ERR_WIN_SELECT, NULL); return; } if (FD_ISSET(sess->recv_sock, &fds)) { /* read packet */ char packetbuf[2048]; int nread; memset(packetbuf, 0, sizeof(packetbuf)); nread = recv(sess->recv_sock, packetbuf, sizeof(packetbuf), 0); if (nread <= 0) { LFTErrHandler(sess, ERR_WIN_RECV, NULL); return; } tcp_base_process_packet(sess, packetbuf, NULL); } } #else static void pcap_tcp_base_process_packet(u_char * user_data, const struct pcap_pkthdr *hdr, const u_char * packet) { lft_session_params * sess=(lft_session_params *)(void *)user_data; if(sess->exit_state<0) return; tcp_base_process_packet(sess, packet, hdr); } #endif void tcp_base_trace_main_loop(lft_session_params * sess, LFT_CALLBACK err, LFT_CALLBACK evt) { LFTErrHandler=err; LFTEvtHandler=evt; #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) while(1) { win_tcp_base_process(sess); if(sess->exit_state<0) break; if(tcp_base_check_timeouts(sess)) break; if(sess->exit_state<0) break; } #else while(pcap_dispatch(sess->pcapdescr, -1, pcap_tcp_base_process_packet, (u_char *)sess) >= 0) { if(sess->exit_state<0) break; if(sess->noisy > 6) { LFTEvtHandler(sess,EVT_DBG_CHECKPOINT2,NULL); if(sess->exit_state<0) break; } #ifdef HAVE_CARES lft_ares_poll(sess); /* harvest any completed PTR replies */ #endif if(tcp_base_check_timeouts(sess)) break; if(sess->exit_state<0) break; } #endif } lft-3.98/lft_ifname.c000644 000765 000024 00000005612 13647352647 014463 0ustar00vicstaff000000 000000 /* * lft_ifname.c * Layer Four Traceroute * * This file is part of LFT. * * The LFT software provided in this Distribution is * Copyright 2007 VOSTROM Holdings, Inc. * * The full text of our legal notices is contained in the file called * COPYING, included with this Distribution. * */ #if !defined(WIN32) && !defined(_WIN32) #include #include #include #include #if !defined(linux) && !defined(__CYGWIN__) #include #endif #include #include #include #include #include #include "lft_ifname.h" #if defined( __CYGWIN__ ) || defined( WIN32 ) || defined(_WIN32) #include "config/acconfig.win.h" #else #include "config/acconfig.h" #endif static int sock = -1; u_long lft_getifaddr (const char *ifname) { struct ifreq ifr; struct sockaddr_in addr; if(!ifname){ perror ("The user-supplied network interface is not suitable"); return -1; } /* Only do this once of course */ if (sock < 0) { if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { perror ("socket"); return -1; } } STRNCPY(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) { perror("ioctl"); return -1; } if (ifr.ifr_addr.sa_family != AF_INET) { fprintf (stderr, "%s: Interface not configured with IPv4 address.\n", ifname); fflush (stderr); return -1; } memcpy(&addr, &ifr.ifr_addr, sizeof addr); return (addr.sin_addr.s_addr); } char * lft_getifname (struct in_addr addr) { struct ifconf ifc; char buffer[2048]; int i, skip; /* Only do this once of course */ if (sock < 0) { if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { perror ("socket"); return NULL; } } ifc.ifc_len = sizeof(buffer); ifc.ifc_buf = buffer; if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { perror("ioctl"); return NULL; } for (i = 0; i < ifc.ifc_len; i += skip) { struct ifreq ifr; struct in_addr thisaddr; memcpy(&ifr, ifc.ifc_buf + i, sizeof(struct ifreq)); skip = sizeof(struct ifreq); #ifdef HAVE_SOCKADDR_SA_LEN if (ifr.ifr_addr.sa_len > sizeof(struct sockaddr)) { skip = ifr.ifr_addr.sa_len + IFNAMSIZ; } #endif if (ifr.ifr_addr.sa_family != AF_INET) continue; thisaddr = ((const struct sockaddr_in *)(const void *)(&(ifr.ifr_addr)))->sin_addr; if (thisaddr.s_addr == addr.s_addr) return strdup(ifr.ifr_name); } /* not found */ return NULL; } #ifdef LFT_IFADDR_TESTING extern int main (int argc, char *argv[]) { struct in_addr in; char *addr; if (argc > 1) addr = strdup (argv[1]); else addr = strdup ("eth0"); in.s_addr = lft_getifaddr (addr); if (in.s_addr == -1) { fprintf (stderr, "%s: Error reading ifname\n", addr); fflush (stderr); free(addr); exit (-1); } fprintf (stdout, "%s: %s\n", addr, inet_ntoa (in)); fflush (stdout); free (addr); exit (0); } #endif /*LFT_IFNAME_TESTING*/ #endif lft-3.98/lft_queue.h000644 000765 000024 00000041345 11053131665 014340 0ustar00vicstaff000000 000000 /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 * $FreeBSD: src/sys/sys/queue.h,v 1.32.2.4 2001/03/31 03:33:39 hsu Exp $ */ #ifndef _LFT_QUEUE_H_ #define _LFT_QUEUE_H_ #define __offsetof(type, field) ((size_t)(&((type *)0)->field)) /* * This file defines five types of data structures: singly-linked lists, * singly-linked tail queues, lists, tail queues, and circular queues. * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A singly-linked tail queue is headed by a pair of pointers, one to the * head of the list and the other to the tail of the list. The elements are * singly linked for minimum space and pointer manipulation overhead at the * expense of O(n) removal for arbitrary elements. New elements can be added * to the list after an existing element, at the head of the list, or at the * end of the list. Elements being removed from the head of the tail queue * should use the explicit macro for this purpose for optimum efficiency. * A singly-linked tail queue may only be traversed in the forward direction. * Singly-linked tail queues are ideal for applications with large datasets * and few or no removals or for implementing a FIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. * * * SLIST LIST STAILQ TAILQ CIRCLEQ * _HEAD + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_REVERSE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _REMOVE_HEAD + - + - - * _REMOVE + + + + + * */ /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #undef SLIST_ENTRY #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List functions. */ #define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_FOREACH(var, head, field) \ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) #define SLIST_INIT(head) { \ (head)->slh_first = NULL; \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = (head)->slh_first; \ while( curelm->field.sle_next != (elm) ) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ } while (0) /* * Singly-linked Tail queue definitions. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first;/* first element */ \ struct type **stqh_last;/* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue functions. */ #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) #define STAILQ_INIT(head) do { \ (head)->stqh_first = NULL; \ (head)->stqh_last = &(head)->stqh_first; \ } while (0) #define STAILQ_FIRST(head) ((head)->stqh_first) #ifndef STAILQ_LAST #define STAILQ_LAST(head, type, field) \ (STAILQ_EMPTY(head) ? \ NULL : \ ((struct type *) \ ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) #endif #define STAILQ_FOREACH(var, head, field) \ for((var) = (head)->stqh_first; (var); (var) = (var)->field.stqe_next) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ (head)->stqh_last = &(elm)->field.stqe_next; \ (head)->stqh_first = (elm); \ } while (0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.stqe_next = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &(elm)->field.stqe_next; \ } while (0) #define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ if (((elm)->field.stqe_next = (tqelm)->field.stqe_next) == NULL)\ (head)->stqh_last = &(elm)->field.stqe_next; \ (tqelm)->field.stqe_next = (elm); \ } while (0) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) #define STAILQ_REMOVE_HEAD(head, field) do { \ if (((head)->stqh_first = \ (head)->stqh_first->field.stqe_next) == NULL) \ (head)->stqh_last = &(head)->stqh_first; \ } while (0) #define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ if (((head)->stqh_first = (elm)->field.stqe_next) == NULL) \ (head)->stqh_last = &(head)->stqh_first; \ } while (0) #define STAILQ_REMOVE(head, elm, type, field) do { \ if ((head)->stqh_first == (elm)) { \ STAILQ_REMOVE_HEAD(head, field); \ } \ else { \ struct type *curelm = (head)->stqh_first; \ while( curelm->field.stqe_next != (elm) ) \ curelm = curelm->field.stqe_next; \ if((curelm->field.stqe_next = \ curelm->field.stqe_next->field.stqe_next) == NULL) \ (head)->stqh_last = &(curelm)->field.stqe_next; \ } \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List functions. */ #define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_FIRST(head) ((head)->lh_first) #define LIST_FOREACH(var, head, field) \ for((var) = (head)->lh_first; (var); (var) = (var)->field.le_next) #define LIST_INIT(head) do { \ (head)->lh_first = NULL; \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * Tail queue functions. */ #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define TAILQ_FOREACH(var, head, field) \ for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field)) #ifndef TAILQ_FOREACH_REVERSE #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = TAILQ_LAST((head), headname); \ (var); \ (var) = TAILQ_PREV((var), headname, field)) #endif #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue functions. */ #define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = (head)->cqh_first; \ (var) != (void *)(head); \ (var) = (var)->field.cqe_next) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = (head)->cqh_last; \ (var) != (void *)(head); \ (var) = (var)->field.cqe_prev) #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = (void *)(head); \ (head)->cqh_last = (void *)(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = (void *)(head); \ if ((head)->cqh_last == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = (void *)(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ } while (0) #endif /* !_LFT_QUEUE_H_ */ lft-3.98/icons/72x72/000755 000765 000024 00000000000 15174131662 014100 5ustar00vicstaff000000 000000 lft-3.98/icons/144x144/000755 000765 000024 00000000000 15174131662 014240 5ustar00vicstaff000000 000000 lft-3.98/icons/144x144/blank.png000644 000765 000024 00000006422 12062431750 016033 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F=IDATx?KVa h`KTDDCmM-98}F]lws\!2FHSd {< H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F/IDATxKl[ם?|]ERD=-ٲNbI PZ1q_ 5)}&pf5C- |ŸZ%3pԸZԔ (`d { "CZٸ 22ddȐ! C@ 22ddȐ!C@ u^hccc000Az{{q:x^NɄd"S*俹\l6O?H$bԍgٳ9s f4 ^Fre}ܹctX,={/rE&''e'HZg֩|aa{w}>M :v&O<ַWJOOϞaiNXFtw}?\|jjaoo|^`ie  =S˜9sf_t FvZEUUfggeiiH$i>}_|{>~?믿O<p:a$x7o233C6%QT󌏏S.8}R}oO yWpf@i$ZMXYYauu9n߾L&9w=øFVh?^Z/ oY^^~42L|_W^n88d2;W^Zp?8x<l6fjJ\&JDP۶HX,~P3P87ĉ͂TTX[[caaP(4l6jUT󬮮umG^ʕ+[N^c.\`JBRZjC|kpʱcz4fD\&Li4qk^#Lrڵ/X,|?i\~+Wx7!al65J&v'5?_Bp`uzyיzqU8/===Jlb`ZZrJ%2N*K~ N?ۑd|29Ν6PHQZq8(bZx<Erh/N}0-,,ꫯH@ ?7Agxx78vXál6p8p8 rr(oN'jz k?|ӟ޶T[ k!O'r?/_&i:tvn7N*TMo~~GDчl|>ru:AիWI$ LNNbZ) .\*"Hz[#GÎ=饗8z(jMHd2Jj.&T&+=47odvv#G|@ 0lNDUU"v, BoΞ=c=&-O2dqqbncZemn( EQle4MΝ;ܹs]׹x"ulEAu$LQg;׮]s,-KZ%*rA4oj#.E5c4MuU5d9Ns%U*EQ5ollF]U/G}jwt%,>|^~ 100iYɎblll)?ٌfraxo[*ʎ7>Ns-('Nĉl6lM+DHRZHQ~_[ou@ccc9rDSVIR"KZEut]X,R,J<'Fe)5av<\.D"!K . EQdv/N{XdvvU\.O=L6) ܹsG:X;<Νʕ+O?]qJ%$BqJEAQ pKBA766QI }lL&^l6+f#rw%}].X,F$W~wov@7*L@ @8Ni3δ% qQ4MX,%HDFJ%n7O?4ǏT*Dfx<sa2 ϟgttp"ϳ.#n.ǜ:uӧOsƍɓ{4BSNrlXVn7}}}T*9 t]~( "r9i%vBRUɓ(f3vK\f}}h4*_5_|;X,YbH:Fu0IbY,\.\SRP(DT"NK_e;* wenn){9<Jk!EQ677YYYAUՇg2җjmhYp8ls.3 |Q^/7vRjՊ%ɐNzNo{3\ϝ;Ȉ4RDXT*:fY:\677놮"ens ::lbȱcp\uYӃpzY__'>RZk-v;v)hiXV]u]'( }}}X,5@ - >,J%JrCaw-QUU&l>U*bii ٌ! q8y" oNZ%077"=jz[cǎr> ѣG" vjL"#>D. --7Ӂ@~~?r_,,,ER!JfY[[rv l6VVVHrwJ%'3ׇDlHQ: $E$===x]U Ӆ`B+WXXX ɠiZ[ANYYYaqqL&C\fzzQR+++Ezzz|1ǃT*aXp8u>R7EPiԾR$l6[-E, @'\.cX,r9xMӤϑ#G;??YYYs;84U,pqz{{1l8N, Ekn7.KZpχjV'j߹/DXJ[aa(! i\zFg2rvP(CCC8u?{=uL&2躎D<22>Ȯ yl6JEFl!=Q*׮ʾ4chXϥR)Ѩ ? ^  033͛7e_fؠRx>݆Ft:M$]9u8Ά ;͆]"u]|^ufI_R*[] (^,pcL`0ę3Әf>cnܸA,y܌STl( p oD0L ="#JJ1ZXnB!/QbD"bl/8ul|LWx=V.Q%]ښ̩lE"DM+1>>dsր{~_"qu:\N$|PEJpL&٬X,nnWXrNP ˑ1̈́at]D^fJ$g8E{102X춴D,c``nmj2<<,SQN6@(bttTUkku}$@Doٝ~6NeVdKdE eebb/kVk[wk܃`xx`0H2X,n{U0N)4M#H$PEUۮM# qYy&XlZ"p8d$ŤX,j׵ujvOjD"&bQl6~q`ee\.Td&|>/4}}V+===8xW6 &(:Vl* B8N*[UP(0;;Nht:)JdYn7>QFFFZd2$l|>/uW[hޫeVXR]NNl]ץS,zC'On3;;tdӚUUezzgb6I$8BX,4MCUպ.M ;ux<zzzr :|&f,//S~DTwq~?r̽ng uq q:|駬L(Oj0uڰ|+PbfYĻU]J,#p۶z<%H033#穵x<۷) uN̺"&RrE("hW$ECdUUT4վ !L+QXI"fɶFyF8frr]י$;ŹPvŰY\.'_θɔv5t:#Hjk!laAPUT*ET_&  @)zzzb?>dG1VP'.땋W ]ܺjm@A. ۅ.'N022B,cvvt:њ^+?# aĺD%gv]) @x*B4zzzN'LMMQ,~!{kz@Vkİ\*5{/P- ;CJ 9R(pݲ[ϓO>n3IKj?S:[B,H[ ruxяc)l JJE dzy XX׷MviK5 {T\.G4 (dq)P,o"Lv4)|@¡6ǚLUU)r/z daii\A:YWSw@;$}E\ת*9׮]#J5M 5NDz]5kW3n iIufhkj:ecbZ (x<0LMMO6իܽ{W&sx뤥'cWߩ6!fQUUB#LNN266F(f+_趚^'-UWX@$PVE.\.3399I(Z?͛7I$$Z=zY,,K[k%NNN>YYssssf3]t5>Pvj;(Zb SSSb1mj@D2g/@urmfggnwP^W@J[2x|!H jz5}:lǼDg;kz9u@rmtsɚaOZ9w߫s@uwX e2vnuwX %HUն-xЎ^#N#ҋbX[,oשAGz\NzL&Mj'V< =ìG!.ۥl( m1t}Wv͜F7nȅֻ gj__ Q鹥%d'Z{T.W3b1~vi2_4~mu; QwY^^@\wFq(@po TW.ewǯ }\.pUU%tdCH`0hXU. J@@NB TVICY@z{{&Z' $d|YvVRMӵ ej}>P4M#Jɍ~bGT*uvCmF{>*0mmUFv |WU"|\.'yhR  @[XսvӇnhԀ(rb aEwbkR${Kҡ@-bUPVUUQ{l[K 1BDvRrp Ƹ 22ddȐ! C@ 22ddȐ!C@:PѸ ZT P4 }0Ԣ q L1&]s4sG`NL?}1Ԅրi *@Q;jCHJq `y.ge[и7g8C[B2-}?XNP5s?|$lIENDB`lft-3.98/icons/144x144/dest_closed.png000644 000765 000024 00000016751 12062431750 017242 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxyPGǿ". x`2D1X\⁢5""!D#cLRIKx Q$n}JP]v~p!rL/fzft *M˂&>|h9PdLQ" E-(T@* 6ػw/ˡR0A* 8tabbW"ҋ9s0%%%L]]cd2֭[LXXcjj/A@3?cɓPW-ZQFM/ƠA?CԩS0 x())'O (LSS1/p]* acc\^^QQQH} BTT/_v *pZ SSS@"@(ss@T+=JBV@b>>> w'- BV={h-2 ?@&q# Px,ZZ7ƐPThjjfT*쬖1a„^AAAӧajj555 jPGc3z<~5M}}ZM2. s;"M#Vqߘhy' {TMO-/H$Rmb1>|l|2+۷cԤ0tF"ŰTUUej+,11/2q$##>ԟP```;?xzzr̛7or* ^a6ip]O@N҈lffee_t ՜>)e˖i% TDk +++ƪUz=[lQ1`j?ҷ|PonصkN:h<pmܾ} wPG#V (00/ϬFnn (33\JωȫKQr 3W>u=ѱ0#Cg5z`С8QVٳqNɵc3ij1@EE5{I>1ȡ  H=ϟH̟?0ؼy3y洖 ;;;^XZT@prrBTTTsQQQprr5M=III/X."[n5M5 Z@@|}}imSu@ @ZZZiԎA1pٹ4裏hSׯgv:ԇ HO HXH$شiu*f\]] WWWZ. tl&&&HOOd.3fK{0k,Z* P=#)) fffT(cb=z9CGs˗XT@z ゾ}"99Xx.9s&&NH@>۷o׻کtWWW,^oܸTT@@QQf̘" 81U 4xJBXXXl U! h͚5DsrrP\\ (..ƁX+BUa( [o:}]]V^\ll,X?cҤIN葀C]ӆ PYY\ee%6l@ljPdd$ :}ii)222:R6l ˃(z$ {{{QQQP(^S*ĂXf BQ@KO<'Ntĉݦ'زe U .JѬ._JAU/رcttܸqU7n ==y<v>腀,X@4קڰaoS躀,,,@tO'$$ tyHR;k}ΦM`aaAբ# *~߿_ڿ LNNN #gٲem\l"ߺuwJ4L (%%(DQXXء8:ӧ.,,ġCXP(d(wy~~~Dp+WijgX#+ ضm}$d&FWmXML)3érz[@666Z6{;.A.8Mj1Ri]|g7n-#88X "<<sB T_Jؽ{7iܹspbhmD7sΥHJJ"|Mx1֭[G$NAZr%,?TÇk@݋k׮Nooo01<4l0XcԤAYY=D{"???|W~:ݻ . <<\#;fAڶmJo{Grr21|p{[WL:Ucjѱ1~x$$$ʕ+''\.ǪU>+G;w @ ˗QXXPF^^gjjӧcҥH$8{,Μ9LѣGc֬Y6mr9"""#@ FZ /'O:&:&1eʔv܉ի#TaD$6FH$8y$QRRF < E@@Ν;={Nhɒ%D ;ptt… 4+ŋ!ˑ{~ BCC91c憲2$&&MCZZ~GbN'Z" dرcG;S}}=rHUUUARUUW d8ڸq#Qa:iQC$R/Ĺs爾4\r1;rD"XYY+C Q/JZ}N~;; À 0' xbN[ѻw7oB!|>x<&LI&AR_~1 nCPXX+++< 555D;vl{ ;;3MLLYpСCaee8x 4i}}=aiiSNuC߾}lٲNddd95'N3grJ۷/_5֮]\\xx7Q[[ oCSܿ_?q]NNN\.撖{.Ƙzz'ޮ]Im#OPEMT|h9P1Z5 EMry -FlF<(֡u%x jU @Fˆ T?PIˈEӢNz0G-xa rW Oun6ٜP)IENDB`lft-3.98/icons/144x144/firewall_smart.png000644 000765 000024 00000044673 12062431750 017771 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F>IDATx]uXTYM C(vkvwv b.vcb񩋅 "93~t[sf=3{/9~`2j@YbwHa9ն<,F@tնRZdF(OʠL<- =ծO-Ub4`7u Zަs@uJ@=&ѵ੥*8&nkQKULPKU jktud!2Y>\Q-T>m*X LKHĸh$ :.)i1G0 q1QDK!հVqhڤG&R(<c{$@ N=b(=M5AwnݤS&ҁ=;[ε7_:w7pfМ)c Zj')!68҈!kӻg-MU:qeK\&ǎ~}hvefV]0ythjT{gޏsjpٶuZkTÔtuhj{>':2CwGEQ5l ػzF;Vq#+Bz`ʕօ#}]Xm-=AG ̱[{CZ`in,Đ#)FDG$&!&> ))xqHMπL&4\e `l^j|X'O">>Xn=zVz>,@?hka0u4+>ciQ5)qHI"1% ɉ qz0wL8KZ&{nst={[+K :weID5OChڤ }y>!6ΛMJ65j؀tX ikjо;HU#KQz +t[\-ў>)+qhjH.?@ښjtaU x|>4x@[`Y8,t]ҜV9n Uv+`BhόBr9ƍ6uH2 LNFBR~3wS3!Ýu,i6JԀX@.79nݺT9\Vq23hGP'.@Ç ϟytR}?y3N4km-]-&YK֮jG"ʤ{vı\g^ȺNpQM5kԀN:A${]Y LKΣDDPDWxt@l|R28dd@,ed)σ1TTPuwҥhsدv_]HKsO=G۟ٳgaLyҐqr k+?ok֮E_+'fky.t-&Ls>w?AGuk쩎Фq)*"^^ti%FF:Vx}c=rhIvZe&tqK[/YDwI-[cVi`Z0{&2@jm;CKC}B\ -_Jj|իkEjWXV|C%&u]+:oIDNlt$YͶ'k׮VIeKt`N2v{]idqd/ E,O)J/!.V-'H_Omuj''KBG)G 666~Xzpoi׆ط*8!@Q|uЎP= ^xc\CgN sC:-_BQ5"?7͜6VۯbۗݻWjġysfQDXHB Q4n 03܊str~̇zH䩪мٶF-Y4LGޏگUbQۻ Wzyđ2Kf&L,&/Z@I q%UnɓeQ4wQ3XL5|yF#̠m[hPabѿza]L?jAl<\y Sh%{,m#@K] @GϸӚUv- :^kW.Ѱh],%[?3j8֭Zx Rr)..JQ?5uhX׊u#LZu:wt}5mL ΟKqѹG͛>PUU1LJhCSM]hSSe>]E,A+N ɉ ػg76nr.2m:*xrC.Wx\ظ~=N_6 K/’KW9k* (0V>O bӦ Gxd4DY9{PeJCFj LhjB[` 02.5աرc¨ѣ1trM%')!ƺNagg:V~29AÁR)IصstqAJZ&F %˖e*gugb2(޶wohQ4b`~U&F;m)ŷ`,bAΛ7ӯO/ھyCţY?UTɱ07n~/^.]+EDDL&.SDDoh"E RI84t: ``j MtraW Nx% \ e1;z$Ҫxn9<$'a-w j=gFٳg! qi~QH.FmYlLMMaooxdff~&@5NgD$Dsp0b#B)SIPRV+??lNR55u8y ϫhѬ)܎{1ːHGn=>zuŲe0m4̘1NQ[&d``pww}=er@ r&e@aF( $H#!% Nk7Ti>3{ muS^}88hI04aC:t?pJBrr2q֭ 8HNFTȧJ[.ӧK`UFؖ"B?cf;׃h߱3;oݱ,k.oĉ52|5D"Z,܏.:u'4i7cgfe&4k/_{p^.x +; , qu\.^z p+vy[oLn 9ؽ=B}*d#=M`7n ?Ŕ3QR.s,'ٳ8c q &8w۳L!æMpUb bf--- wxh„ hժ߁LBI^@$q :w튽@EgkS1ٍ 93mξ‰B#0}t,_ry18x{{` --Mq$ѣ5ؗh ** 111_@I)B\x&F055>\eT6n Ey?ظy L,dPV߷n`x?EΝyܮ}%0`["xw>`ذa033C׮][ 8P?cI_8]D]fCn޼(fW-@bD yKZhݺ5Ο?`,[ Э[w?}v]ff#++ ~~~r _&"g#<<>/v2T9ՅZiC[!48׮y {tUkfsrV*yL}b MG\5 ͪhB!?~D%%A$1h ف@]]fff@ &~݋`bA*B.sΐ?uTr$&H͛DDΝ;3Xb%  &Xy浫Xx "iFyYiUOHGۯeXYY!''ʥ?gQdoo D"\Ճ⍯S}Mhgr95kf͚!(({éVtl_#ͥk ~1rs-Y&Q1aI#G4bJ!"ھ};l,"iii'JMM~Br B}ظZn5{.׷ 033j #3 %=!6۷`64iW^F~ 7rĭ+`j޾ÈaCaz09xb/`0 Ş={```["++ `kkm](`dd_G~is-1 $b0eYP҅ _:0]c toh(r݆6!+;bPSpa*4dž p Z6o[7g8ϣ[ne;ݻ>}„ qF|vvvرctttJQVT"+*\KK )))@j|>lO `\D,YN$DlD""#z"] &W ,jS_Æ ¦M`U~`& :z2d'niIS SRRpm 6'qu;w7n܀M1)kSI * < #7uLRRScRr1q,X (F&hڴI1VL"ɂ8]_g}^^Y,ttJ/3pXD@])$S,|]VjrA) C"@,cȐ!CBSSDe" ى'p54i߆5'}e9lnwTaYd 9;;ҩUUhٔ&L]@5(~zJHH(M(szE23'o֭iĉԽ{w𠘘 ]fffIUUԾ.A!hki|55iC_C@Z _8o V\f-ZUjc"q-??*VZZTx<]bظq#o޼ V()_ '''\rHOOzG9x@#C|T8bcHρ0r >&JJ3 -5U |5ux޾ ??? 1*Y]**x班Nm;,߇JGD%&vAAA6.AUUU]v-Νn`hhG'7id2ԫWFr9,xl,ZòuiA$%"IlD|DXX87n+נ\4-'%cϾ9l]AoJ ~f6mڀᔩXXXispak^f w%%%dggQF$Hաni1UѝXL<)G%~w' <9BYYY ~H&)D"QdPVVV/BcJH(IR?8c LIJ%:(lݺvAOWnb܄IŲ}}:+V!C,0rH()))T2 ś?#~^^^?>455/_"11bFFF6lw&L*e;Ёݮ4 fgёĄВE (!.:} 2JO鷞=r+t|?ƍQHH)CBBQRR"*w!@Pl6mڤ澗ݧ %&&R۶m+o1`Y si=@Ml/)U Y3ЮCGnN`0+Uwb6mڠm۶x5&Nb^b֯_HTʃ c~ %eQbyffʨ&*:N? LčkW[5l|M"[N=k&/_X\jw!!!hڴ)ÇؑL u8zz мE+hBOK Vg}]-d=D"++ &LBMJ8 *\/cXjc.ptt, D"bڴiP(Ѿ}{xzzN8۷GXXbbb0w\4h@їR﫬ϣήkxbp8L2HIIy-UTT0sLl߾R&/DLg 8$˖-BO*B~(lVI 8Kޏ|rz3c9'NUc~ϟ?ۋi;t'yP()#>>PSSC޽CSSf.***H$X~=w^H7$ e`م1m4fffb b15a1{ DLq:|}}vRj)U&5>֭YE q/.Q޽… Ŏϟ?SΝI P~LJ\.+cǎX,b0d2… E}%CCCZhyzzӧO)<=z:vwݲeKDg+hrZz5ijj~_U9l&||Wmi홯/NPLڶeh %W5f~rww/|777z9ݸqR)͟?1c?> <6lHW˗i۶m4k,Zn͚5K!Em}n߾Mnnnϋ1l02e eeebK2eHe硑 ManaC=Y@S[jrNPt_X_aXmƎp$LN ߧKr F L-[(_7o6lLMM1p@hhhcǎpttKdN;w@ (\OO߿GѨQ# 66l6:::رc.^XdQ |Y|LL*p'?~'ƫgO ʑC @ a #}= !''^͸qol7]oڮĩ}$֮Y s/,Q6qD899ap8s8ŏY?XZZb8<,--xb̚5B<O(aِJZdJԢSi1vΝCQ%HHLP FDWH CJj8*<@-mhҕk8vj|>lڀ惛K,&< _]W݀>[*X.[(@PCC/V,rI +Q@F  ܽ{fx틾}-f/Oʪ )BsfNV̌t }iղ?1,ԱM {FW.yjƵԯ_?:tegg+7"ϐJPOAU.*6ŋ eiecd2eddΝ;)==L$vEDD&Lt>߁  vvh޲z#Ty|3g]@ \(CeZdR>u:"88v+UXd1?xDPYbӧO `ưe1*Z4?1ԤIp*_YWiƳX, U<pIi8rxQP'^.3a2XZ m؄:Vub21a8HR_VԚ(H ͆\.G&MfyA*dB"@$a ӧO>E\.\| {se}JWi :q6]W{O1tP\|;wº NԪJPRPNv;#44G­wi_$ aia'''Yf%J"puuU000͛7quŏ#;;*d|qqqkl>XVEΝ%+ eÐC]]zFPUUq^ԩ)#<2"ڵ S&O [*k{|`ijǏb1() =~']t ]vU?߿1qD<{Lo4n.\"')d2L0ׯ_ҥK>%@ oUJ҂@_-bhެ)4+oQS&W~HOOKƌPr&$ 4@q%p\c3f :u!C{x5D"L&R)ի@[4YvZ>};v͛2y \b?vI',, 4vx+8Zv9=!Q6H_{}J#QY B^EGEDDs̡A^~M-Z(6fϏ:DRLӦM#PX̥(lٲ%5hU,,ͳR#e;>_ C4r:veg ؙbs)C\{$jį}l6f̘ADDYhx53RRRH$0:s թS{yH&P(M5ihh69Rq߲?m/!UxUQ8߷q`Ų͓LS&'wvpǠa|><== [[[]vӧ^4U>Ӝ/Mdwڅ>@UUA`` *,UEDDTLxt\ *zzz066!-`Y fFMmhiZ- U#e6%dbW3YWݨqIPP޽{vիذa,--wwwѣG ѲeK8;;O>-L} DEE GGGlذ\Mw[D5*22 GHh8@σ6,,`ei#6ꘙ ..vVʪ.z =Ik'Js>c]wB,ɪqCZZLLL`ooP{6nϞ='0g:u ^^^ҥKѱcB )(^gYYY Tȑ#PUUŲeF$LX[Ǐ Kt_fLGÇ .?(4֥-Wyz֭[G5446CR"-_|U` 200(qԫW/pª|S --ii鈈O-l&Pu`V̌Q^]-.LFP'=z #SoE!!!rH$2Edggɓ'FFF"22ڈCFF#x 7n@WW7oބD")( &&~~~`0Ul@m\.՜ׯȑy*CΚ5Aڴ`u~g|r>Xx)+勒|jР?"""|m޼TUU֭[dccC|iD0b0033CXXX19>|k***`و˅|>!ў>}QFFbbb~ Pkڴi#ѿyLfw)l###211)s7݁~p266Fll"kQ_---1]vd2ȸ_@.::z?tuu~_t(X$5nܘ,YĿ|4h@TnݪZ@?25hIIIHHHu7p}raaaTW*"zZKC]]FRR233b KUUU*UЩP-0U TE3ԮC-UBkסH֮C-U<wZK?Igpv=jt @pA5u P ] Y6Tea%()bkרyaTp@j-I0W,e40@$FK ;>yN(1I+IENDB`lft-3.98/icons/144x144/cloud.png000644 000765 000024 00000012612 12062431750 016050 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F IDATxoPTǿgwaYX`4!7C`.e`^{ΘC8i|h&dn6tX*͘MW@i$MLi*Va v9"tRyfs{>s~?&  $ Ԍ`XGyl;cAX`@$C3?T' C@(@ D"P B@ D(!P B" D(@B"\Cc"#==III0}nGKK ؈^NmłkעYYY0L`N(7z{{qaTWWAu\>z:͍5uhvuu| 롪*,XGy$`7//r ;S'Sbcc套^&q82<<А.+V;wJ sq)((kdʕ+N._Yx<ioo׼Tk_~F0ш^{ < ϟ}`@jjj`֬YHII3yصkz)LJmAQ̝;BRRQVVQ~HTT@;۱i&sCk|wX<v7 j@d`)uaZTb```znA=}]w}6OhllD}}=l6Ξ=Y,ٻw8v}駓I47n<%e_/M.](""Bl"S_񰆧퀧~ZۯE|J$.]oƌ#c6vZ@ (//GZZDGGFQ{t:oߎF:DtHOOj^4l63!\\hm޼po, VZ'Lu>ܸ=HIIax<Y,mɜ'}Z[* 6Q$8Ç}~dW'O G__>3W  ѣhoo^o.]bC.~;@Np\t222 S# NAN>zẗ́(Ο?IHߺ~m> wɓ'r y&Jv; }tD ۍݻwޛF->-{9 111 my*++QQQIum68HсJ;p8ׯI>rNpzモM6iwYl:tHXK*N[[|ᇒ=%Nzzٌ^x6l]w݅8ATU q |GVsźurJdffh4"""v=röngϞE]]ۇ~?{eDFF"++ 999Xx1RSSt$+MQ*\.:::p)l69sfZA L8݂P B" D(@B"P B(@ D"P B" D(@P BH040?Qu'6ƁI'_+w%P<#pʗs q!@.@ 7#s2ps" @1nOHnrKf7`es,1'fQۭ` {Nj၌ݢ7i^IENDB`lft-3.98/icons/144x144/firewall_stupid.png000644 000765 000024 00000051401 12062431750 020136 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FH,IDATxuxTGlq7BmpwJx)E V(E /Pi)Z`w]m)@~93Nfw{HY m&/ R "Pſs%J@ V^`a lQ &ОQ$1@U$ \#k[H.$( ^|]0/K|>]`^ $J)| `. $R_ xCSJJL 5՟j * *3%{aBbr jbSyё<&>1p`(cS*D]D`g1DR|x -.+d $]ZmK#>OO)իSK>FpЯuo]!5kRbZf!wN\L69@oR:6nXR+#֯Y%4 ^'6]%Z~T̟3Kr͇[ՅoGڿsΟa,,5{>E}sT¼9:؋ ?aEy`gMz}V&Sdh`nW2 ? |?j6v(Ld9;UX8;9bD"yc_'e(%K)R?Q-p!};mړ!=bc'&!{vJRJ*z Kt:-JS]p/샵ޞXXZac0>i0dΖg^;:? bHHH$6!XcٹW>ofMHdy┤" sS!M V,[,<@L7ZbMi/Ei\ŭLEon$ٹMt^̜6E$'k @^&O *lC|Ѣr{Ab7}E{7??}ĖMsOb|8~P}ڿ'!.V̘6YoRھw綷cQχ>U%}j x6mX'>eHNJz5:r g_ 68^% :3⹳c֢Wo6@|. F(&⣒%DZ5 ojv6b UD?#5i$5W>~Hÿ+4bY*b¸DZj3k?)? O=gܸpqrx)TXpvvT~t5֯cԩ[Ϳ*N#1~] mV=eW =#oUwqws%44x8gg\^ Ool07_X=fϞ -hyP<̙y.< u&ڴҵzJG>{N\B2q1Q$'% WI9b8_vBi@5_{&3(IHI#!&gOI3;4T\4טbbc@؀)H;{QWSˡ̴d㉉OD7($_n*,͛-RރY#fM*')" s?ibhm% ڵB\|A޳\x^ X0ou%d[k1".&&-^(u 26j$>*g%|BRe>*!6o\/'ň!En_B7@ΏXT)];|`7mR*̙5JS%M˝7 0ͿlFeΘQ1培ƪK9ym}IvAk(On]bС|T;w -]\aʰ>(LM7[j)I,79fD떟3~J_ܷD X' hѼ)#F F߹xVX΅ Թ3:GmD.c_;~qq6~%K-4h8/ K+ ܻiS1ǎb1S?[[ڷ[//uǩERB]Sk Z~LIb׽={veKk/۶қěQF(9 ʈ{pZ~}z[7u:5_P,o4 ŲŋwD"]:($ƍ-bcru@-ۛ@n L89`脅 r+zß%:23g#:uLVm򶧿P5 VX΄Wn?BDF#@fu)I,^SNN3vl:u/Z4gA88:Fdt i< =Cbli)ZA {gw-"%#رU*3qXXY< legܥTRuxa =v0aAAT^&PӼMLL t֠^]q`rzHNaW.'N֋5?-Ǐ>^^t{maSiĒ_$Tbo`ѲEsc_ul/|z:ѷO|Rbˆu {!CŢsEKǏo\|ӷxQghʕ>&*B9BXZD"bh,^ h\H_++V,],28ϟcGΜBy&3CxpZ?#N8rNVNBn"$ETXDEG  fϘ&b8ZuXrE[vboGEj OK u. 'dhUΞ>)jը&Ѹawn\X6w3b«/J9\=~/fG׽cF^y~vէG7aB'yKؓP!HĚիEWL*^Ć_dhuo QDM_Ї𡃄grE9~*.Y(]] 5T]Ϻ>QTޞTȤR1l`۰ rM1;t$IEv+8ibi+--ZSN}{vJ TXZD< }q%%&# yomdhkR}23^]\غe?vڵBe"{wm[}.F ""JtFԡD Ĺ'Ű}R)5ZLmd_njaz_6~ٲϏ> ʔC Qϳӧp077ÝgNp'$gl,qvr (,Z~;j5jN Òŋ2uJ߫7J3s 0`0rL4ͿDƈaC>byncƎۨ]ΝJ?<-[d)<gIWk $e0S;\Q)pwq k[lP]ӧOӡcGڴk֐Ŋ8eOÞѳG7F:A`*ȅW7)! 3w,Ю ÿ*-.!J¹sg7*+ZB|lj-U;"11L @R''g:0MTL,E=ܼs OJr7CRfMV]uƒLIHMZʬZ6z͘f 7la؈> ReiE׌= gW^aҵ HM#..42R`ƍlپD{g}G):o-֯YkgMۮ.b"3#_n"-׮>5RKV,'EJJJi_RzhSqɷGG~'V,)G*iE{93i?*LM1`y۲;jիVeǞѕ32p0L_dhH%W=SJU._ʪ8 ʔ.^Ai|/YF|طj{Ɇ5+h dr93gϧ~dGϞ@%Μ=֮ yDZ~}/]8K ޳ErcX"EUIڱc;qrvHaw<|qvqWwOTV( KP*}x֬@">lٴ:e=<{3ex`.&L• L*C3n -[JDD$bkk\.Go0 ԫWSlYT*ԩSV111!!.iS0obYp={D NgϾw >dd9wQ11 4)(r< ^EK`em=Vv(&#">"=#*YvI6nȴi(ZE`"79%2ad3W6ˡG}r!JsU !r .N̙3޽{TXKPr|yx ϛ7eTV#MI*;/Ǚ4jn^'qptfm`&o0wm?[!ޕ4W'_Ic֌m'vvvHRLLL?IL& Tq*Z(Pl<-91ys0{[iD\#sϾ7'7T*9w43fϡa\|;=*yu׮ҤaZnR=,_///GM\2͠~HR .t>222Bp<}jT&@P/[j2}8Gk"rݿȪի>;ƍ)w!rj\ S8y467ZL\ڕK4k҈/;uA7~J"qpp0}`OOO6YfAm9u֝{HﮝIHLb̄HLE.c=NVR kklEn›Ӹtvӵ?IrJEFzkW.gʴ {A_0oWK(ߢ3D1YrիW)TVVVƇ/rierrrҒ &ٳqrzEJJcά̙o=7||:"c$&1=":>^GdX(i(UVȥY05#(83h|з'7o…+S3~~1Z4kL˖_УWukCpA4lDll,!QT$%%Lrr2eEH$d2bccQ*9rwww70a$Bpxz Ȳ&rS3\=qqv ej|Ɠ'OIHf 1>`rz)]Y1,zDei7NbYswI&qMvMbŸ{.Cbcc~\B鰳#55m2w MG~3~YD|nnn 4˗X̿~e \.9H&ѻ/$,Z0Sgаq<:MHINd維TRaߍ⫯:qr F SӗmڴARAe0X|9DGG#ˉ'==ZϟGll,DGGNC駟߻/Y[-*s\?V03{3 a8r۷nѺe -Yk\.aTk[OF:!@j|M?,X K.Yfƒ eʔy OB~(WŊ#00CŊc]Cqp%9)aDDFOTd$QO $61H"JNJ>jkW@ڝ&N/< }H@@;ųP!-{**y#_h<=ڵks9/^LDD'NΝ;XYY^G*Ihh(5kdҥӧSzuRSSy)S;wܚb!x. 9LcBh4޺IRN+0enH`fnΒ嫨߰1 i ϞI*UYv}{MK:ЇѕF2WB&M޽;;wfٲe)S;v_Ptiv(JINN|޽B BZZ̚5;w2|GPPHnJF(S "**C>~CR9|q(.!۶x4Ϟ6QNfKgܿAAL#=^c^iG2|QI.;EKr]E,e&-%~R*^ĀjԪ͛ b͚5lݺ۷ocjjJ||<ͣE!Ht!Cp97ѯ?ǎg|$v\ެ=}Vm!,T;D%pv+ E'{_峘d(LMy1kW YRj[aZuΡ1}tXwaO6mXGT0[_ L27FPVmd2j׮ ŋGPЭ[7={۷Y&J ZMHH_~"e`ʐ nݺKKKz=JKKK:tӧYd Ç#sMzMOww<.dRp˕ڶ{uSƉE wnڋJˈC;~-9U9TBd1gtacm\S$[ڶn)Bn<?"99YZ-DǎE^DRRB!ԩ#"99xv,99Y qq%J///!˅Ĉ#(Zѣe{txx>.eJOO[E̛1E|7ҹ?ٿwfk,T}z:O]ѱ};! V(ܞ)־("Ehh5jh׮زe8}xHKKA\d21f!x (9(%%Et:1`abb"rVj666"::ZjhD~ܹs]{!7tػwYW/ϛ7ڷwCj >\t>cRĴP)'5 {?FN">>^j*֭[BV?VѣGQxyyeeHMMs"55Ue<}˗/7x8;Px1]=quӳ0vNXYZ"J$HzLdsfY);iK:- e{w`ر߸E5ufRK[,(l.:9ꊙ )D=gL<FĀ <ыL~xJKt56Ȥ mSN5Aʰw̙EˌsiuX.a%T*L0sssVJʕR*Q( ٰaŊٳg1KRJX$d2Y+FݝӧS~}.^JC@fJ%ݾ!)VTΡ).nyPtWL5)^+VYfҨIļdۿm`-?!qsBXZҼysQhZ#0T*QQQx{{sQQh4l5&..0-aT7nݻ2xE|}d>`.eO+\x.z:v΅  ?N%y>]=i԰!.]¿G74Zum*TVkLɎԇȊ{.Boi 377G&EONе`֮tmDةYt+ŵ78삝+ffya>J 35ߏEr4Q0&L`ͺ TR'~‹󷒓S4Sz{IOKeMh_Vd1-ѣGDGHaiiɼyhӦ &&&+ …9wZ=O'": ޽Sh Yi8a*9bRaki }Ӯ|uffܺɌiS9{&2?-[J篺!ub{=j4dddz/ٳg1 ,]B++E?~:]v}+$ jׯSvm4h˗w޿@LDwbxɁ% $Z!,O<%<*'aؿ K+vP9.?bYteܸq88v2{~>hoOa001114Z-NNN<|ШiZ6os |=+!==xR &k׮ߟ)SЬY3R{'%`п}HupEڎG/}]س'Ox.;øq9z8?mĉT!1ONL`y̞;ztѣqpr!""///6LJ#lٲ+Vw/'Nx⸸%e`4i’%KX`9$&&~JNS 2ZurMV\΁C9a}*W&waV^Ob( nݺK\.J*ܹsJ|E\.gŽQg̤^z n߾?Ŋ}U=qi5LrǕk5^UVc 8k3VszHF%6jHv*̅'R$F@qF`QՔ*U ;;;t:JgϞk kN^}(W4W/]Ys^GBЯj۬i)j::< {d11h FMpp0H$裏&NTR$''[>vU~XZZ@T^Ǐ5f(#[AT&RaZVYKbJI>5pI* ׌K3!?ulݺeb0jcmmmJ P(\CNǣGVB4iBLL oG->>>8qKWw~?Kb|sfNgт4k[lBذvG"5=jq _*cQD.\Qx T&LҥKɮqYڴiӍ:P)' z>s:Fm7 q1Q =GM5777f̞Kj5T9Ҵqvf=˗.#TY;GK 8::m6d2iii,X@?~)*ŋs:vT*ɓ'yZuF?jժ1V^:))).\|T-޽{.Ugg'|Ց%JRg<< aRiF勲uϜX[[3nBοgdZ͕HrEg9g>2|QrUL'Kmذa0v m۴!99GPX1Z-vbӦM?~sakkݣAyz!  fΝ̚5u뒞\\BR(S L4)_ ^ODt 1\q PD!7'ʖxg[\ !8}7"VkwJz ͔~Aeڵ;4WW?Hgʟؼy3a PkYړPΝ;Ƿ~KѢEBP|yl§p%J,'|Brr1&dKFFmڴaĈZ 6/QFJj(BCX[[Oùx1QH>zq2}n$'NÝU+ѭ[$L!JS@pp^5koжUK&PүK5)R0ݻQչ> E.3l0jժExx8>HM\LI5>+VN:xxxٳ D)Q._pHfz+ j``ۣKg> TXZcELTdkTKM @9^rmڴZVgϞ+PZJV6m:uʪMݡعstou-))Ie2j(T|؊ZJ\s?WPg3g4W(Ϫk1 ̜51pprPo\JnܼűǙ8a/\e7SF###H'22333qvvҥKog[URWҾ}{233~zIph,,,033CPh޽;/s,~ ^盁|TBO/lGx߮팟`:uFΝr]JKKD g9HWkо njůTcdffb0صk...<{h]FrOKKcԨQR$** W\B@բV<VZ'|BŊt)R???^r !8|gϞg&]:G@zR<`+K ܜ*R[;;ܜqpr9KRj9QMCG)_4ɋCH)rg:_v/HLd2SS)YǏe)%nnn=wRJJ*E\\/_1GX"Ԯ] 6PhQݍUbǏsmF˖-0`O>]v\xSRZ59z(׮]3:CBB^ʐʂ~}0`@\=^>V@FLl4-[FV(]4[&==EGRhѢL2GGGnݺ¤ m[ѣ)[⻁Pe[!wٵk'Z|B<>Bc0!Xf͞c\>2d|%a01ҥ+ѣGnZvms?+Ǐu4i҄Ӿ}{.] d)ʊjժL6mN:PD J%r{bjjX{d25mĉGw/7Y$%j7'H{wԫWN]{퍗7)))!Ջ۷oӾ}{ߟff0L*WƎ]{ˋk׮a0{.SNeΜ9mۆNUVt:#{ɓ'i/G:xߏ&#= Ān077E֩k!,-TXcfjyL67n}i˼$HMNb̘5Ĥ$cC ,k׮TX{{{prrBѣGٱc[l!$$pwaϾ ?pJEbb"W^ѣx{{Irزe ѴnݚӧӥK ?kkk#u1???V\Z???_{r9l۶ ŋSLj5-[wj133˗u[,}ѓOLt4dB[Hqr@`i^pYjժIX(2=TĄ3frR w<Ⓩ0mڴWj,Y 0aΟdɒlڴ c*qΝH$ѢE ]ãaaaUܸq۷mLyc 0T2339tQQQJ]PhQ4 SjU?++T*e߾}߿R/ LFժU ~%JPX1,--9xJ wD&{Gf/;5jRj4?ygIJMg…A}5r>w!w6[CǎrtRVZ9s,cƌ3 F0899qyLPP111 j5mۖ͛RD*AƴiӘ2e ]vܜ.]č7VqttʊOboo5ǏG.•TrY#͛>%oD"!33SN7pFmXXX0m4z<FnݰaڵNll,\~ӧOB`ffFŊs#T=ƃ?Ejg+1 \RôiӈaÆ 6 s8agt0LVOҰ~=N8qr}{r}<<f9lqqq!%%>}н{wFN2|.^h4ųR ժe4LXXq߿Oxx8&&&dddP^=/ŋ /_&33 RJ^7^Y߿U bذa "F=0088@|||ϟS^=*U*sAl~Odd$>|I&xbBRJ8g,X`رcL6ƍӴiS*TQtu̘1|P"^gfr̜>;tj̝sA/̚3G1c-[ёd,,,Oy:VKJ(__j|G3o5[7111R۶m#**hN8AΝqttdŴhP*4lN\.4Lyz+W{uF;!a'<s/K^FdŶLLQA7yzjԬEi/ 55 8r͚5cȑ?~Ng  9B\\NNNiV˘1cW{1$7ofٲelٲFڸq#FH$#9_*_D"V˽{ԩSoܼymBLL UVeԩݻtʕ+G4hnnngJ%6mbܹ=+=ٵ*s%{Jao_}|ts˷(6vZ[bbjmwCnfJXj2[ꃮ\b ޛs|`0h\p;;;Μ9C [.uԡbŊx{{ΫT*nj\,,,0ƣJ,իWy!={ϏVZѦM/^?>>>-[BA͚5ٽ{7???h4ԭ[\UT12vΝ#99ڷo bŊTXh%v֍6mC,,,(T8;;SV-;`߸m0!i=%Sμ]ni/o_| dgATT$ǎ׷7[*_KsjFGqa1oguk[~R/^4> ZHNNZV>|X)S&w+rPTiӦbbĉ?qqq"##X`E^P"11Ql߾]*UJ)RDGxxx[n &딀1cƈիWM7n;;;g# wc'`miW!wّE})S ';<ܰqtF*hgܺ9sXj{;mYnŊCףj^m۶dIZ-ZCoQF b帺ҹsg233BxFAP_'͛B`iiiӓΟ?O޽qvvF<['D* G'<\)닧OQ<ݜ)ZN^Eҷ@|}ع{i8-,,UVt OOOرck֬޽{y$J֖aÆݻ[J( $ ?kkk~';ӧO1cGaݺul'O%WmT*0':6\.ޞh$''/ege3J(sHIIuQʭhԪU >S:vHB^˭[233s=բj̲p5P* ʗ/ƢR͖{e222h߾=~-+Wy游<V. L&Dɒ%ȑ#E*UDHH'Pd!W\ .#FZNNNjժСCN*lllBȟe!nabjj\.LJwҡCڴiC 億0VRvmڶmk%,,읃+$roooj׮ɓ'JHR~g#{ٳZK9qqq<~L^ O333v-J#C@&Z`}H DZ0RLCS R ` $'CSnKP cKS@&:xl(ɣleJkkH.$(Dg/)H Dˎċ@} ` +OXQUBWAjxH6@S$)x /DآpB_wF@\IENDB`lft-3.98/icons/144x144/router.png000644 000765 000024 00000025635 12062431750 016273 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F IDATxiPTg?C7MC݀lB@HD3:13j*wSS555SS35f_J$$Dwq\P}_{KDVR)y><99*gm| !A pGw}R _uecB}HR|#A&  ̓G$,Tw71y\dMRƻFc4jy\d=B@xQG#H *䱐T8zD-T~GSD"YIJydɒ%$KH, Y2@ddɒ%$KH , YddɊ%OBT*HII!)) Ʉd`01 h4P*1:: @0$ 1::J8&p\8Nn7NI?CCC 4WOVVv4HII!!!dL& FKRhP(tc4A H~~?%444墯^z{{줣hg$&''SPP@vv6YYY̛7TҰZXVRRRj3y|>GOO p8p8455100 4+NRINN ,,dffbP*c˵ twwA{{;twws-hii!MۇT(f唖I^^yyydffRb BP5s :|(99+V|rrssYhŤ ۷q:dddf) ֯_OUUk׮eڵ ŚB ;w,t: .d͚5b2Q(#G`6ٹso@ 3g8uĉkSo&b+c}٘_>..R)~_Oh"A:;;ʚ3 $HMMhժUܹ*NC >>ٌᠭ2RSS(Jj5qqqܼyN h4S<l6x<:::~T*~i{9^xb~K~?O>ɓaV^jo S"Ve˖qm h|BAFF  D"m4y饗dǎ3v&5:{,9Hr͛(Ν;(J-Z#ϕ/,e۷=JNN^c֭TTTI#'Ng7#`A(,,NZt:vލlFo2222w2򗿤k2[o@RR<O `4jt:)hn$BAEEd݋{L&~c֬Y3q\?~K.ضm ,h:]n* Ax7hسgUUUӖ.YLee%6l`hh7nX,fl64t$v:D?iii@k֬g۶m9sjJKKQT|>l6999XVf3 3Gqqxg˛p B cx۶mc ۩FƍԄ a% ".v0ͤèT H$bdd6%D8}4Ǐ Xb1xr:x^c&\騮&b;waRSS}6x^RSS"%%zzzy&w! rM._,Rz=999z((( 11͆lFVVL3PRRHK ܸq{rss"))iBxFGGq:RKJMMfzիWKNܹs\~^OEE \.x.^zzz;wNŋpp>Cكbw~:}Z+WaF}w]txb6eV^ҥKyϟr233*to:Vc%G\jxxQBݎԩS;v͛7?p7<<̥K6UۍJT>֤R()),6Rdɒ17hQs( kjILL$##c x hSNaYr}'g7`ZYv-`pB Az11ӺW,^G%8[&771ŋimmfiӦ<.t6n܈F#Er5;~^Z-&iҟ9jdn3(''>ƙ% c,X0~~:E100Ç9z($%%)Huu5 ֆ̛7OˬVTt:D"W^ . /,rssinn]233IOO q:lذ7(J"ȸVb$¡T*1ر|N<ɉ'X~=cjI?[bpiooΝ;z-ZjF||O?Çb f Jff[1>`WbZGIi @R)-mH$wAU(c0Dqˍs8ӱZ022ȈEv?u/9v/\gC vN 8&Y " R$WVh'..F#VQTaɂD\b$A1§hHJJ"));P[[ˡChhh@ӑ!0R)]3)s)$]5--p8L$b&χDp~u:ZFPLz|n7Zy&_5gϞYjVO>ŋtuuj=n\.j5&iNu:]%lP( jD"tww388tT*1 c@-h \.0z^?cǏO?RHIIgꫯy&7nܐ"ZٌJd2j%--Z-`pʩ0IS]"FGG'u;үRȐ|p8,h4x<i2Btwwp8(//'//OGLXh4$&&>?Z|455q!Ng$ +J]J(**ZLlʯRZZ˗g PWWB੧>JBTv\l>L__߬|_1p>nOIAL v;( q Yf̈́䔦N:ܯP(HMMB/UF"FGG2p8nwGZ__ORRҘYSN begg':-[L؄\FFFK69++]vv믥 (vG%?vԄ׿ԩSQszbԩSlذa ÜQ())M6q^~Yȑ#ċ/.0F:ݓV%??ǃ`bZX Rav сVh4bAӡRXr%tww6dӦMR:%KΝ;g477J\\۶m{`Op8 :̨/Mwj5&mx^~)Kss3qqqvi[`HAO?e޼yN˘G=c˖-;?~|^ FFFHMMl`ڽKv{L4D/H(!v>^,#Gx +Jŏ~#_ٳggl}>T*پ}k|>===l{oRDIIw"\bsQ'OݻwOke̴F}Y.^ӧ-h&:Hfk K,tT8O6Bu W_}E{{;?utz<ߏ^gQu\ے)((b_Jjr?`n7f.>c";w|`$Y:DOO%ֳo>)/11Q(?K<L~~>?J@.^ȉ'Xn+WҒzW^jJG{H$Bcc#,[lN=j~̙3\p /^<ߍ@3Z|rCcc#^g֭( Nt&w$hu.]rgwwwo>n߾͞={ 9DO\K?wye˖f͚G l6233ijj -|ÌHAqxX|gr ό;vVYdC':uvv{AuuvHDz4" I ٳq)MIh"\.WZOVի׿vHD {<%x 444pL&7nz"ME^7*)z'՝bjiiܹs l2JKK%#԰gݯh </^l6ʢG܍Fc嚖LWWΝ"JKK'|y6gX͛vpayVgHvL3K~3MyWgbɚy+%%E(x^yƣ$/O,L3ݻL#* g|/t:e"&)%gHL& 2!x !)))Vu{1a޽<4CCC›o)lذa6Y4NC:*vAUUմŢ9x @ ?o4&e@dŊl߾-[bŊ9^Ajkk9z(_|E=2wի)//gݺuY&jp sYjjj8y$ΝEISFF/ʂϟ쓑餩W@]] ttt @b!77 [VL&Tc0j$$$Y^䇌H%BbX__===Fgg'c֞&$kNI), Y2@ddɒ%$KH , Yddɒ%$K i( GTD t 5Zq uDxei"%<R-Cle ]YP@<6 ]Vzal ,P tc$kSy  kV]&߅eufU@]?I!߄.Q7y?t?*kIIENDB`lft-3.98/icons/144x144/source.png000644 000765 000024 00000015511 12062431750 016243 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FtIDATx]KTgWZНdI'a M& h%K2R6 ذMHh4=tw Ql߳H_m?]v]dueןι^K:i'4!(`y >".||DD%fsC/tH2i- W}8 pQ'zF_ ]2pV'APY{9qYpJ@NF00 /aH@ d@@@@@@@@@@@@@R֕lnn駟ƽ{$I[}QE+|Q9sh[9KKKիWU^;z UU@e|H/!OoNG~B$8ymI_3P%I,p @'Noz .~},c=uG ozG,:%K.,4!GX(',˩} p8qU>}|M}h4`F.E}?$7I֊>G9'7|kM $ s8YdYN߇8hZ}ض uv]/ * e$AӴ1"S:K(gy*i(jf:۶eaaa^j6p]BQC?GY(TyZcids,öm}X,ƹj4vEv qyޡCܾQFRMbyZ$wdeh`0ٖ5#mYKZ-{-%8-DW Ǒg.+#qLZ+@NJ '噶މvEQt]Ou}YsJibee~0|Z'֝iHk.+mֱm; 8&%Lߕ$O'JLׁ<'Ȳ|:3uO"J,*+@!XiNA:ªRQ{YfZ<4#-}i΅랲+N* TxMylۆ(c"9)š'w4)ΒP+]I(IWe鞼ui!<=y]8\ׅ8!:#5uxtOVוו[xY\4%""DXUMh2"mvX <σ+TRFJHdY֘)zhIH*L}>ُJ$3*K ґJ&<?s|smIGH, =[DANwQazWQA,H^nqqj48vy|'ٙ;5:+,YNo' k6}.\sp#PޙE&1tR^>W8H2j{U:En6vwwÛOO&Z;BZ[ /~p[;wΝ;˙f#<ׯ_͛7^~+++8sLƦSZ__8qO>T~ADF@5ij>թ_$ Y]xKKKSGn_y/k^i^x˗/öD$ŋڍ-&.][nͬǼauuoΝ;u7ߠ( ^~e|Gs;7\l_(O&< Μ9~. TJWƬq[f EQ{o.}u:lnnBgʃ pyKꫯ:~mlnn0&EQW_}u5Ui$\t 8rȡz8!g A%?~? Pq5x'Oѣva=))oplQ z8躎{akk ~_C4ԩSX[[tBMQ3]""]o_]a۷ockk ѭŹYDry H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F@IDATxip\e}sr3Leɲ / 6 )*'*b&&&&bLϯz#. 06`6x*˲}Ks#S%a;"Ö=yg_Ql"= x> nW@P#υ_1/+>x 9RE ^<[ӷ \*S] ,&/?`Cq]KXFhnq EGED6ע-"P\""Z I1\W"KQWX\"*H"B`fQ"P@&$aE,HC<uz=v,ӗLlG@_bv$fIS$@ Uz=ZA@Bѽ. і $hIxt: (h%hɨ* *U%dPU1w󽵂UJfI¦`E [eHNV+ Cc4IE!$XY,lT"o_Φhpi4j&Aщ"zA(ImR(,ϓ(2dee߁#$E$QRSɓǦ`IΦG9qY&(YS)treT|E}Pl4`0PQa$tyFfɨ*&Q,Ih0ɐPM(h>*iU%*ˌg2''t'xSe.{A 7Xd4h4[82e,lfK &L&?p[ZA`Oo_hYU%"Le2܈ǹL2JY44S``*He@Lt V+Պd6P- ^lH(6UU3H& xl(0X }21,HYU?'Q"AO2D&p5UD ,jzt6(DdBq1ᨩ\U[Y&шhD$ٷ)(5۷SmiYFeRdd"A4$<9I#66FrxulHm6[Hi,18r3ΗIi44yjd)IHdBWU 唷aDg!j IwngRܿQDEA'9h%EIFGapW!@/tX5*zbDJ&Ej bٌ+F͟ЕlitJX0VT1ךdɠT ex `Zy%n< W^ t7hiiaƍ,]˗v3u8Lƍ–ep){QzGAgݽuPt9$ ( .]"(NFѻ\PZfCc" DR)N>ͅ $`x== իiz)̕w~׮=r=|3g{aDcy9fܫW\J&EN$Yx֭rfKZFIRf3YƘbǩj2pi$ łbAzzz [c4bAgDHddc1R~?sUZ VXAyy9d2nqݻw64GN&9Bo~CT`x1 ^zјL_8>|'O2o Xz)lgp-](DINv]DO4Px1:9 "x:QdIGc2z:KzAhա1HPO&Atbt 4 ,ZAYYi5V^~3ó>b[:: ?!71|3~#I&EkRl6QSTnD٣lm\Y6o-::HMMXrɄUSuhvJLde&%%Kbl (⬮gxQ.\V*"rPUbä# 1WWc.$I޽۷g<倸L;]ظ -ɠp>%+WR|9:N笉FAr=MdsuDQD@Ć ^ŋVhA½T_6od?###455!if*7m" 0qr.W.pXf uuuR__{2^X ʫʞ={m;3z0CObl5۶Gr~l"A{-B7n [Z޺ݻܼǢEv$j6r2I ի4F#%K7718~8o&,裏n:lN'zKM ,uunLL8Lbl@Wz2Z-NmF"M:&>2RH1$''Z,8ZZrhXhkkC$JJJ~:tO ɟٟ{nv1*k-B]]Dg:Źxw혺t?$۩zig77#_:r:MG)`d 3[o122BCC?)//Uoe n7&,@bbl8LCq74`F[IZ,Ć >["Kcc,+<[>M:K~)oMldP۪yyCi{Ez~[F>%ƹx1 Gڅq^_R eb.OLzvźu0cRX CY[F9(8`͖V@ň!E2R,snjjjlNN V[񜏒ޑPYb|!OyHi{; /3]dkۿSTENӏLSOK/GG iSe%rUv)Ueҥ /{n$I"LGoO֭[innfѢET >䱱B88֮Eڊdf A8z(WΜ!z\$jYڵkq-_N,ĉ+ R[[Kyy=I~_Aii)Jkk+:nNRJ$`0p8% l6I">:J|tPTWByg2jfoDNP2Dk]/8֭[eoe}B^駟"I ?|.PilAq׳p^ZEJU ~chhu`R"AFI8qko/fAZ-t:44ܽ"رc\tRy/_>o Z">6F|t#G/\dSgX{s3/|17n,:~m~C'te7or ٹsgOld$, jk\XYltX(Do0#bUj$TUE׳dlB__⭷W^.ׯoxGx'ZZx<^{ 5k|(7_cY٬T^m7n 'Dc? ,uu_K.eɒ%޷6M5kְbŊwٳg`TWWșĽ^ygNtbKo9NF}>A Z4oݻI A4, 555qN<ɧ~O?=k ŋ \.vIIIɜO4E$}lҥL]Hb|@g'9 s:66I$qp3M.]ڵkJF˗n;F2dժUn˩сCC.CSU9MHz먊5k45QUUE[[+WdŔa2xyHUDFFH'5QeTkjgH::H|˱͵  6n܈VeXX,hq:;;tt:fZg,Ak'E|llaFQ`9۪J:"TSSC}}멩L?L&ݻYhQ*L:"1>+ Z00[6Uecc{z&550N^gLMMraps*V]vyf o>*++1vx^OUU "X,+hF55].R9?1stIc2'#UY&HFs\Q7B 0jy-//GeE!ɐO]ze5߯+h`2NtwL&X,@xhhS^+8ޅ1FhZf3쯃7orN>M"d2zjV^ͻ˅ z444vQY TKlbg&suՊ@2bd 0D#OzWxۿt^H.`0H6Bшz"'HEDhGZqtn'F&! 2& to@ ѣ8pD"QQ~饗Xd sN>cn޼IWWF1etoudwu5,\NG&E G65dYٮZE UUIO |̔VȲL:9 n:ł`4zc466H$HRhZl6$}#I$twwp l6zgyuxjJ[[tww>l*EDQp d.z-ZT)L:}` guR&jI~v>RZbY[ dӡ1 &zbi4!U Á lC㏣*hD"NtYd2tuuq! MMM444PSSCii=Gk*t!^~ L&ꨮ'Lňۿ:xPЀ`z#2DAH$h(ߜnܠ\--R[[㙕(h'bjjꮪYl=_,#h4\Ȃ@"@Qv;`,t(ٿ?F֮]͛iii+ pe~_3::Jii)^x+W/&oתjZXkkIY,ȉ.Y H 180HP8 X8=~HOЖfzۻyMQXUU>oF!G]|FM"@ I$ ҙHڰNɐXИLQ}]~?nv0׮]ׯ裏zj(lA_F餴ƞ={hiid21ݼIML8}sHz=J:M֭L*EFbMQVِ++tIR(*%V [HRZԱ1n'P]]G9OM$ *L&&&?uT דr>0yJ:_gw+Y ?@^nozG1Nsi._$Ilݺ[x^z{{9q~)8~8Gyq>Cg:5HF#J*#N>ZҡO>a$ՔX.L$ u:dɓx?ߏhm#yXNj[[dYիݻ06jjj0\z4c`ԧ愝yKMMv0 F!20҂*ddrN'ֆzl˗/̺u0(zu_زe o+fCQ}C(M VEI",Gx 4fs ($|>|'OR"ֆ(Ta ի8***u FƍB24GH (߰gkkez nwAh55Qy3mAruZm! ;wQz=[n-b̵Ma {ꨭg߾}9r;wL&D"i"xafl613 f,/Ǿpr**Rj``($(TiN<Ɏ;kRN<O<13٘^t6ɂv4ÜQjl ;y$:ݼZв,s x 72v4=FYhegŋI&zzrs`.NWh=SY"D *It1L`ɒ;> wž~iYs m9Kbl׋sɒ}*d4;:c`*W\ᣏ>"3<}n||@ @uu׮T/YH$t1LB8?d,?dVKXU9сqr^/t>%כӗomVKZPE ܑJz\|W_}qO>$OO(5 !"$|>b*pGbTQ"91Alh9~?~#jjj$I VuޡNX,lޒ$`0$Ɗ V@B8& %KļBQ] sfQ*b{oF  $ɩ)bCC$'&0WUݱoZ-Jl Dzzr=7nF_f!ߏ``sj޾~z=aˠD{zww* Fj֬܌բ( xd2dlbdZE4%%X\pITUTVVlٲRH 2[n% ݑ$ ͆ "'HNL`wl]%%$vv==> BvYi# qw"| $91jjt:$I*tx<++p80e2$l\VFECׇ"u;$Ib޽\~ӧO4w&ɉ bwłkٲ\N vu Ŗ388(ܹKg>sb7U%tkPY,.̍>wtKe GGm-%553O8A?/]岾5\~.\g}tXjk1WW(>6JpPb0g+h#+BOOG!ɰ|r-[6o$%2@UU)//<y^/A$JW:g?O* M%!Z,XjkszJy?~.^~nU̯G?od'|2W.(/%vR )[D1x.k^f4;?p rQ55/=·nEZm"UU⥗^[ka0HR].,**>-&?fZ+Z^/+W跿W^g I~FCCüIh4J ($⣣N$חS]…Hs 4܌\^Nra)Rwcjj_~!@׉^~e>^uvڅ{K aҙ ɉ ._ƹx, # ,`ڵ: *J&C Stv 2{:*8Z[޶ ҥsn]r*EM.]"9-J3lm+ƶ;#A`T[,˝(#mm8-5ZܫWN wwc)u(kkEAQ,Hl6Kyy9uuu3&&-D&a|ǏΫV=ԜZH_2;$55 o0m'rmPQ?laX(/]ʒgklTw2ӧ1g*O'$ׯ'Dz{/^{ {S*LHR6 :*H }ANxGk+V}\(Dx}χ۩~yEѕL+W?tttB۩^҇N y O\6G2)οcppTrQe h7 =1; Ô>ȏH!IE'~K+_d2;:#ݻ1ߦt|>SSj*7m±h=}Ytnlx֭%q>rB7o~Q3_FiL&\KRy3a½y::m6+VPSS, q->C(555P̂sUOgiΜd \K`T^~GS IhmJj,/dc1>ѡ!Bn去,zܫWٰ+==!t΂ ضmUUU9I󅒅mp^= F߫@~%Q$&&}MdjVDgXHd `ppa.^H,fþjfq׋ʕXwK ]. ӳ_ӦYRӠ@ ׎o ݸA`qt%%hkj65i8`…/ Rq#5۷LĆ ݼI͛}W^&ni9Qkw0,Z_,D"^ep~G%g[ ^zM{:xf4=\ti։#G_v-D:Sw/5ksܱa>qcc~2(1Ryi`ZWłTV SEFzO͒b9g!i{شiƤBgg缒#O>ˬw:ظ^ȝp<}s_Og>P՚ aVV+}H  KM-+!_̋?W͕RdBqi'\Ygtʯuh/~{7n 70zaܹ\%?&6< XbgYg#}! j/5&d™ ݄{3FIBc2aJ kib.W?}s(I_[n?|: Ϛ5q1y"k׈9$Mlx+W0\gɾNܽ57+,h8rgq$''Is|w$4Fc!i,+R[KMMa>m|LMM_^{[C!4JJJwuT}ᓢ dKNN %?MN&sq:](gCz+WDl6tv;;p0͗Ioh5J=${A۷W_o%N$Gl5?spn|z{ Ӊ&O"ٜ#2<߃F6׿57s΀7z455waaAeUfU%UTJMB'm7/Voެx55J6>lD"զ&a^0/KjjjO?͛wj2QUdq$ۖfC??gjjj?ͷe ^~=̋/xδmwݻygg~Jr먲jŹx1o${[߿]HS$<Evv֭[ioo5 2 ϟ}Ǐs Μ9S .;IPUUŒ%KXlK.~7twws5:::r  }@svS^^np8lft:e ~H(BQ)` |> 2::J__s'P?-.AEQ$PEQ$PE TD@E TD@EQ$PEQ$PEQD@E<PW":1%u(+SסCaYwYEz׋Q=_iI p (Kw/{Xqm\`FEcy2'k@oLxdp,j/my?I(ބ.淨|@lO7pIENDB`lft-3.98/icons/144x144/dest_prohibited.png000644 000765 000024 00000023430 12062431750 020112 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FCIDATx}yTTG  _5 b" ʢq$$%Q8hP A\h4h&u>^isNtUzUWu֭{hN @# u O ~ɘqgho0cj!}!O: nD+p`(=C!~1%^y6x &~1H#Ԁ }a7bHoܥKNNNpvv:w ;;;X[[ȿ LLL؈ǏP^^(**Baa!QRRbhS =<<<鉷~nnnk畗#77ϟGvv6rrrPVVR H/ H0l0bĈ߿)//iii8z(N<:cQ֎J$ ۷\.']\.D !D/pwwwZv-UTTPGDEE]ݍV𠌌 #}CUU%%%D"#L:u'}R2d$4bdT^^NBA7n$ #I¨ ݣ0:K ƻ!>>+QRRB.C.CPh\H$T*T*EΝѥKHR^۞^gdnn @&Dׯ#33gΜ3gPQQw7ヾ}j~r؈+W"** OGAAz: g&B Ujn&_NM#b1oߎPN_ŢEdtM3f fΜR@ӧOǐ!C0|ֳ)S`ee0׳*S^^(TG*3`455Ŏ;8Dtt4ajj֋=}T'&M! OOOܾj:BCCcbV% D ~'N)--/eee(,,lwazy 8[lAHHk%&&bĉWj+VtׯWkpLze3g6yO??& ,ए[~=+΄&eY-9|DDDUvȑHIIa=QQQ8q"jkkY… ۬MhҤI֨ ڵ bKwڅQFѣGY`>6i#o2ҥK;`~۸zhݺuP.\z˖-qY̝;W4n8Ջttt: uSݻJ ^0@]]e/aiiɘOT?0Y Ν;kkk ww%K\R7o`ɒ%v}i9޾IPVV.h|2ضm d=z7Rm=H$gϲG,[z/_ަK.ՉKb-[ƪ]gϞUad2̘ȑ#7oK3gVZs2@m mc޼y8rcc'Dw 2ЪUgR>Ee a֬Y011g}3f@}}=f͚?gۗ~z>| /"<< ՖXbJF2/u666Ej+W40)66U-E,Fݼyu@ ~A--_\/ [oiT0_VV⼄uޝխѝ;wbќ>v^{5Ζ_}!!!k7ߴӓKXYY%֫Hhn\dߖ>5` [ ;B}눅 2NF^MZkjjr~gb 6Sŋ111zp#++&++MRdְj*uL֡e ///^j*Nmv"Խ-+VЉ=Xr%G+(99wѹAXj>sΝ;HNNMd Y3f02 t@W^ř3g EEEI~z|'OӧOjg o߮Nػw/N:řD6m?'1IIIZ퀆3OVD}AhǎZ0 .].& 555 Bvv6]͛f***۞@l\8p:=z4~$ڶm&L`$:x c6*KjO9r$/J$!11Ǐ7H:t1KBM*n %pENLLLc._ \޶z뭷Z-Cآ #FLhX;w3() s6o@x7Z-SCp QF6 ct%Ԥ0%C(++9+b1݋@!ХKZS@}al+Wtc_S9D1 f^tɉ@...{tsJJJ9f9___'н{i&H^{Ղ<7oLbСzOmA#W[nq&Qjj*>ztLă Bݻe5888G [[V j3(../9ÇY9`G& ~~~{.rRǎ@l d+֭[KRv$0Hex⯝:uj.䊛7o|H{!wwf{6̙@^< I]}xsN/_Yf dccC @DDΝ(>}:EEEѹs爈Z[K .TD;wTL| ͚5ӧDDHvvv/=3 36!PBBeee16,,,^U8p-[F6m\""*))I&daaA'OVսn: 333@fffA֭S>D"QVWWŞ}:֮] P`P*tnNڢE8{UrJ\p1ߨQT& Æ kYeHHz聧Of"m@. N% <<< HPUUs 8"_ƍ9?x{+ + s?Ѿ" S]]ZT*)55I(Rnhڴi*˞={Ek2 %''ѵkٹ|t5b5M"rrrԒ}k277U . i̙ U:t.NbPFD2e .\hԩS'WF}}=PTTLAQ“'Ob TWW#88C App:cذa/4hIv{wŔ)Sx53"8y$=+++#00Pg544`ݺuD@@\]]aeeG!??GNS2:j71}tlݺ1N8`dffLG#??_'r)BN͘1Xmf۶mâEX}߇Mwa޽ @dd$Y533CRR"##y/hllĉq눏Ƿ~fmU_|YBJJ y?~<8MNN_|Ѧk3) #%%u\x 22ߟ3{u P(X6GXX' ;;011iuDFF"''Μ3qDNn?s&e&L͛Y155ETTrrr &*NsCTTcĜyfYYY}q_[[ 333|M|׸>qN8)Ao>%@.qD ڵkLXŋ2ӧFWU8OBAW>>>ѸRZ8:[lc7b1>Ow]lٲuVLMM)&&*++Y h $ԾfI*L&BSRIhrg7BBB%MOWq5lΎBCCiMlECC-_w[$MH kxQYY*^"ְ^}U}A`ƌ:|B@@aaaTTTD{QXXXt(iffF2LuPP(hƍM fԩ%$$ JH&joz<==)##C/$)))[^m$kv孢֮]aWzG p%eرHr\gD mr}&Z0tP9ÇXipq9rNb}A YOOOOpsskӠo瑝A >0tpvv[[[٩0Y[[ H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx{P,{V`U4/aD%1E[TcfcM59rjF's$!#qH8eD)HD)?aU`YXv?=!>,wF}}y^]ܞ) }L( q4¾7C0NIXE\3w@  @LJf9p#9.((ty<1Džfd< cHTc#{0G۰x؄C@ 4ϟ477b"Y,aPTW~jjj^O/^_~Rx`u b;w.DAVSjj*I$0???YiWcڵpm?"ͨAgg'= c6Xw})))˗Y{`0 44tȱ2梥25ƍyfqX,)R)BCCP( HQ*ÎLq_ej\4|9s^L&t:4 +\.GRRyT*](H  b^^^Î ^r۷?3z}H$Ś5k/Xso;aX`6\4,Zx뭷FUߵL( qP(Dvv@WFTTGwիWۧ H544~NsxX~|9G@@RSS1k֬H8tkۡj>gc2c@* K.u(++YRHRl6# VFz܍֛;[|9rrr.9VH_>^ט@)D";{ɇP2mwc\v:|3n+Ю]k.R9^ȸ@f>ʄ}]̜9t3˳{꯿ozG`E=ϟ?oWVbd!f$&&Nȇ]UUs\SW_}!AT(u!!! q׮] /fb4EppMmڴ c܍w?o`i݇!v*cϞ=())qz>FZ,^|GaKsܖ@III5MX1}tgGG=zݻw\ٺuCv{Ji*l"W5 {?' `\زe{ ::agffĉv}O{lM GȻa[ZZ{aܑ_hbX bX aaaX bX baabX bX baabX bX aaaX bX bX aaaX bX sr's_e]7.n!%%ŕ+WP[[ç~.A>YYY8}4JJJVT̟?'OT*ƍ!XR)|I_ ǏO?^9s ##O< rrrpuAGwJRTTD2vݭߟ>c|wc*((Z""z*Z$ӧ{Gbxy)rζw^^%$$ ///JHH<9$r$(''gTT̙3DDta7oO H#G+WP?>|-ZD""##i͚5HDD_}M:UFԩSDDT__O#MDDt!ytt#Z$… ҥKtEh4DDI/IRy{{Ӗ-[`0QCCUTTPUU5 B $J)--رcEgy;FDDflzP 4τϧ!㏔A6^l | rJc֬Y_B?~ YG`4Q^^-[ 776m222с@AT`0wAyy9FՄh/+WGXX$ t:P[[SmmmQ]] "88}}}|2***pE>b ,^+V8z(:|Aз܌f NDee%\ Ʉ>>Nٟ<==!;wVeee(,,Dii'Gk^oP(H$J~c7npyÎ[O1Lth4vK ZZZ Gxx8Z[[B!a9{kލw6^^^h4 z* {իW> D9s RF|m'WNw^h|74erb7ouAD@VSjj-ޮ<+j1crmތwzzzԄ;wN]:6U?S||<PKK Y,*u^J理!R\=y c cӠ.W8 MTVc#"s(806r4 `|`=&#@ p 3 Z<@388=dž W4ЁSq; H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F sIDATx[S[Yz@Α c6Оx:q'5=0IR?K.oIeҝI{߾r9x"Oϰ,\onmhf;9 LD_EBU-(j4b|ǥ{^}p\woֲƠKNY,xnlv;u+FI&b[lD#DaR94=Smlxl2Li66bҴZ&s9rfbiTJ:8n:Qoc2 (j̣qyܘ4bvaw:Ot^tRS[sшj4rs1`۩Db׃#tvuQomZ+ f3nKjkkD{ VMETPqals=OU!$[y= Tx<ݛ4:+@u m4f5&FqO/ϓfIӤS)b[[,9/#7rV jd껉Ȥitcw8\#n0vSJkkk=^Zm_/_Hih2.&M# + L@?gΞE;BM5< <fcTZ99zp78rQMYQ 4>(K2mqLƖδX|>_ V@wC.>,l>PzrEn|yPO=hYyvBnWۂNSpZWF Շl6salap2iji.xr( 2'U m9@@'&T5b/pPu`\ꯅ5fRfZ`546aXtO@Y+@ͨbN pnN 8 -0_@G8A|D"QZ:]fi}OW W2r)!J^T2Y%B5_k/,q)2 / ZDʺP$q瞕^TdYB7_heyWRUL+?|X(P2`|Iwt*\X, CE HM9@g-azrG%}#mta<^,Pqb[[AZ?DL&:bixdi$UYq@wD<Ν_Ntg ..t9=Uh2aw8xxFw{RG! 07Q~&ɖ{аs.׮TU);;6?7GEU%wF470?7' oćMAc)72VB$}KF$&JI $쥵cdiHEQex5,:zjC/# ?v- "9W7tt*s,chyNN3N}d2|A-wO'䘟{+W7u!t{6:;~FOOv_/P<x WߚtN+W{^E*"Zpq ]ޏt~.~0랆|mm"?4M'?ed@1*zyqT2% mlw`gǞXF4.Vn@@m]k3 7E!Ud6 ޾Sv@:hVB!pzEn.y;kW_E"7z:"p$$(@ b$KRBDP$+"a:GQC( ЭO _@pa`uhk &e#@lǕH\BRF>5Wދ' <\rev)~m/;$9tiw< sgĊIENDB`lft-3.98/icons/144x144/client.png000644 000765 000024 00000010500 12062431750 016212 0ustar00vicstaff000000 000000 PNG  IHDRF pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FkIDATxkSYIƴ~ 20UWafO7~/p!,X\;ątIm1IYsGoĜ4rKI{{U椤K~I@VRR+I$|E{r4=ǍH*9Ǒ-<$EPqIHJH:N|IIIZp~ $$x@)IӒBv@^WAI?#@H/AIcjd<( 5M@ҚXa(@PABMՕeuwwYˈp(a@ 9+ :;;uheeEXb&'')a`OaŝvglV7n@ 7CCCr v֭[fM;-/+Êp=lΝ;pMY'O4119ϟdSƶ x_~5U@ ggg\Ռ{SָXZZ^ޭtZ>}o߾e_jȑ#UѣɡC Ur1C QR @ I:|xyy[`ŌR3.12@@@0J%;y >@h$L}@~J#E"6.d2R3.fc->B I%tZvO.{y}}[|྇e6@-*PGG6`mmqYWkt P 0[V.yc.cƅV!#?(azݻw@ Bb@Dj:%B lں Ԃp 6po*#Bg!іee ey dHF*ŌM 7]\.haa,..VP*Л7oZ@lz ;ӻ;!ж z$UQ'N*ʦ duu]xsFFFD[!٣5<<9>K~ۗ0zT+ `~~+|$HٳS BL&D"-s i|ww"Q&  1Й3g DCm-<+[O>˗U,df!U-U499I @@ MLLxYhS'us|9MOO{rX(JTHR1+ͭK=fÔ0@ h1T*WUK}gzQMs333Sfff<I @@@@-C4F]T~޽ŃXH@^x: @@@@6r9MMMUKR8R Ʒsx` =2BPR8@fTPjQ@)I{yI_%򧤗kٽ ))&iashAO! yI4OJw\H$w1ev=K~3NMqA)Q8 H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F IDATxM%W9ug&$&DхHv"B3"d)*((~ED! (pA!ř龷n9Un51(SCU?oJ""+ \i'@b[668׀؁v^@@i1t+Fێ#>V,><.X AX:$SWc$"n6z=#"!4`YxT½QbGyAU#_ooo>еߺۗ]yH0AdFہ5=0?v3 p>͖RAqj<R;&9_N`= P7*@lq?HAl6=$P9=F w[j s'fDYolzDCQ!Bl㾢hANLl<$sP--~@RF (sV+)6A͂~`QQv~ $PÇ0 l3jR@f]ŖQ@vP\0Y8(S3TH-Т bӧ sg[ f5ZfSJ3srQv `[Aeiԑc-()f涘SLk!," 3659g@N tP,PA䌇O;45E(@ciP`X8敓AXPTtSp P̫qskXb7 Y̝hu*ArbMbn7rcբ UکD:$"<Z}[$x ->pBͯw{n\FHIxPD IB2pvwÝ;Ds4=;k "$H"E Rbjk7Mz8ZnPH"::V9!R" "ӽog[~Pj5q't˽.iT+E992]w9SkǬ5ߺz|j5#05Zj(V^[ L[Љ!y9 9YeV9j`[лmmkiMHëJZK7H{&6;!{ Y)5iDYZP]PM[6cQ^,a `W+.wjnF.AxL˶(miIĒ] )@iZe2:^ X5?fF{P 70ޝ\L kqlpC_zf۽ʳr~65Ո&xWwʹߎ$җ4Ffvj-g\_n {q8X Ef6|Шҡ+Sh>H,Ȫg/86 !GAޢpC=ra6Vw8b4ii+ZP Y>@mPjԲ@͇sHzfO=&hjv$JKXPHh5@:WڸG6;8:u0hT##,`r Y%bƃ47cƛqtnqrƀ)(֘h6>{)w/چ\:l~ 4];8n6[QdZ)mP Hr2IKT JaP)ɏD'U9\j5^zЂAV+9P;A GsLt;p 3s%L, TgjЬT RVIW" 8 peK`M p!d RG[r-mYڼO~1Ʀ`[įt yګ*g) bZ:G-?{AN{񥗇g/0JY+_OLU0o^ۉ՗ '?!V,7ٝEKZ>56GÕTOxߏĊ898&?S/^J`:r_O\:M3p q#$"ٺgpI"q|N+7^ .kz&QBu#u)gTvoZ*@,ns;Pq&W~g8P7_m[}6_IENDB`lft-3.98/icons/72x72/router_cloaked.png000644 000765 000024 00000023346 12050507714 017613 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxܜُd%\"3r̪r/Uݍ o $$4 $\!a.`FC{a>{B8$1PjyH͇ h Lչ Tv≟%tJ*8F?xǍjsչ}4u`qX 9:q@+qgsn8_W+)ϴ9g,q`GY[.MO7~wC6^ &AVN7+RY=a4F<5& 3';woWTnsnP6_ :3 w䇘8qld JI?Wo|;j|3~o80azF֥( EQJc ?2֒%VPjS)%R  wlo0lQ(,0h78%%Qy^P E9y.~F AD<3G}~ŋ.ј_ޜAK8>M43Uj9'r䥦І4#~"^}5._ݻw?;}˴@E"az& 9L;ցU!y֫OAsJ+ٿ{r|K_" M4%C5Xcj Xcx~&zG.w.^ PmO.E%3ffQJ>UQ׸p0dQ#e^D@ #ݣ9^ana9z b8Lx` !/*4$^)$IIIk⌐pH!Q7n275ҫ@7:ĵ Vt:6<) _Q438NJGQ?YZͧ=ȉ6bV8t8j;DAgtΛRz0D8(%8nɳEhͷc~~F.k'dJFJ+KVΟ#2޿sN]Y5ZkѕH5E/<"I2V Q#DL>ȰgquNRڇ,$C2L@>9.\HYEQ Ô$I)k i1p!V)+[bGBq|tu$cmjJ2?7]iF5(fFDa pNdtʒ!O ~"IRZk$[?qrpq],|uhQaf%.Bx:Оrh5u΁.KzZG޳k%Ȋ g*pRcx饗k&9(BH1z"=eI $3"/r1@'-, vBӡj1l25=MQuI;8G(-pԔ8w2g4gM8<:8 OPVj! C捛t]JH)Mm R ,'K1Q2%iy8X][CJIKY(NqH^HV:>c/,Jhζ;bA%VI$#W̟i (XkQBk=LфC Lu9$c "P !=H#C4#J)7TPM@Ue?LL5| _`0y!i)M3k ^58]yQT4r0eO+@T|>1v>F)^v/ˆ&? jrwc^"&^n`󬮮p<;eNQ`-BTH{> ,m/МcuuOܸq?oSqS/e4}u'LM7|i|_ʽ{[ =:3g E%΍UYpO="Ͻ2{;;C@%h\%d5Mxl,g{{W_{l=6~zNy8Aڦz׫ANH?U W6ֹv:V/ow~Bq Fxγ- k 5 ZÇtmW/O=+9h&)iz_,+]&|yӚ78z1ՌgQABX_[gZss?GGAH11“oŸR~`]co0Zy}m Y\\¥u<9Cߧ7$i5,ɳ7B58~.J)pUpgEAPM`0^њaa~%V.\djz,|{= g6#qlZ.8S(Ŝ1cBXkyLOp~yeZ X,H,Ԛ,yeuxA-HqDѠYl6if[^EY^r˻˃(j%XHrtT,OU Ŏ,J)WO5cv-0;;9VA( `j5ՆGԶGKJI ~NnfgCzEQxpÐZy<;NRlrx'm+]2tdB 9G y0`kO@^gfzfIXkRo4hkQD*U@m y%Y^0 =t]>T58fּlSTIĔj5WO:0Y%a\U֮pA'L,2hӑdn~ZIQj;4`DU|4 5|d;^'ž￳Ok\~@$(9OVc0%Xh zf K|_x?=#bX{*łFEX{ܑ ^hm3}^PcoLeJÌ_ t:c0!$k4j5͵x+gy.?p@3ee4eNQ?wWya{v7bXI<(H%=r3W; zQŴz"׮_r_08hCpn]a`5Zg0I;I4/_%CzG),(}oUqu- gLQYEJ=7!vr7+?Ȝn97i,q; Ώw}"啳`D3Rl ր3H,d)ոjrkW8##CzCڐe)Z8le E18T5f W]3O k|Νϳ=PܨM7YZefFr~`C9l}C7櫥(BڍCd~&GQRըb&2AX縔$Z"@;q7g4곻yb"$&ALhqJl^PdCG9o0"ш"z-a@"@jԆR&Ky0H nt 9kj@k׈Q=/kWS~-ɩ-|@标ýe&>hWF-NJpUXT  /~cw͝lw[EJmk=8N8%$(1 w_TQTzz|/L7 C;gGs%qb Ž5ڒ%"Ö9Nk?IHe>&@YE r%20P1YYٱ`Ӗ*ֲP+ĠKm³KX=Df"[QoOάGWf'T#|y>'[Um ?:>'hS_?42g>:Y,sʙU\O”xEJ]]c:Uut0l=aK(3EB[N/rz kȄzkqBe5Y s>}rOck3o AB5:Cf J8g WcJXmn=]?OV7u@U3neU`X}̀  MIENDB`lft-3.98/icons/72x72/dest_closed.png000644 000765 000024 00000023520 12050507714 017073 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F{IDATxܜY]yk}ιシ)΢(S5ٲ$b94NڠMEEC- 4Eh$n!n4){ﵾks%/%UF$69{X뿾 kU@H X>8@Z/1` 0U| BYnQd)~pDKƁ A0 >b2(&|;EcL ?U]/keU ` P1ш&GM'W9}+vCRؚz{8g Ĩ|?'Eh9}מwčQm|xrk0w4:P?FxpGIqR˨% AQ郤P}D2C@f6ΰqBF{O"hß3Tɨ^G /Bӥ`ǹ1<ςnF|jaN䜣Ra*,{c {Oۣb#r@ }F'e!D!E28G2.^;:>aiʺM[xٿL:J{2PQ̳ h՗[V* `Ce&\@r{ #aŃqW!vJ}r&f \=}?!c|@ 6m@F {! $t*_{& &{rvk dXc"@,)W]*泔6WAQ1( BB x(AV4UHXN%lZ1)w^;3Å+s>NjgMINϷ8f>Ro *푧 B0:=_;NOFJ'7.8['pUsȺ~!8!O^ԍEA/PUy5䞻giD"! ߬bJJP9(^;޺vΝ97EE:6NXFU ȍelDkz K=k 0,6.ש/^_H6@I ^DVfaA/I_ Y#> "1ȼXH 8S˖O 4o!wy*57.S?;k pe^`o;YBʺЀ3k Y{nr*jTky\a:#T{-45:(Dj/P( 16l =赻4/%KPra١Q Ib#hd:T]8k%5ZŞc3=2/PSv}vm2cH:g,grv s^f5T@':BTU q .cǿNf_c4S=.Mk-ݑ* ]"xAܭBm!(ii64pj+DDVM0%%P LFr|Gݣyh@WE4zP(PJSl={ |AT}ymv:*q!>SXuQzqaf$e* {+|Hh:F0֒$ J'MYluh%sT+sTY)~)NS^".RlJor½~:U9htCP,!Eu A9RcbqdB% O;1!q:g:gUZ9LstZ8`^Wv<@^!z7կ >dZ{zYrRovh!aM)Y"Rz+Qdja ژA|C^ͱ"|=yQN`-z'~0^;=16?ƞCǿTbހhk?.EztqtZmLf;hw1.Q{a*ZjXQ(kءFyv-~}E{e"t,stFe=.oU.Y|F[W#h(cFasWˋTJdR(!axU!1:keHڿwy:vJ%RYc8{Ks,KȽGჟc~V_,b:đJ7)ѕD1YQ ۢ."!rKA<[~yw`?eźCƗΓm#_߾<%vu% NxNeh446^ ^VJ ~'3Chu\iUH7{>w0RO|Ƨ أ@k?#ez#fz-=-ڵV +lQ4Hl韣+v.izi)oTڠikampjl8S5J_HIVTr%^zK, ;212Oed?JE3hd +y-ӏwYʵK$Zj ]ؐa?8g׮J:6֮1T\߼W~1tV7Naor׃'4 dVpwAX]Ӌ 0KP_a8lݱ VE>)Jk|uɡ IO]L9pOs}6A(\فkGc7I>QT-U~?))F!c"h[<  yXS_[uCEj [N3^ŌCƒ9Q8&{4rWUd :RV@#F2 Z?|]^CDqELůђQ@1FɌ]:cs0ؓ>:Cb"+^bɋ6UTO-b7!7r_[Sk-._~[+͛6kJmEUK[J#]b<֜'h ~ Rǚk0cC ]_Pj*I~^l㿿Ƹ6kwK]nOƦ!֥  %>ywo.\i]tС2ǶqW/FF7cBoiL a(K1b8!x4&{ᮞeW36>'eK7ܼw#6sEC:fZ7`G9NuSȈhBN[Kٵ^R/d;Mѷl `*zvِQs唊Qݬ-M^J:cRCPմMi zLOgO$I$Iw )6x|Tj1~ ɱ%͎(fRxTs qjp$x!7|#G,XYdԱP3lvVoށ#P0>8582kA%qq2oڿEF FpH.4Hƕ*\pi;U-&wfTH>jhYod?W^ߘ<5 ^`>*|PMcz2AFa' K ;~ג? Ѫy *ٵ)Onur.&TQSeMk6;dH#svl_HHEg܁gXI5q!IجYTgʿcZ戌L6 M0ׁFgE ] Q*bIENDB`lft-3.98/icons/72x72/firewall_smart.png000644 000765 000024 00000030172 12050507714 017617 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F%IDATxܜy\Wuj꽵"ZZ,KexdАf 01 'C0c 3 &1[la"o[K[{Kݶ|x)Ts~'1p$R W2=! À3cHs^OTyɷg"RBP; JU7 l"T 1A ś Ҳh)vEyG/ J+4N"mNM17;KRh%2Rz=$!"\ף^wB%3oA:.} Eb֘jHkEH"䡟|M[ DiL%V*LMP袵E~H,@z! H e"Z;jނ4&NE[O>=ߺу(ٰq#dH$ؖ繜:>纴Iz C/AAb!RDQ^QBX'iBy`؊] lKX;~{EFr,'1!$dmCVEZ6pÐL:EږO (栆QH^u(RԽ) #"Pͯӈ\;ynj|n6ozQ fgrąmZ!(jkA FRCːt(" @ZR 2,@JQl)謋,4L8#0o'dݺ8NVRJ--x̙3Xu1=lVt:C[!Kkk>HԶ iYԪ5xYc#Y"DAm(̼.(E5Hj5Zc;b70wi|[1H*#B(Dkur-:zPQ) La[c' RAU(BEFXVRg ? " RQ#7by0TO CC+ȷcp)cff o24amR(Pc=$%|7)_RpNb%i:^e?͔ #:[/ABE8OҿbtIVff9?K\\l;eI,w=z.Zۊa8Nl>O4;?]˚q1I!)6Beuһb :e %5Z1 ҄J5¼Ai*s"{=B^7`6EJ):3g0rbn.ؼp8Kg:{XFrպKRh 'S̞>Ech-FR/A"E5*+ "M R9`d yd;3!N3d)vRDBċi|e`h8hm}D"I&f{wAJE]JgWQTRb]]hm831AuWQ8눔c"(i U N/b2$/ddL~4Ʋ.Z6& 8)l reI!/P)x䩧)L3b($ICx2Oɲ$ 1DZk$i-]H YNgbl e nL\ɓj5Rd9R'( lrtի,2e3* Gf c?o>d ha^J{ę=khm& "Ռs3S>gr NG`嚵 Zd2T*[3u֯rե^`6mm9O*"C„ &PJkLC)H" ,:M"Nyj=?F;cѹz%I D#Ns}-;;!C6B8v("Isk_KK[nNH"͐H&س{73g&)vtq v=5 E.gfj3'Gh_z +[A78׶ s$,˭Б/~3C$s-_Ķmf!Jѷ|N"q6 v -yLLr!,K"dx& *dmaz:Yv mvjZGZJ%$޵c#tҔOVDy,J8w}"jPA4绘6cɕH)܋\֋+2[bJ7ql|?:{(8B_zxRiRL.9#F]ӛ߼;P(o>>ϲ04>g ~W ?RF6-v 8cP@ 17T8R(o8Z[E.!@Cq3\.kJ&F1i>tz\qgB z(0\˲fsL'v,ad;މcGg^|䦛˿ _1zme@ؠ$[b& TbZ;ȵDgMP ). ,X zôQVq뱠IRaphSnBx5 S`s_=_ڏPA\8Q^ -1qjL,{6lT2E[ ϋ?!S:tذy3. ɶd2߷y@@iêU+ԩSd^6S)E/3 |EQ^1M~͗#eRG!VZ{tT* ʳH;V 7n<JFT#Gػo?nx=]==T*e<7g͚5MpopwfHм ܲA,UiQe"" Q -:N j%rHb;Qaw$luskwՍ@!jKJ2,JG~3gXv +WBʥJuֱf͚\tE/_d9O) P {:2a^4Q9v5΅"?+Q.!m.:{z0Bkx~@:'r}{8r(Lk{N"AZE)HP?}b׮]wI&tuu5WV/uo+n8^I79Y\EKZcTD&LQ6Hm*TKG[Hq]F !_s|= b*I$Ke P(@+.bbsy'`K*y!K_zxNq.56--b*$#Lwn2˜Vu$8Ҳr1\uVÔKe0 "=|E9z333tttyNW)En|l3<{ AB8j(5k#l|/w/{X’K,(QQ63cLNNǺuH!(+aH:%/pN8,t璋/n.d~L;w"l..|OhY:݋_ M՘T"9G~̪8y'|c{Vlڴm۶KXb~U<|žNoabF6 5Alg:2Lay~n/20$cwuW3BLLLg}YZ[[y衇رc$۶ce׮p+w>E&$eҺAT14JX $NoiΞ^z.eb|"q cޟ>W^u7mBiCZ ϒpxQ{E k5< vW:;;5ɓ'y8_=pEk:mS( 0J7'^ 5œn]TAA Z+%U:ML>M"a`vv5dL&ڵ lXwKr٥1997%\ڵkٱc_WYl366wҥK9z(K,a\uUǓ?8> r [n}7!BxMsx;>J:aYuJOQVm\>f3$S)@)70Z y qXz59t06m&ϑ꫹H&ݻkm|dŊ9rB@X|_n000=\Բ_+ĨL93imtW( *. sdGäfRNr0$ Nl6GxG&q5WShiVu:x6mP(PV;3>^q嗓J}/mmmhYv-cqӟGȅ~X94@Wk Gx93&#C6z,XHvs=TC,lb;N"hjdD2I6eI<\ .x s2sssHihooo~.[ry022C^pw}vq뭷6QJ>}Q"|;8~"E bV^ģ0k{.j m^b2\b:˪T#Jsql|BK J!^?LQBPH ۶ !DQ(Z/Jv> APዸq##1F 8h,Dcr^0 d8cc߰BK v=BJZZ LO)mgꪫ%t;vam'> m۶(\/̎mIכ9޽{)9cb 2 ü52KL'X_Fk [B ts=EDlr1m"zJ%ymxӧO?4D>!LӨ0nOO++ظq#W_}u5>* _}c $v؏byW#:1~]֠CQg&O>:Aj|D#WeaKsiV -9P(f9|]a^v]=˅^mۍmjJ.{; !7A ;^xj(=)`; F=6?z "k2wfltA֭FHI\L$98Қ1Y|x'x*||O~|;d۶m?~KKK r5`Yeoƛ|=?$< _ NjZ\A""$)JrjA@Day$cMOSTXa=/ނK%0KWjgc_N7mgm]ZG~SG5jd۟'(1cNZQH!Ulz2N<'Aᇚ@8ؖ8R,I$BP*q]L:yKȶĉL8iNutWGN}>ZT*l߾oիWϲe˚^dILAm}obo%$]=P}QȀ֤u#gZ)fzt]E.bnnL9C\&K:CE!Z IaERtsl6D6#ȡ$ls`>x>xY 'j ;;rˇCk_ܹC|{ۡC/ȤMLBG*T"ǡCӱSJ&=5o.Ig>C:_ Q{xG vfLvn-.tH.PT*>O-@9x jbȶm۸+H"~7ߺеINd5'34#3xH `РjQ $D력=Q@"5t|6G*a]`g[y,w#LGR_b3͐$W`vblQVJq7/z_̑$Ł:,YzMy߳+@vb##L̻,1oA<~а08I8pxc $&, |rzIT֎~zJyQGRmne˖111AXdŊ-oګ[_5LdP PR+dd;QBbTHR{hGocB$%(淅UC>&ˌg.THڊcn6ZQvCi ؒTBJ_a!eWR@}ƈm۶rEН<@`|X3?V4B)LQҶiӸOc[Y,akҲ9C 3?'9ҿڤ DY<6S9yZioo\&C*b۶1l)yw$N*""ML3XE|N &O8!%z+w1yqWSsST$JSX*27C0`RԎ9K5_//2vdRItcm>6&=嶧nHI?Vӂ fmhٵ*IJ7x] y11-Hpo5qW޻Bߗ 5p&IcȻVgQkS7 ZM)dk`WɁ0,G_Z k*H)a%U2^1ES7xzDU%Ti#QwJH[XIwM1Y,# S* e K\7͊@؈u$O6FpxIENDB`lft-3.98/icons/72x72/cloud.png000644 000765 000024 00000014362 12050507714 015715 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx\u?7;?b؎65 MI m(DhTJ6R6TT%-JJU4R"5H$i~0‰ ᇍٵ^̛=7?ֻ`PzOf}9{}FU-P@]wZL

2ħx_,a3I5 .'ff,m`ts5I@ZJŮ㵇rݮ#FU.aĉ/k0hg0m'Fxpr_~o}㛻ޤx|_3f 4AR M:!  U]5sGG? t'g޻Z ^f?^Zb1(UrR< R_e>Kb6=ǘ% )Z-Zag{RÂ$Auz>D!FdNb%˽. iAj)"%а A<dڑVf>qi4y PrbM`c(rPaAҌZaË4(ja LJ@"jQtrɲZaÇ&-BZH!bzg 64 O$M,d9O-~jD9wd>PtއfApGB6oH<7H :y(*:8r R2/!a^ ^ǀs Ǚ0&5 ҍA|+̂" ^<B$.ffGe(_9YwrO05]ftdJt x/K NŌ180~p{ضu=\wnL{9ņgGwI}C Vᕻ\+&^(+^hpJ%n`]饯q]\".xW<V 2Hx8H A *>TQymyBNJ%L%=zO~nW]w~u3rQ ‚4FlJҬ^ 篞ØX((kp`a1=1|i~JcN47-\,"AW 1yΉH0TZ%$f`h֛U׼wkVW^Z`. ŬsZ&>3ɺsغi keX1>5î)^ܷ?xCC#ڛ{f~SXE MY!Q"YlȭA#q<,D5_ oF=ٱs7\56ǚf)=]IW@jSH?DQ#Q}VEL&ZqhӼ|#M/sE–ٲqkVR.qhzR\Y)?2Q,P9 5ku'U,gz>eRAc .X|/<ǶCu12|/<״02f'_:;@e`>,`Lƍ4X17~(s:G窀]T^AIJ.arbMÖ; 8gƍo=9`FV2׍=^|hT= Q.[{yUgf|u??ȞE֍7]*ƨm I6QTUSg8ɹpc#tE92?t!u~6300|Fj?+o[?giAf-%j61қW_ۮMjhrDZϘ<6k?;;kh꭛=Sĩ뀪I<(f@|V~ׁYZ8(lMxw_T*gw^? {zZ5+ܷKrsKOFFܟ4=+֏*,i ԮX6] $"XgXO;n{Eഏwn[֟\5#Xf-v2@E3QP% n~9]k+w\jYH@GhK$/5_n^v:}DU0(Tb- ET]XdռuP,d[ɉmE$D=(M3VRxijPlyY&MRйmҭq QTYd],܅Lǃ3[{!0F{mIQA1-}ԁ)11.v QL~c{{DGtwvRM$J(K6)Yx$qq3HRWJQE $xʃfO=|gpF DKUZ@G7B8K{w}'+µ ]u)Ø,8^GOQ^-'8KѤPKCҥzG+Q-6D)VYUS&xy~$֌r9$ΝQ1Vj)GO0"8l)’.2ڨE`fbF&&  !pp|ς΁Е`KY3 8hh@q<99R3zS=d!2ťxC 9NbH !&L>;Z.ܢA|1C>he%nAuv +/ƍմ[꣋Iq\$@A$HB@XOh +(AQP6'Tu*@ZcΈ`Y`(6goW΍Ѽ ,D?BIENDB`lft-3.98/icons/72x72/firewall_stupid.png000644 000765 000024 00000031444 12050507714 020004 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F(OIDATxܜyg}?o]}w#ͭúFd˒`8 ,,}p̵88aYIX:6H %uH#͡g{?g$2Nv~J]jj}Z Ā;kBZۼgX۰j$ +dm_w7@a 5X}a^(zK @q`6RYK'-C"/_껾rBk4ܫ⇷y,o0ݍ#RAC6Jr{?'FZD۞7t}ouq3PBk0P^7P0!A(9FkM2&H*&HHň`8@ _0̋Rc#4 n—?c̖U;|ۋO̲JZI#XJ0(Zꘙ*?#~à5R TT&K]L6K,de[f*!qiZY,Whn`/Csѭ!RAJ Zc6MT?aIVt$(Ny.B82Tǿ.[zB*q)Bp+.J!r L1f/Uu BPIByfM4 Cɣ?}gkFmW$Dq,i޴\Pkf E*# i4a!L~``)^ R+U$@R)yH)I>Hcp>1 D$X*139@2 q[,Eg!@򪸮~ %C]ldA ΉJ1 vN?}e߼X,R 8iy.\\]= MMRRj[󪿆H[|aR %& hŖa;fs|!Vӎ1yvF'Ծm1LΞ^l< *^ET =1 P%:PJ*! BB)? 6,V<գLG{k [/ЯF?j g`j+.rh!240͐T*uHz0}\0MB)ADi9R \rtX"2zzO0qd^ .sӘǎaLII">.j!0MʋeƚX CI! K^x@ϗĬ_J°FVmʷ0?9b.TYC<& 0m9;rJB:0882~reKߎ+H ?AiHyJQ %AX0^rPjR F0$!RJejxB%5~H:w2#w8Rk\Rr%%T%%G9C6eWH$V=ʕEl #n1Hױ EPkUA4RE,|NfQ?=KRЂHr9.0=5MټT6纄Ae$ ''>Et 2G#؎Ύ16v+MBH d"A{>K*cV71QW"yA<QR!%5-dœ' Ŝ-i0L*r t:ARa~fò[H.Hӄ2{U!X,A<ǭٿƍhllRT|۶,lY^HI$qt|0%B`Xk"VǴp2 gY8(b-I02 сB81 9V445UZR8B'918H*k!W_[iL%qbdnz|c#/l6ˁg@kada)VomAjV"[F825E]KV50QbQ=^bZE', a4s3,ivs*ض2LON180i`e)բS"IJL:M^w rGZL&L{37;6*~HH.W22 ,pLE"QFԒWh곎ĕg(FzUm,-o(*۶㌝9ÉCà+> deNc%&8sЋ׽qҿh! |K.b9R 4JbИB`[ ӲPJW}'0bVW9dQ>8\yvp]R! plG95tlj;bttf0?v9VM(%R !XD2S4[v\K5@a"0XnkyA@Sq]|0Zr+.ABr5 ;0$\N!X*n A60=/S,YU}($Hb[B]=K:WW^A,`Db׭XXddd޾y.mNI: K>hjPMWT2`ptc L=u/*=4ea6dFf<;N.ڍ혖N$_}NP,Vh7?Y#!H!cYd,r-r.vl^i%ZGeJ)%BL"S|މ!`fr!G֭ttu.`KL&9zƀA*7 q8|`?~Ͽg;mg?Y>pQyظq#ַG?C&&y׻ 3Bʱh84Ҋbi2-u’+zŅK龆'e 8mFrL#e3MxbYC'Np]̍>O^q.X?&vQ0@ ېh&RNn1@{* yr˴p c2u 44 P..btr{2==úuj*~@Px[ns]wZ8!wqeWͥYXPlU-z@x+:bs-C U+I!RI!)kh$Oe$I2 Oqb|իI8&P(`&l=2::vfJ_XbŲZ.l;L@\FJI6Ͽ5!8&a`(_X^gü2Q!:]+ͳ$퍑TabLyN8aV^굫<~bH$) 'O qI$WNojd2sA쫀y%yP֚RDCn~u=+[ JBF&$BGG\!^6C%A yCm˱{i!AT0Di8SSS4~ !(KA@""rrh3g'5UJOR?b׬VenZq&{p3VVB&Ϸ YIv9(s0r$C'=4[A% f5װys?_eedi*2/#K7ˤ)-.bTdzz nB,qH)d -^> @ʷ>  %JKRO\_־$SiԲ qaKϪ5XCXBJE:SÜ8>v\s T=4M[8;>Wm۶-t$t/j@6RH$hmm0;McG/>0@WoDI.P5Ҳ6,RR3 bZxI8b1#̠dWmD"ș9J27{3u+Jd2i _xCȵ^Ν;ڵk|U\qimm]0I5k/a^Q C苴z)QXklTO*R> :* Dh-O.9| ^޾^|?X\]7Ё;>D2a[{/___2,T*EXE˽,D"AKK d2*i,2 .婿 jQ2 j PDk kr[-\Vmm *~kA 5JIiDD0:{DZɆMf~~Xd28 evfGa5ޞUW=(b1J{a֭Q*.p0 i[Goޮa6WZQ~maHH 9j+eXiV(eqe٤3i\ץR%Jii_TE6QͰX*/_dzz.aaHPR-159^> ===7x룽}ٚtm +Jttc%_\z)Iaun(a g<0, +Mj0=tMo9H:%yBȶ+qInV&''馛΍l߾n.6l@?B{1p r-|.טgl v%jQ -u3 :" W_j$fTOQvJ7H]L7`;Z@a8tN`P5kW߿-[ sq\ g'k<y!\暮.$GĎ;xg|An6Zsq!bIi-ƋrlZ#jm^K"X`xqڰ)/DfZl\RGAlظЫ`X6op1~1n|+zJZÛ|{|\))u]3 7<_|gӦM=z;3Gv>Z|BkY_^h%!r tn6`T*2 ǩO6ȁXZŮ];ꪫرJ*6nȾ{8v8=]R)͗cqI>O~TUbX u!Ninnfzz֮]׿uR\Ody0~.F`a?X_6%$.bAK<7Ӗ/*AyH(B`D sC/F}>ORY.TV}h4RmxV T*mQR~UXE99ؙaΎ016qF0|b>R4b$IR'N377yZ/}~N8G?1::V.k&n&z!OO?M2 211.֭[R|VN$c03FnM814* 2.t1yԀ2Yz7lufMMd$S)Z122BUkRR<,&19>/[o{җD}}='O":>ѿZ>O4@8X^2Kɩ("⹄a,!J'D4778VTD2~v=+xoR4//Ło>IX~7m$ wݻ 6pA:::뮻(lٲnx |IVvt||]I!taE椅8C2P%#KjH*A(ÐjeT"m˴X'HqBP(q]d"uVrC燏)7z7W_y%j:Vzŵ!gX%f~^|Eٹs'7p+WdhhիW#"(^P.tF5a(m<=MmŰ1Yŭ0,i"d642 ˘Rlm|ftfAѺ㓓\ȏ_\lgfL];:'*Cg?iV\;|'V?\3Rb둎|P!B,BSXt ΅ NFe0,3c}m\SM$LD:MyU @iE[S#;.+y`8/ME#) S0VKTw|ns{YǩH3H0Z58Oqڃ55pohBkMX$r7s7#pwͯ"eXP!Bh {(P>B@>h 1 N~Ca0K$*TcђMbx䑓,@"(O@KZ4 4~0UOAC$bq iilN&Ô>J+ : z NҲp ^{4& !f$T],T4c 4XC^b¦:lt#Ghlk疏|ǎ(鑳Pq!߇dXs V 5a,CU[ %Μ9# ::enEb3A Bbuu|m4"pY*%>8 |s/ߵ'&'i/BOU1T  Nmy g<9iQZkد2V!FP%ȶ=dۿ.d 1;d\0cg@D-? QTĴUiYiPOf aJZE|mc(c}*+TѲb  P%ӆ%aDy ]B (Ԭ &  jZI1Ih^ʵO#C忚oIENDB`lft-3.98/icons/72x72/router.png000644 000765 000024 00000024025 12050507714 016124 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F@IDATxyg}?o}3,itdɇdCC,00 !dCvdf%aC6'Y6dIHwKd{tKFf껻ciDvYk穧[w%1>c8im@ښlm8-pR@ȷ5u.|1$[h G8xviz[J3Rj X<"!hQ&!u,!7?CczCԷc,-`5H\?+ϾSG%uc8-qf} om uƨ3'qFGxwݚSI-1;jbK 1t,*yFRa{~{x0~PiFo,7C|Ai6AЁ"AHt Z34D> ᴿȕ( Rk6"RHiuTjAl;N˔GAIRcZl,DR!0IJmR 0t !%\Tzr.ZRH" 0DFZF(%[%%l,\|\Dz\ƶ1KGeYR 8vʴY@fIRH5X˲b QPK4%j 3fj6Qa-#в#)Xrx /L&G"#!NNK"4FZHF0ضDE~&"BiJE$U. , vFҨ-P]T8M0I8KXCo R=$K2Oؖy8CHlh4&AP%r8sB#ɞ!=W\I*G"²Pբ@$LiHbl!$T"jRcYX4zDp)OM,"ieM"Co_?L|qq۶,AIaH-ѱ$ C0$s%ff ,,Ld̒!;գd<ZBEf[ed&H7(:*-,mH:71OV85fؼ~5֏K z. 0-C۔Ras%xf~9-︉>0,\1Jk52O fc!f{H@ﺭW#HaTV꜀THQ2 y#)FjCJ)XCTbAf#=Msؾv-[]M"Lv&!z)cZw&q?qMu~S[G&t/|m8~"lkpWR.9{ Ǐ䉣C[W_58(Bu+z wF6 @j^9O8sώWkB:FJI^gff(:`'f6m/mc;Nݻ}== lL6l^ c:HZ>gppm۶3==qqNIa4Rq4 q ,A8Ozo{;پ}3slKte mYOZ'Ϝo;{IӷOVCFF6h$ BAƐM&  x 70qΊ1 %jsF &Iقc[Vv=`$a8.cH&ɤ{(߇1 wqT( kxH&S :."e>_>Wk|pCObFѺVZJVX*Q# o[i4lܸk; 󪇠D/A$J\V p}q:dV\*S*Ԋ ,LO<d)v?8Q/?sd2s-IƞDZm1`>^fNbr^:|b E` {>.mћNKI$4M1S&lb(3JLT3QD>[oŲ8oSVCk5߇뺤inqWI*8R$PĶmҾkYl\f9~e˖-Y\%͒d3Si|}\ömmFRVﺘ_ 6H%Haqq XR ڦ׶4E&!L2_.Sh4lܸ]vquב癛#,y v=̜=/}djjVϱ,>/>;w3>>NEbuT*|z{k{z5T"ؖ uNtIq\d2}8y$W_}5BaKOb ɩ) ir,ׯg}sJqbnjt2t 1$)noΣ>ʷ-&''cxdjnچ!`=?ԫ2zlI-Iq\׿e_Noo/jGzJkR]w_0anUByV<֮[G.9 R9<ЇK7IXT5%.t?|6qU*rH)㏱fZ>O hNjV^nf1 e'+۶I%S)JF\t:M*D}s=JB`ǎ\zN^_;{纤R)^EFh_ZQTY~+1G$|׍}8HEAtkGr]J%^8rg2S*S( ؅F$}^T*b 1{7sg23?oo-?|ӟ&RV2VR1P*N-̗;Bo~/~ >KĎ #XBZn\d2IpÏG_C>,o~>Goڶ% JAW:ۊ-V*ro }o~;z#laH!%bMs.~3 pvjGr9j=ϱfе*܎|GÁ'W}+4s[̯*c[OROq{K]ta-qp]v5s;ޱL"hw |B2o;?= =~'Y~=/W]_ƥ72<0@diY&m JJ.>|w<a @;"oIɒPmLLKٻw8S`xnnv.c[~1S1@ԳXn'yݻyGxgxޯ;4ͬ[?mcƍz l}?ek׮$e.%g&935 ჼ|8<{a|%}{Og)r==]ҙ |\<\͞CGiA'7R"bbHTgxAJ0`+6rŕWpWsUlٲ5vV؎:n U5kBe'p5p5 |$>=Aq}{aDI,v\uVIvV%%FI$ ͣeVF7lK/eʕKJKe 7GkWǏ366jR,Nb~nr,brL Nqzdr\~r<^~Ƕ[Ga}uU"6>ɐdXjW\qŒڵ{ۅvj 0F!.TA6q6GԣOv{XEq+ 6]/hǢP(p8?rJQG!V'! VѠ~"IhуhÓ?x 5=,7_-͛ ~|~{/jˆMcVɐF~ -~~0_Ʈ)N4a>0evpkƋ/?誕k`4S/-w~ o njb;Qs{8;LWO?yq˻ͪ0q{y{(LuwQ:L8V).s xJ\Ζ /s~糿ν7m\\};Y ?ܷݻxcGqoeqާ0=ql "Rt79K|+1 Y/r$LPﻄ훆zd 'yw/կrٕWc玝[@..)Nq ~Y~ x oF*|*s0ctܷ,6Y/s^e>gSo[uJ3=;6O>+غ};6md lݺr =?"ŦT+* g2~'O#;|,ѬZ1̛nHdMp3ٱ$hեbbk@+0 V4ϗ|N4K g)fxGIf 084\t*E:\.K&!Lc˲:@׉H%j+UJRzBS H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxܜ[,YksLL01!' J (>"/(%Qk4"`LLb4Hd$C™L8sn{Z>֪>یMQսky.S'|YYH,mYPp鲭@Hсo'R\!G'pW5{K4nیCab|wg838SE~M38 f*1PHj. >}?}xw섽]ufj tI{/w8\F@ r ) !x~~???s*_>lڎiU!jMr#S0`wߙ'/;U.0k:! IU4O )*͢aXp8C{*{Gp #3y]t>a쨼Kieb9g2r\ڟ1Lp.GW۶Eɸ]s=ٺv/ZR*ڶ]fMQQb\nμ_R;ŔeXv*DDSd0)3'yc䏹03TQ`PbuyVrn5Huyڻ]Ojf39jZ~(?% H1E"+kU1ҥ@4Dst]`63ï#4ыyªIY㼿~XBTUrd_M3k[,˗/Éװ VS%Ąꢎ21%BEԑTN("+3F1snm5وjPoZu]\rl@aw Iew+b^Wj)]RHqV:S%4 \Dl׋m` s65UUBXc$8=h([1% 9!$$U*ALIIjSAR\31ksGxuBXcbc HBbǔ0H4ƢkpT5%JgB1(Yb9l9ec8L&hۖ}RJDzfIuTW.b$P/dJy/934% ѳk7fvG6"|t!Daxm_b)#mUAbu'U SHS$apSl*m[5G-l% 4QT .!U6dD꺵u86D>ʤZ3L5=`7Gc 5_9<^A,G&QQD@ 0H1QRb|8ljqxMpxxHJ麀ټh,뫔`V4k:X Ē*&.zxlRfb1s8SuUТU0p[E+b_n[ ^}lz4Ml6neV !bC3Lhfb:+IaNs #is$,w#B= <6Zs9N _WEhRd0o7ExrjosGMHf]\+iFլ =m4kKI-iM,88if͵wRģeR,kZP%x_XE0_F9;9ũ \ތOP=kT(8VakUel9SRgKCk ̐Rlcb?-9$.]@d2fzq\dܳךr96^ziK/q~u9(U!"ږӳXQ,'v-Ͽ@&UUQ5nw۶zKW4 RSJ9A"˶i$/?̭UBW&GSJ| 9hdB]hyNٌr9ݮqigGڔR2;Ns"|>ͮiFqP^MIW~|jvtJ]\xmzUx=oC ƄŎN ϥu{RVAUVH ӑie|bu]uN7ys4P%m[n_e"]KviDp,)AncodbDsYy,%;J#t8aXLӫmZfT"ҪfKUHc+V-uk$f'J<206M֤t]7\k͓nD*X`+cUD* 龚`h13A,u:5|(NspǦOI ;ԁ BJ-Z~kg%I-,✱lWՆ"bXsTVsm|[_ k`Rp٤vvd5" 9Uby24 MܒzMM wbq,J[qhsvwwQUf1ƭZr-c{MBY.f8]'wgm^LPd,5ANt=1QlQl)\~<Ê -tm"}eTzMy䭏<> ?h/CG6}l[!E4s)ytub^zD#)}ë?D8|wַK/I]S1Ӝn?u^BG4`&)HbR⡇U^Wƽ6Vf;0`͛zI,䍔OA2ҤZ&S'O3!%T|CB-[U#:DW6)ypݥglr+^*SW$lZ -P TՂ$&k/KVin@x:lom%Vj+,awm˼isKmw^zMkfd^[#i-_Uט&fєď$ٵ/XwULCJobb0%YҚ3VO'7Ņy#LBxK[عQOv*~}Da Π>`R1tί#+mK~?<{c >s|O;Skep&#̬El^4#N:K]'xϻN̹xy{{O~ P,鈇qK1#*/ [hu>է:(u΄{ӌݻtv[gl87n֌Yt 2j/uFǪCgNמ/<SܲTR61-@8+3~o}.s )?s,O~ctIOI;M*7Jq~ݼwz`7`\w{ξ,O>s}ثޚGC zPM5weyCSOCqi9O]2`v}6%ֶ\rgΝ_ pN  <^iWTvp*y-o&;|ߞ#NO F\:?d~쮃ՙ3pJE `F ᤀ$$ 싹c_ZVS+v/[53qqg.A2=jMgؽ~;u?خ.|99T1Lxm/:tX8^Dž=HW/:n{JbqI H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F)IDATxܼytuyU}#  K,lQ&dIvCpj8EcS_ԋ{ CVLY.,I#J$iYH\D2>N2Onx7FMMlo.KM0HIJ^ηj'#D|-H$O(A*qg^od婯vg෦_]J8U t2eM1-61N9=L>sklo>P |,bEc(àe#ɔϩX#s/XH uE4n%]B&q)\( ydr:L3+wlJقZJ2@ZVC,W ٵmocA`ph@4FZjlW]M}z-sXpzD̉nJ$16g38 ^r-W_G..QJ}}8 $u] "|Cl4BgAqJKWJ0bFx}$RSt/bAlx9Aum ZDp"t a`|etJǩ޷9ZҼˮ |5dmIP™LqCqpS |6OLsqK_So&M‡G}U$ճby<6UvѦP1 Wi: JEC㸸R"Us%0 =o?Bsju5Do҂e2ychש}8H [(03By|خF \.Y׋G·Rx߫/ #L'[\sضM1LWJ4@R/Xv&vfx< sjSD_yswѸv5W1@(aN&WSt.iEUL%SNz<@hELIk&h"ܯ؋N~?^}%>-юyMk\WR]Je:H%[HS%>Ŗ׭c mX  0¯"Ѵ_sUM@(eYm&ɒNRF˜LbhEM&''q}?.hO}3vuM<>L)Q*9y/3ƗN2Lߕ.Zsbqi %vMlW⸲"ҧBxbEϳ4h[w3{>Vps,6=]I."'׍{=BGQk"h/ ^=( Ld}3 a_Jh$ϴBJ%#dc5#ZB31N?bþJ",T~g)9.+3-g%](2iM[^Àֵk1<sDJM~$I&9J*9BzO1{Y$-T߬Y؄'ɐzi}'Je( y̩HU-_Jix-X >JQ Ȯ*-=0"D+>qʧi(V2Hk*)+n^#}Lˤ #% %Z/'?&;:P~!fDt]p^K , WkK~dka #\B3S,N2gV&vngx@<ÄmƮþ.j9/pĽ 1 l+qQ K8Ae$]W*#̄P=Itihi`u'Ok ׯ'|e855lb6cR 114LeӶk1qww?sx˾EƄ2y$Ҷ1Ld,5ɫondـ(0 χ]*tDnl +"x̞5 0 !>G_K/gػkO1V^͒(8 Ǵ \ԸJ*ϜXg=HlHn99NÒenwޠ3e_e8mݿb0Woщ r1Yb2 rAZ Q .Bgm{j ewoXҗYj5_{6pvZ=߳o[,H]خX3sWi*6[k쾃: *N\M^G,'95#Gu/ȭI$'p{ 0LL8ƛT85wdR&C(`YY'@<o䪫BpyW\.Co˲x<3`0H(M& ϛGHc⡇HKL޺Yq }^\_Ij5L.;(+K8isuY{[=r^Ncu˗s%RY<z{)lxs:b|` on$$΀xzI>xģPX,0<¡SN"Ј\+;q2ƞxKkaP,aM|L&Tԝ8/6ESS`Y}e ϩqyIm|u]G$c,$70`]8p>! kSgt|!00-+xl>o;LdDA>͸%1Id-[ƛo>L,N{﹗U+6)ք.rL{F\&ݴ0N!r,!09V:ͦ$t \К--x%9{gkK/!ϣ>mbBퟠmaddNK_bNk+Bǃ-o"DfQ,epu|>S'3ƒ&5="TȾw4yQߏRU+VpWBP8m `v,RcG?p _~[J%/qIg2y*d>OI*>imqQ[6on=Gv(:=c=>^PM lEa8e~xT4bd0, <5Ws'qLȆ_H3V4Clc`L$n:f54qf^zYT9(N040@ɑ 4gΞͫ\O4g#`RN'>G/͢"71[.1jGb`^y9M/"+.TkX}r9NcYt3#ax, gx >4J]ӽ~#~!(]<A p˅2Z/DAhFH6HeG`v.D" q-u۲#S,yfMKS M_|1vYsݻ}+3^.~`gd.O;dtlekjp* )׃RiC!dElEh.w2 p<IL7֪ \iyЀ32Lԩs.1017xcb6vm'Re~g'sژ5k6] ;& Oo# pv\L6Ojr==8Gf|x֪r u]]tΛǂ"QX}Ѱd a4;ws0P荷 Ԉy* e9d*0BqK(?Ʈ.l׹['n'NwnvŁ8q=vJk  CI&%Rt:M.lR CKjqZpkXu%,]{9ÊsǰX_DkH$̉yme|)W4hJD&&Fo&z_.>w. :::\.G__C룧)$DLfW0MLonxUʹwtܹs9o?{o-s1ߏ=>U9 Xx1+wp(r9@~?~rl}%scW֝Z Q,Ԭy )].*J 2. VC:Bqkg#L&K[osAܴKp)<# G+?Yݧ۹zxU#cZڕ:속 % 5-Ut8c7o̽~GxyV̟ϒ{9=>$Çh ds+.h-Ϣqf$tY-0X?x#Uy௾{v.Z6l6mo`mY?i O}{4- 锳3V5嶸JkV ö1^4wd4=>~ClÿlWodGeB47~,㎙ג{| _< h)jO˓TY;Ĵpy!C0QCIpǧ>/|T%>noya/~x=w˯Z,k_crV>znOHlxu>?WǎY䖮f{$Jar̘,=?48{>=ȝ+VpۯePïS_gٱi#Xa4U+VyG&+Yɕ7V8ܛ*UVĞU,<8$k-c};m[xҏ\q|?/qb5ix85mmG`J˯_|svb͍<9t`Պ;zUyfHҚDZj J.癬].+yAe/J9d0 dΎ' iMt.o4o=~ <ł+jfoʉ"?yxs[R L ;6n%U덜PHsn6=4#%k2y5{[ߦ)c^M뒕3:m@y%:@:*69u%KOx))2[؉4 =[n,s[dޜetN11>́`d#{|w2"77QW]C0^-(Wٶ?/L+J3::SǏcX~ .XTi߽DZژؘsY9׬fߒy7 SYcمM:OAtSц`s%pa&)/8jcvK-<ܪMw:-8eR4F.'^*r#xg89HOa m #AGn/'12Atn rǻ3;sh;ob`~\F8Lh;N?ک+MO}rDI.夿e`&BhQY]Lc&<%]87G v"Q^ TZeZ~) ҭ{HN E+X_@ױ]O-9Ⱦ6z"h:ɠ<),@+Nҟ@%P̕beZI3e0@kKW1|GxbJinHO!<Eip>鲪;ZbhCJAF uKSQ"%rv6Fj̚J’57i;L0ӣN̫cg|n(0ep‚uQ=q}&V >`e*&Sl,N_emiၓK`h}*#Lk ijd8o%K(W]@8E, -NٕsYDV_Ulz=C嵳u~ Y~9V3oJnl(rNU7&V,H8CIƓtRLRͨ7Ȅ/D DU!vvJy!^R]PU ]|N s  is^YO[hNB1%=r*&˰!fouEWձ٩QF-`y;#F"v1  ki;gNCMvsrn:0E}Ɉ6g[eB ]) 04~|"C};|K̤ˤV5ZP@&H1Y,~?qj갫j9=0<2!x- CTmNʙT\v(o3lSGESBY7lseeT!M:?žqCUM*g )X"l՗PP`) Xh/$|8XoM xV-)CJ+ ej)45neL(CUFWo!*"nQn 5U$N1>bbfy9xřKGh8k;H)ˍWa&oc,!ߚvVXNuyF6؅,>f^K] nm#3Xo BL!Ta B٣́֝}gm ѦhxZjĿÖ/ay2 uds9rp1Yy-=q*Y -Uk+'S@^0"A"e: Xץ)oa)qA+ez#^9rpJ8Ѧ'htp5͎VD f_\7b cf6^Gk, -ZQN'P@QZtT Ӈ0UJ5Uh]:ۼYU\c05Ŵa m&w L\LU3d r#*4M*ҽ<B3-hIENDB`lft-3.98/icons/72x72/dest_prohibited.png000644 000765 000024 00000027476 12050507714 017771 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F$iIDATxܜguvM A A  2)R Ily%咽kڭr֖?زlɒ,Y4%RLbI`@$`̛7/va?t  A5n{s[Xk%Y @ca*p @I? t,I@*JT9G/]6^k{ЃXOYX0 +SB,Ok0Wo'R(qkD EEv^)`l")kLR8Q,qddbMhNYQ,? d2~cQ>&rw`R rTE8F>"}'R R =R1L^w|vz,BGO! B|?@I+ ixĺA& v)x蚔#%n&˴8y'l5JFJ0$  "VQXl\!xt,t"2 W`-?AuQ RJ "8Vhc0BҘ=OZ)LbH2Y#,i/ a0DU 캎ѻEnƜh:(f9@d "F.롋E2 #S^?|4Z-R]A6C GrNG`c |:ZEJE~!5 HKb" ƀ;ž'zRXL"NșSYQEvro7݄z•+8*Ό:$Nw_aujtjb-d{z359N:Ey} EŇ?yo@Lb l5;>߃D":1hUE&*x%'222#˵ӇE`5Wio̩'N m`"tUH!08qR!+x~HDKbq=008jkǙڿ61y'~MkX8 Ӽ1qL! &=Wヘ;nk| ذ&7_ġ2,d9{|H}Qarx<&u[6@~ tD2^̪DZ2RD ""YX)M-8Õf]_sv:Foo?m'Ǧ\.귿ɑIC}n+@H"145Eapxobe&1X%nU1~r$a=T茝$,qp_1G{hq]LP)bѱ&54hk. k 2: E:亓{{x=d+>0}(yӷU('LDOw͟]zh)$NMVeR[Ѵifg^V}ϑc,C/?f~71L挔"V * ED2hǬ>[p~rdɡ`r#Xu5Z֮B XmQBrL}FkRHB^x!|&VX,vxe;F)y!V< +}&XTV'8L)Dd!elA ,\socx. 5?z;c_ xCXTzr~G4֭#h?DY41q$8:yX[d'N?ב{P$gYط8~BcILir"H@Ai,qG/?V/k>ܧC#0s) R 3U&^zλec<ÙB'Y@!C^G҆(s`ݟ&n R,i*A&C\,Sz :J'5NS MZm hBb'&X73G1 _jCh{g UDcWJj62Sgp\ *캛 o &Y_fi.Z'cP E&f-(cZ Dwڬf%ֆ#YvG9~t:g|`~ Nˢ/>Ah,~fݦ_gz &f,j*;-:~W.*ϒ?,AHVJ֎3QJw`_db6I/"iF!i6DN@Fr=gW?N6MQ 3m _H7̆W~cMb8{Xcq08R$dqhkOrZ/ᶻOӏy\Q6KDg' lʃ%.H\ĔIkv1$g]Qpm,rZ>_CG3p]A68Z(xY[#V[vq\eF{^\Kf42dNrIWo&R(mq @Ѧ`>`ǎb,%cGS&j73bg" h-C}?8[#(dK?D;|b&)!ޓ^X2}ؕWӜEKyFc 6zYeGS{je7<@KĴ HI;):@ 2Ρޕ?x `^IAWqTDH~0ԝD} BQ>_D}LL_B*{b CklV 3y*'p憵 T1O!l( @g#eP.i]@m`VcBЇɬ\EoaEyr?^xU9#h }nWtA {|uY}#[tR Cp;mkmc&[>Q< aH ťdmfvdEy/Seop2ĮVQJ{Zy,.3Hӫ6J8ZQ~)QX*nƷuޠY%n[H֘wP \76))&=vpRL:6XSN^K)\N)0*TߜI]{F1]ИM[@?0he]DžH"Tb!֖X'Ihkb",Q&\YE5RJNHuA.&6vcƹ A*m ֊v#"R΂V]PR)$ NlB~w{_@h"Y)DK]wS-eZ@྽`OQ-TB,ADGj-]zH)͡RV"aD@hg X)QOlEW4ʒkcgxc2O堉ΟJ?M#At\*ўK2^2g;!ѾXC z+@gOl'o9zw5i3_aī*i3g֒{w]i0ЪgJf ԿT.FZ\XE;&1P?I6Mq;[+ F/ k 1&" S<;ZgC5m}ˬ2_fG7݆%G$4gXE lxV\Zq Ll#|F3Fc^ikyOl"@'3<<HEܖg%!͢/?7)2]3nB4\N@ iU8;f^b`ձpu@,$$jIN߱fap"[?Ƴ7`*3{8|= ^4,ZhMf48X(HQ^BK16+66Va TDl`p0ofIm{؋GwZ(qB>:wa;J֫=G_G6۴ٴ[n.d!@P8{ yS^1ʹL֚TދEB/>G7q͒]D{l;'l Va!#k0 "?J+3D:imlulu*`Ǜ'' F~3"A$ Nc4O]0txd]X5 }:>K/jޠqFħߢC|Q?GdEB/r fvoc5+f2A SoмTj 8opKvOF!;&G{y9֎!ma_I4iFa.0=/u&4pQgr=gﻝlN ȵ‘C ~FnlQgqw3(1̌FR5fƸO0nilw\[NM3g֝<{[bPJoI6`oӯdHUC6CsЙI;y$hҌ\{J\A%aVxqĹWy_d=o=cGWsya/% :\n%ٌDi3jpeAT3!:05jb 9O`Up4@k|u}߼e)) 0<9oܣj bUpU>t vs8azLRZԒL`i.Rblgw݈LU ^Dpb?i⡍PWD7Z|eZnH*ϯ-׳ _zkSoc;\!shPN;VK=wOaeKVTh;: ^~;f͵<9Tȸ0tu[%ݠo|EG7DW|p5) ~P>I :=u}pXQA"Ug x?CxL :B[>COFӘ܏r^5-M4K6^Q1榦Yt~V{}۷sSJS'ٓmF$_껂ן[n RȅD6o J?iKJئV;'I] =q+Ǐ`L˘yN a ZLΡy}z "Q'wu!0iˉt];Ɇع6_r7 ֤Zz+~^a8rW&'*EWKt09}J1`pzE~CIR P 8',TIVUOU k)/zmŠR?>*ٞUl}LD\Cs!O;[H)s caޚD{L1 8SmfMn 18>~v&þOk ˌ+4l.+1ȤTh{>jqvk=vӧ0 hnth8U|El!Da鐏c*PJ5˓-)b.>#^> % oT׌IjuLpEE٬ե8&&Z#ˆஏk NC>E٦~I͢p>nC9g()%Zk!ڶ}V% p\d&S(dz{CDC̕z8"\֚zgQF"4V]Rk.XFs"LMnJ1566&ZuN/Xp(a~t7$ =,)|`%C+<ב'DG$DFPN&bnfDhMsy.BX?9VI_*nxgºhj?/'5,X)G&F.ə5Z)Tu|0qDu(y2M![˿U355㒚=Zz m/o3_.`!6D%NJ4vزlkdF!㐸2ݸ K&8d;-Tm0)ſ"S|dz_De@x&./{0.0f@8[osVpGa6>6 #Z:[or}"iaizL|/X VɊ7T Ȧo.pD<]O4@K @ ¨|j]^M1)8̥ "Zf IENDB`lft-3.98/icons/72x72/dest.png000644 000765 000024 00000021215 12050507714 015541 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxܜ[$y93ٻMJ6-R]LŖ+Vd #<H  $@I+y2` ɐeHEn˥$w{ٝK(28GE`:u4' SH68Wh8&GڇE@(F4]8R)" w!fv8[TEoPK+Uxb=?_BG.X^Yw!|/?+w,Xx`wω3&9.78y}[,],7GF% c a }Lʲ,"_zÒ AU~!#0 `O9zA`HQS^WQްA#!&ڎv/SjwqI!½ #p8b0SX!֠d 1U)G%PxO+][ C4%3T#K<ZC}7ME #DUε#T[Ա M&loh|OQwU$K:4| Hgng>7_8CL#E5apT!BD< MJTF"! h)m@0}Pty-^DSssbs:.'\z\xiGolEF]3PS* u 380ϳiGu7͖V W WsḠQU!PE| #Eᄍ~Iw6~ Ձow^住?iqT!1F09y$sGvM!PMU޺FjɃ CMS< 45Ky>'Wqou?qQdﰵrk2!2 8.Y:uG>k'R5=g~Σϰqg&rH[U9,aj&nj]̈ш)q }n(#jD"7ַXU뫳$uhCpHH#QT fS!(#5!}HR FCDUg}$De.?ÿۜ:_ʍwZX TAp@1 y:^fh ~8i× p-zk+~+^:-bE:iƨ\ɏsyT ]ryI5G_"Eb]x,>YTbSUL7ӔYi>XJ1YJD9ΟòbT4lP|=;8L2Y 'ZfeurfA:39&k>)R2CVQòfmh8 )Mqb:~DSF#`"`Y< -\MI^pe |˂ش4Tg 9q. 62;Q4AZKjF ho@.[TL.hhgS>-"SX͍h]ȸ6 QCd{X2Z[gaNv>T"hL7؉#Ho8Yt Oh\0ͦiS7;&Il|a9DS-̳I1D)C#Ǩ)BHKG){F.&ΥД!ལv)8 'nmY={ǃ4q`TBT!7?c _8 j K&ɲTwUIK$OUx]+U[56enMYIQ;7 SBfqWRB%0jvnY6z8' @א%ziJRq>Qj%(6 gzW$_ZIvbc{JŊ§ )"S`l4&i5qS@kK_)ʊR/M'Zvy1MLdbvŠT5.6)a3@F,]Duwc "9 J 8:)pjӈsdǝtfʚ+JAS*?Zę;o9 71I֗=R6tG̲' '囏31Ĩ.vebQDZh6Ť&I[/J Da@q53AC!2)c.ְY+9|(cYL@md9>vh;`kLizekqy|v []yk/r\f8wcPNį=^x#\XFa|&uj\#Nr)y^iڸ1ѕEe`w= o>o}s]\k.ňlyqӇ*Z՜Wx7 S ]ZskA¹VY֫ܮ >x|XwkL2S03ů1YL\q޸r^ܺrkS>ȏ^b.5Ҹ]YLijp(\{.B5:ϜdnSkw'9dVS~;?k?%=$uЀRʿ]1(h%B #|${,?ɹE@(J>sأ_+{錬$ե ̉r~Ag6{N]LwnZ0 +<g~gו:t 1uvo4Q!7` dz?X=~q޴eoE;2EO Ez~OMb9~}Ym] WH)<$ˋGCq94E B;-b\Df~’>WkV%7_Ūtш84_ w^gs6*Rr}47+][dqonndkQ(<\&5 k5ΨUA-,|VwsDŚ[39h<("YIw1q#].JBzJ!-{x?&x;?vƮNՍޓn{(/h$&km0O96e]%vlڲgaML(7#6M 1uG3oC.XA봓U4P{lUNbր[-e֥lxyj'8.ːD4ߥ!_-b͕5ϳP¾jXެdG_ZxX=bC@o,ţ|ؕT(|8@sR,/`KӒ8g$5=c8]i`g5.-g>+9B tFOZ, g@+IGϰtdac?+N |+]'3O шN'?{棬Йf_1:KfհRq(Rykku޹(WyJnѶֵ5z} wxO$84Fwdݤ.vXhԇvѪĵZfQLewĈhYBEv;LU='zb,BIX:/\]k?N%#3 Z!ih =ZIqw':Eލqx&佣3?ϏWWAֽro,ɼ8XPZiF٬՝;)^o{o;kBCM%V xa`ף(zj q:"ch h@Fh]b!jVGvٽ ؎`=^Fv%I9.F,D$T o*|hi:DLSvlyg?-Vg "ffѹ"@zUuKYQz" ڱ+/T_ƟsԮ9M9T_t}XQC]+[f54zՋ] mpnjCtB8ZhQG?-cQn}U_l9Kf6{s[|Y4AV5O~ʑB5+Ĝ@G&o_ tt(6o D|ϒܟA@?erIENDB`lft-3.98/icons/72x72/dark_cloud.png000644 000765 000024 00000016364 12050507714 016722 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxܜkWu{/_ھW$'HC( 8)EmUVB-jZQT~/DQVjE?>V< AR$ر+~8̙{~9k_'7wΝ3?kfZ @%:7-h#)D8=@?8 Bq,]^M0qi ^`mqD,LV ) ȘG"k0Ɣ/OU65XO'/VUUf@lDsT$M}/|!f.k" YHO&/9gQ  ='EhC'Gr[+*FU.3/k0Y)hoth }9˧s>?<7FIv\R"(I1Z%.WPLYٸ~7#i4i$):c`Z$MZJs+jXO`#b\c-<]Ҕ}qW鲠gIJ#IEZ@=IC HPsLj$ӓdUAo҄e˲2IӔFbÇ&BL!br (b{>gdf|@%`ށ ︕]ﺏ[޹(l,zzz"$h])I(G4/H:pDHg_aIP%.UaAgx(~y?v $3\&]̓f48 Om,#2 FIrolPĺ2S}d/<gN'7n17"ܼe ܼ yRH@"7.v2b֑8cz1(}ַŕ U^~Gs|SGH1*@Y>*^B(¼$Դkƹ< !*`N㥇 Tfx3G_׿Ľ}(A/B&B|A,(G{@b-E`# 4f&i, k>znR%͚|;.gz׮ӏ㞽 mk[P\{YdmD,'t1c,.ixĉ\8,sO\A\ŕ6nƷa^9FTvb2 IeAGlJ.(Aro/>8X¸U'mŗEדDqETS!Y񞎒HknQr@$!ִ{u!3?6Ǟײ$Ň@Gߦll}T%$NBDТSYżxEͨ4bz/+ڂӪ98簔I#?bL=7smHS0KE-bT Qc-Ũ1(ZhcXTh%&O$?,"\ Yءv=)n؎InyI.&ɫϪH`mK23¹%$5pz6lL:7Em4ƚ :بDu FOrkgG?Cg 1F@g.45`/i|@Oj,ɋ.&L}O2qɚj Ŕ6ztwTp8g2;?{E$: jh/ ed|8k(UdI?xɃO!UzJq #"ޅJ+j074OsۻL,IP-,AFABxc: #OR$ l/`r_Q1Y\X ElcL^!KS$anv0w/|YJJ($u!5IFR!"(jk-"qQ`nt,HfI$8~6 DFAŊ|LTQciL2{;A4%c2RRsW-( ij4M+b1M`$ Y4q1QB6f~%/eYG1DQDTj[VEmZnກFrF,OCPrIp/%V,"֐kDX9v~t"h,MZK)H KX^ %9[vh{Wk!K]ު==T}-.  HE DC4D@%]Nbfظj߶VZ--l  dCW58T='B&(98v.n;Er٠u7һjI7bdB; нҝ$$lzU νoM{f4VZ@*'k!}QݲkU5Kk-?Z/'OBL%HhDsT 3o펻7oF3ξ\ݓ3#Mer뮷pO=>K=#Nѐ;>To؁5Tg?3FR5.%eKrJETE[ֈ6Ϲ9n0zI5__\˶|7cEr%n74uwei8*v/y{+Q]*YU?:>'(b!Waj5ri/~>n|q"0]0\|~Km\)v E3_Fצl Q)FCJW`{﹋_?6őLjb3zyմsWCVm= 5lׂQKO3sy0&0.7d{-YrmBW%^yN$[op)ΰ+l[ZDА,Q0SP߉`*z 'aVD5BhIjh\g`Kldh_|5xN#߉@_i"H:}Bq9Hcط6vlcOqL5cvݼaDn.uY`AyAA%cj4ys}寮>Hq~ vr1-Gb+Zi@-mVn~<#gZZ8P.&~IGе_QL\FNgKwb0W٢DќzlXY!$.J_;߳^_\|䋰vL-].#1K7/Hn5-&nVVYBo_̞w2{^<ᦻpr>M"5wieJZ}C區>ӌ%ֳ8=~1=`DAښqycz.$MT<&W뇜 ?> [֝p˦Ig>+[0!xW(ŧe9]^w% @TH,͖\jģl!z- ^H4A&}^Ȭ,j57g-KisɧZ SS3(G3;M2K.a9|LQ{k$ڎChbq-K.Y`6k_1$O#P 50% }j<ش%1/Wڂuö{ ۷e}~ꯢܯ-֣xl$l`? p6Trn* G59QUŒ?o7#!ĸ2WƸ5P]96d>f!Iehs4-Wy[62j#_.qI*H|3 ̷bkpF|_-$)Y$Y@'@Zg:IENDB`lft-3.98/icons/72x72/client.png000644 000765 000024 00000016526 12050507714 016071 0ustar00vicstaff000000 000000 PNG  IHDRHHUG pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxܜۏeUjsql8! <BD!@7 !<"@ $@ @=#BIP.(H ;$883w콫ZĴ93}zwַ0&.Sߏ0 ]=ASs 8 mRr JH8>/=^s0 9`FM ErM>B'= Dų;?}؟و\Mo1 |yk9'7P|Ժcow]w}ܝAYg_%Kc#;mzA3is[z@"$k@LgstG39`~4ʨA6 YElb`6#"旉0ij)OZ7]ܐMZKg2a[Hu ktAD689g|AHק@ {h@v0+YKԬƠ&Wpu0e .qP)C,^v,-">e!1 "glDA/K͘?)>;pD^_qI9C?gS0[#s]t, U $MRcrAѐb Q O>|ȏq7u[V1rU>'y)3wEJ堊}۳h{BdVY=A LVE) WHYA83z'[DF|Z9&t%fGQ7mϼKA)ggɪPb U]O>Ujpmz;b3qVX̰W^&9%>ib1g3]ibBM}ZW&MW%JkQ d&rW̭W_7t}x.\~KonbN5KJ ϐSOJ21fTW ѥD#}VRJ+00D3K Bõ3 '"4#> ]Rer֚FN3}[T%LFLUo K>+}R@C `bAjPg#YO-O)/}?fĝ?pnwD#21BܠOFxr`q[j-nhA VRHk%5ڶ}+3ErN-ȹ ڼHIJZBrhVɌFs J$s6&Q ̜FVZ"Fl\yߘ" 9@ %tIh&U xoCPmb s#i /L3%Y #,cTlt/E(o"h:Yt1 Gٍ*GdxGYl6H)M 8rN&aQv0ZM:GrU}05"KQ7JN;nGR -V"(s"͈] E /N*T1Vd˶iñ(oROFʉ+4czs3<4fЌ_^Yaz0RPhC|+hDCg/R IL& q)j: hKYAWPlOH쎙*NRR"Jl E 1S0E4w51SiNͺdWݪ^`RGԽUĈ&u&d%"o{ɪ\xEhR{G$`5+ts%`.Ea' DRs7\aˋo~1I,rb` }aSp HUωʔ×HzbK 0ja$gb!6m6LZqs0q0(B RjwZ>  a'bPδt.j_a$д)1z3 ne +1IKLF ;Q e¾fepy( N*Df]R` e˙tj:+W|ۦJɮ%MYȸL#Bq MkƚB'a. M$.{rɪ,Zq+T4 &54kd^ m9iIԔY C!t=u ͈JŗW{uHdm 0Q >%mp;93r'L&Z j7.7X颵D,''eI:h1lε.[ Ӗˋ):2:Ptnݿc_iYfzdvo]ehgh,MUvYA`3@H1ţMl!7M:; s^ݟ3Є@EA +$E_CQ0xQ7TKjϓ3{~hSc4n[**kTҼّ^ E!=!6Xlm.g4EjMZ;H 0aiTQUF(3"fZ:./;)VjQϬH=˕{$ĸi CWCrsKFΉɹAs:oEn޼bn5njjNUIC|;.xұa1r1фo*fYhQQ9[q |28VL -w[A$'*Fܾywp@_HVb %SKVb ~6=ڠUwmG4JRpUGUI.3xc'Oާ< $M#{ uTe}OX8hh|?AntKJfI[o~{Jtc9X$nᅧXUmgP.˼r]Jz4Ŵ I!!b>7^}Cu#& ʨ Fp |o/^e蒰?ruUϹq7Ӵ!r9(ce.6被 ACN: -7V5Tge3D3sK=N !NvRFe_ӊKj*du7I@w=|k4㡋o3k[s>oCez2R=mu~)z7eմ*UR-yzǿ> 'CK/~'wO7ibY|ƦL7%Hsm{u~b}:p;XWpj6ǍFu[Quc3A7xXhST=nǧ9uk_QN.7e7:gv'~7&o_*s6M([i~x=!孧Z'9{<6#m06 ~h` Q;Ύ7!>zo7Iڭ{m^ g3@^{=.>, ^'ELK1R|O"{{ uw^[y9VZ nEr}+ܹuos7^ 3fP+hk)Ɛbu'fG*U ,'v )ڄH=-1PgL*9K*6hAC7꿫=*w1O=CV19s.TWOM6SGu5\Dc8=&/3+vk(wcru}~q50e}&+$O1u@!~yGiS;wG׾?n&ҦSU-o=͛%\]氾2[ S]k+nD%b>v(Z{rimv-'=@- Wi=q<297^9(1iQ`Abz1c^ʾ&F\J;4s []!a߳Z6%@%8tEbj pM`qjsR7%ۄ$Y\–}⅃+3A3V$5 pl(1bwFop#YCh QP3jbf=R.IXZovRFAki<;/LI~ocz zrc  9?Ԯn//] WUgnixt:fv4lTf-RG:ߤh2 qoyrΙE;4?/"6<{)Dqd#%wp[˭ρY}m? |nIENDB`lft-3.98/include/net/000755 000765 000024 00000000000 15174131662 014405 5ustar00vicstaff000000 000000 lft-3.98/include/netinet/000755 000765 000024 00000000000 15174131662 015265 5ustar00vicstaff000000 000000 lft-3.98/include/sys/000755 000765 000024 00000000000 15174131662 014435 5ustar00vicstaff000000 000000 lft-3.98/include/win32/000755 000765 000024 00000000000 15174131662 014561 5ustar00vicstaff000000 000000 lft-3.98/include/libpcap/000755 000765 000024 00000000000 15174131662 015231 5ustar00vicstaff000000 000000 lft-3.98/include/libpcap/bittypes.h000755 000765 000024 00000007445 11053131665 017255 0ustar00vicstaff000000 000000 /* * Copyright (C) 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _BITTYPES_H #define _BITTYPES_H #ifndef HAVE_U_INT8_T #if SIZEOF_CHAR == 1 typedef unsigned char u_int8_t; typedef signed char int8_t; #elif SIZEOF_INT == 1 typedef unsigned int u_int8_t; typedef signed int int8_t; #else /* XXX */ #error "there's no appropriate type for u_int8_t" #endif #define HAVE_U_INT8_T 1 #define HAVE_INT8_T 1 #endif /* HAVE_U_INT8_T */ #ifndef HAVE_U_INT16_T #if SIZEOF_SHORT == 2 typedef unsigned short u_int16_t; typedef signed short int16_t; #elif SIZEOF_INT == 2 typedef unsigned int u_int16_t; typedef signed int int16_t; #elif SIZEOF_CHAR == 2 typedef unsigned char u_int16_t; typedef signed char int16_t; #else /* XXX */ #error "there's no appropriate type for u_int16_t" #endif #define HAVE_U_INT16_T 1 #define HAVE_INT16_T 1 #endif /* HAVE_U_INT16_T */ #ifndef HAVE_U_INT32_T #if SIZEOF_INT == 4 typedef unsigned int u_int32_t; typedef signed int int32_t; #elif SIZEOF_LONG == 4 typedef unsigned long u_int32_t; typedef signed long int32_t; #elif SIZEOF_SHORT == 4 typedef unsigned short u_int32_t; typedef signed short int32_t; #else /* XXX */ #error "there's no appropriate type for u_int32_t" #endif #define HAVE_U_INT32_T 1 #define HAVE_INT32_T 1 #endif /* HAVE_U_INT32_T */ #ifndef HAVE_U_INT64_T #if SIZEOF_LONG_LONG == 8 typedef unsigned long long u_int64_t; #elif defined(_MSC_EXTENSIONS) typedef unsigned _int64 u_int64_t; #elif SIZEOF_INT == 8 typedef unsigned int u_int64_t; #elif SIZEOF_LONG == 8 typedef unsigned long u_int64_t; #elif SIZEOF_SHORT == 8 typedef unsigned short u_int64_t; #else /* XXX */ #error "there's no appropriate type for u_int64_t" #endif #endif /* HAVE_U_INT64_T */ #ifndef PRId64 #ifdef _MSC_EXTENSIONS #define PRId64 "I64d" #else /* _MSC_EXTENSIONS */ #define PRId64 "lld" #endif /* _MSC_EXTENSIONS */ #endif /* PRId64 */ #ifndef PRIo64 #ifdef _MSC_EXTENSIONS #define PRIo64 "I64o" #else /* _MSC_EXTENSIONS */ #define PRIo64 "llo" #endif /* _MSC_EXTENSIONS */ #endif /* PRIo64 */ #ifndef PRIx64 #ifdef _MSC_EXTENSIONS #define PRIx64 "I64x" #else /* _MSC_EXTENSIONS */ #define PRIx64 "llx" #endif /* _MSC_EXTENSIONS */ #endif /* PRIx64 */ #ifndef PRIu64 #ifdef _MSC_EXTENSIONS #define PRIu64 "I64u" #else /* _MSC_EXTENSIONS */ #define PRIu64 "llu" #endif /* _MSC_EXTENSIONS */ #endif /* PRIu64 */ #endif /* _BITTYPES_H */ lft-3.98/include/libpcap/Gnuc.h000755 000765 000024 00000001417 11053131665 016277 0ustar00vicstaff000000 000000 /* @(#) $Header: /tcpdump/master/libpcap/Win32/Include/Gnuc.h,v 1.1 2002/08/01 08:33:05 risso Exp $ (LBL) */ /* Define __P() macro, if necessary */ #ifndef __P #if __STDC__ #define __P(protos) protos #else #define __P(protos) () #endif #endif /* inline foo */ #ifndef __cplusplus #ifdef __GNUC__ #define inline __inline #else #define inline #endif #endif /* * Handle new and old "dead" routine prototypes * * For example: * * __dead void foo(void) __attribute__((volatile)); * */ #ifdef __GNUC__ #ifndef __dead #define __dead volatile #endif #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) #ifndef __attribute__ #define __attribute__(args) #endif #endif #else #ifndef __dead #define __dead #endif #ifndef __attribute__ #define __attribute__(args) #endif #endif lft-3.98/include/win32/wingettimeofday.c000755 000765 000024 00000004461 11053131665 020127 0ustar00vicstaff000000 000000 #if defined(WIN32) || defined(_WIN32) #include "wingettimeofday.h" #include #ifndef __GNUC__ #define EPOCHFILETIME (116444736000000000i64) #else #define EPOCHFILETIME (116444736000000000LL) #endif int __gettimeofday(struct timeval* tv, void * tz) { struct _timeb currSysTime; _ftime(&currSysTime); tv->tv_sec = currSysTime.time; tv->tv_usec = currSysTime.millitm * 1000; return 0; } LARGE_INTEGER getFILETIMEoffset() { SYSTEMTIME s; FILETIME f; LARGE_INTEGER t; s.wYear = 1970; s.wMonth = 1; s.wDay = 1; s.wHour = 0; s.wMinute = 0; s.wSecond = 0; s.wMilliseconds = 0; SystemTimeToFileTime(&s, &f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; return (t); } int gettimeofday(struct timeval *tv, void * tz) { LARGE_INTEGER t; FILETIME f; double microseconds; static LARGE_INTEGER offset; static LARGE_INTEGER base; static double frequencyToMicroseconds; static int initialized = 0; static BOOL usePerformanceCounter = 0; if(!initialized) { LARGE_INTEGER performanceFrequency; initialized = 1; usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); if(usePerformanceCounter) { LARGE_INTEGER tmpoffs; QueryPerformanceCounter(&offset); frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; tmpoffs = getFILETIMEoffset(); GetSystemTimeAsFileTime(&f); base.QuadPart = f.dwHighDateTime; base.QuadPart <<= 32; base.QuadPart |= f.dwLowDateTime; base.QuadPart -= tmpoffs.QuadPart; microseconds = (double)base.QuadPart / 10; base.QuadPart = microseconds; tv->tv_sec = base.QuadPart / 1000000; tv->tv_usec = base.QuadPart % 1000000; } else { offset = getFILETIMEoffset(); frequencyToMicroseconds = 10.; base.QuadPart=0i64; } } if(usePerformanceCounter) QueryPerformanceCounter(&t); else { GetSystemTimeAsFileTime(&f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; } t.QuadPart -= offset.QuadPart; microseconds = (double)t.QuadPart / frequencyToMicroseconds; t.QuadPart = microseconds + base.QuadPart; tv->tv_sec = t.QuadPart / 1000000; tv->tv_usec = t.QuadPart % 1000000; return (0); } #endif lft-3.98/include/win32/wingetopt.h000755 000765 000024 00000001711 15165341545 016760 0ustar00vicstaff000000 000000 #ifndef WINGETOPT_H #define WINGETOPT_H #ifdef __cplusplus extern "C" { #endif /* Globals shared between getopt() and getopt_long() */ extern char *optarg; extern int optind; extern int opterr; extern int optopt; /* has_arg values for struct option */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 /* Long-option descriptor passed to getopt_long() */ struct option { const char *name; /* long option name (without "--") */ int has_arg; /* no_argument / required_argument / optional_argument */ int *flag; /* if non-NULL, set *flag = val and return 0 */ int val; /* value to return (or store in *flag) */ }; int getopt(int argc, char *argv[], const char *optstring); int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); #ifdef __cplusplus } #endif #endif /* WINGETOPT_H */ lft-3.98/include/win32/wingettimeofday.h000755 000765 000024 00000000322 11053131665 020124 0ustar00vicstaff000000 000000 #ifndef WINGETTIMEOFDAY_H #define WINGETTIMEOFDAY_H #include #ifdef __cplusplus extern "C"{ #endif int gettimeofday(struct timeval* tp, void* tzp); #ifdef __cplusplus } #endif #endif lft-3.98/include/win32/wingetopt.c000755 000765 000024 00000012673 15165341545 016764 0ustar00vicstaff000000 000000 #if defined(WIN32) || defined(_WIN32) #include "wingetopt.h" #include #include /* Option-parsing state shared across calls */ char *optarg = NULL; int optind = 0; /* 0 = not yet started; first call advances to 1 */ int opterr = 1; /* print errors to stderr when non-zero */ int optopt = 0; /* holds the unknown option char on '?' return */ /* ------------------------------------------------------------------------- * getopt() — POSIX short-option parser * ------------------------------------------------------------------------- */ int getopt(int argc, char *argv[], const char *optstring) { static char *next = NULL; char c; const char *cp; /* optind == 0 is the GNU reset convention */ if (optind == 0) { optind = 1; next = NULL; } optarg = NULL; /* Advance to next argv element when current one is exhausted */ if (next == NULL || *next == '\0') { if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return -1; if (strcmp(argv[optind], "--") == 0) { optind++; return -1; } next = argv[optind] + 1; /* skip the leading '-' */ optind++; } c = *next++; cp = strchr(optstring, c); if (cp == NULL || c == ':') { optopt = c; if (opterr && optstring[0] != ':') fprintf(stderr, "lft: invalid option -- '%c'\n", c); return '?'; } if (cp[1] == ':') { /* option requires an argument */ if (*next != '\0') { optarg = next; next = NULL; } else if (optind < argc) { optarg = argv[optind++]; } else { optopt = c; if (optstring[0] == ':') return ':'; if (opterr) fprintf(stderr, "lft: option requires an argument -- '%c'\n", c); return '?'; } } return c; } /* ------------------------------------------------------------------------- * getopt_long() — GNU-style long-option parser * * Long options take the form --name or --name=value. * When a match is found: * - if longopts[i].flag != NULL → *flag = val, return 0 * - if longopts[i].flag == NULL → return val * Short options are handled by delegating to getopt(). * ------------------------------------------------------------------------- */ int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex) { /* optind == 0: reset (GNU extension) */ if (optind == 0) optind = 1; /* Is the current token a long option? */ if (optind < argc && argv[optind][0] == '-' && argv[optind][1] == '-' && argv[optind][2] != '\0') { const char *token = argv[optind] + 2; /* skip "--" */ const char *eq = strchr(token, '='); size_t namelen = eq ? (size_t)(eq - token) : strlen(token); int i; for (i = 0; longopts[i].name != NULL; i++) { if (strncmp(token, longopts[i].name, namelen) == 0 && longopts[i].name[namelen] == '\0') { if (longindex) *longindex = i; optind++; /* consume the --name token */ optarg = NULL; switch (longopts[i].has_arg) { case required_argument: if (eq) { optarg = (char *)(eq + 1); } else if (optind < argc) { optarg = argv[optind++]; } else { /* required argument missing */ optopt = longopts[i].val; if (optstring[0] == ':') return ':'; if (opterr) fprintf(stderr, "lft: option '--%s' requires an argument\n", longopts[i].name); return '?'; } break; case optional_argument: optarg = eq ? (char *)(eq + 1) : NULL; break; case no_argument: default: if (eq) { /* argument supplied to a no-argument option */ optopt = longopts[i].val; if (opterr) fprintf(stderr, "lft: option '--%s' doesn't allow an argument\n", longopts[i].name); return '?'; } break; } if (longopts[i].flag != NULL) { *longopts[i].flag = longopts[i].val; return 0; } return longopts[i].val; } } /* No match — unknown long option */ if (opterr) fprintf(stderr, "lft: unrecognized option '--%.*s'\n", (int)namelen, token); optind++; return '?'; } /* "--" end-of-options sentinel */ if (optind < argc && strcmp(argv[optind], "--") == 0) { optind++; return -1; } /* Not a long option — delegate to short-option parser */ return getopt(argc, (char **)argv, optstring); } #endif /* WIN32 */ lft-3.98/include/win32/winlft_ifname.c000755 000765 000024 00000052302 11053131665 017547 0ustar00vicstaff000000 000000 #if defined(WIN32) || defined(_WIN32) #include #include #include #include #include #include #include #include #include "../../lft_ifname.h" typedef enum { KWV_UNKNOWN, //while unknown (before first call of this module) KWV_VISTA, //Vista KWV_2K, //98/ME, NTSP4, W2K and XP KWV_NT4, //NT4 with SP<4 KWV_95 //Win95 }KNOWN_WIN_VERSION; static KNOWN_WIN_VERSION VerifyWindowsVersion() { static KNOWN_WIN_VERSION WinVersion=KWV_UNKNOWN; OSVERSIONINFOEX osvi; BOOL bOsVersionInfoEx; if(WinVersion!=KWV_UNKNOWN) return WinVersion; // Try calling GetVersionEx using the OSVERSIONINFOEX structure. // If that fails, try using the OSVERSIONINFO structure. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) { osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(!GetVersionEx( (OSVERSIONINFO *) &osvi)) return KWV_UNKNOWN; } switch(osvi.dwPlatformId) { case VER_PLATFORM_WIN32_NT: //Test for the Windows NT product family if(osvi.dwMajorVersion<=4) //NT4 { if(osvi.wServicePackMajor<4) WinVersion=KWV_NT4; else WinVersion=KWV_2K; } else { if(osvi.dwMajorVersion>5) WinVersion=KWV_VISTA; else WinVersion=KWV_2K; //2K and XP } break; case VER_PLATFORM_WIN32_WINDOWS: //Test for the Windows Me/98/95 default: WinVersion=KWV_95; break; } return WinVersion; } static u_long lft_getifaddr_95(const char *argifname) { HKEY key; char ethname[5]="eth/"; char pppname[5]="ppp/"; int i; FILETIME update; LONG res; DWORD size; u_long ret; char ifname[256]; if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Enum\\Network\\MSTCP", 0, KEY_READ, &key) != ERROR_SUCCESS) return -1; size = sizeof(ifname); for(i=0;(res = RegEnumKeyEx(key, i, (LPTSTR)ifname, &size, 0, 0, 0, &update))!=ERROR_NO_MORE_ITEMS;i++) { HKEY ifkey, subkey; DWORD dsize,ipsize,npsize,asize; char driver[256], classname[256], netname[256]; char adapter[256], ip[256], np[256]; if(res != ERROR_SUCCESS || RegOpenKeyEx(key, (LPCTSTR)ifname, 0, KEY_READ, &ifkey) != ERROR_SUCCESS) continue; dsize = sizeof(driver); if(RegQueryValueEx(ifkey, L"Driver", 0, NULL, (unsigned char *)driver, &dsize) != ERROR_SUCCESS) { RegCloseKey(ifkey); continue; } strcpy(classname, "System\\CurrentControlSet\\Services\\Class\\"); strcat(classname, driver); if((res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)classname, 0, KEY_READ, &subkey)) != ERROR_SUCCESS) { RegCloseKey(ifkey); continue; } ipsize=sizeof(ip); npsize=sizeof(np); if(RegQueryValueEx(subkey, L"IPAddress", 0, NULL, (unsigned char *) ip, &ipsize) == ERROR_SUCCESS) { ret=inet_addr(ip); RegCloseKey (subkey); strcpy(netname, "System\\CurrentControlSet\\Services\\Class\\Net\\"); strcat(netname, ifname); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)netname, 0, KEY_READ, &subkey) != ERROR_SUCCESS) { RegCloseKey(ifkey); continue; } asize=sizeof(adapter); if( RegQueryValueEx (subkey, L"AdapterName", 0, NULL, (unsigned char *) adapter, &asize) == ERROR_SUCCESS && !strcmp(adapter, "MS$PPP")) { pppname[3]++; } else { ethname[3]++; } RegCloseKey(subkey); RegCloseKey(ifkey); if(!strcmp(pppname,argifname) || !strcmp(ethname,argifname)) break; else ret=-1; } } RegCloseKey(key); return ret; } static char * lft_getifname_95(struct in_addr addr) { HKEY key; char ethname[5]="eth/"; char pppname[5]="ppp/"; int i; FILETIME update; LONG res; DWORD size; u_long ret; int isethaddr,isfound; char ifname[256]; if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Enum\\Network\\MSTCP", 0, KEY_READ, &key) != ERROR_SUCCESS) return NULL; size = sizeof(ifname); for(i=0;(res = RegEnumKeyEx(key, i, (TCHAR *)ifname, &size, 0, 0, 0, &update))!=ERROR_NO_MORE_ITEMS;i++) { HKEY ifkey, subkey; DWORD dsize,ipsize,npsize,asize; char driver[256], classname[256], netname[256]; char adapter[256], ip[256], np[256]; if(res != ERROR_SUCCESS || RegOpenKeyEx(key, (LPCTSTR)ifname, 0, KEY_READ, &ifkey) != ERROR_SUCCESS) continue; dsize = sizeof(driver); if(RegQueryValueEx(ifkey, L"Driver", 0, NULL, (unsigned char *)driver, &dsize) != ERROR_SUCCESS) { RegCloseKey(ifkey); continue; } strcpy(classname, "System\\CurrentControlSet\\Services\\Class\\"); strcat(classname, driver); if((res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)classname, 0, KEY_READ, &subkey)) != ERROR_SUCCESS) { RegCloseKey(ifkey); continue; } ipsize=sizeof(ip); npsize=sizeof(np); if(RegQueryValueEx(subkey, L"IPAddress", 0, NULL, (unsigned char *) ip, &ipsize) == ERROR_SUCCESS) { ret=inet_addr(ip); RegCloseKey (subkey); strcpy(netname, "System\\CurrentControlSet\\Services\\Class\\Net\\"); strcat(netname, ifname); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)netname, 0, KEY_READ, &subkey) != ERROR_SUCCESS) { RegCloseKey(ifkey); continue; } asize=sizeof(adapter); if( RegQueryValueEx (subkey, L"AdapterName", 0, NULL, (unsigned char *) adapter, &asize) == ERROR_SUCCESS && !strcmp(adapter, "MS$PPP")) { pppname[3]++; isethaddr=0; } else { ethname[3]++; isethaddr=1; } RegCloseKey(subkey); RegCloseKey(ifkey); if(ret==addr.s_addr) { isfound=1; break; } else isfound=0; } } RegCloseKey(key); if(!isfound) return NULL; if(isethaddr) return strdup(ethname); return strdup(pppname); } static u_long lft_getifaddr_NT4(const char *ifname) { HKEY key; char devname[256]; struct sockaddr_in *sa = NULL; struct sockaddr *so = NULL; DWORD size; int cnt = 1,isfound; u_long ret; char *binding = (char *)0; if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Linkage", 0, KEY_READ, &key) == ERROR_SUCCESS) { if(RegQueryValueEx(key, L"Bind", NULL, NULL, NULL, &size) == ERROR_SUCCESS) { binding = (char *)_alloca(size); if(RegQueryValueEx (key, L"Bind", NULL, NULL, (unsigned char *)binding, &size) != ERROR_SUCCESS) binding = NULL; } RegCloseKey(key); } if(binding) { char *bp, eth[2] = "/"; int ipsize; char cardkey[256], ipaddress[256]; for(bp = binding; *bp; bp+=strlen(bp)+1) { bp += strlen("\\Device\\"); strcpy(cardkey, "SYSTEM\\CurrentControlSet\\Services\\"); strcat(cardkey, bp); strcat(cardkey, "\\Parameters\\Tcpip"); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)cardkey, 0, KEY_READ, &key) != ERROR_SUCCESS) continue; ipsize=256; if(RegQueryValueEx(key, L"IPAddress", NULL, NULL, (unsigned char *) ipaddress, &ipsize) == ERROR_SUCCESS) { char *ip; for(ip = ipaddress; *ip; ip += strlen(ip)+1) { if(!strncmp(bp, "NdisWan", 7)) { strcpy(devname, "ppp"); strcat(devname, bp + 7); } else { eth[0]++; strcpy(devname, "eth"); strcat(devname, eth); } ret=inet_addr(ipaddress); if(!ret) { ipsize=256; if(RegQueryValueEx (key, L"DhcpIPAddress", NULL, NULL, (unsigned char *) ipaddress, &ipsize) == ERROR_SUCCESS) { ret=inet_addr(ipaddress); } } if(!strcmp(devname,ifname)) { isfound=1; break; } else isfound=0; } } RegCloseKey(key); if(isfound) return ret; } } return -1; } static char * lft_getifname_NT4(struct in_addr addr) { HKEY key; char devname[256]; struct sockaddr_in *sa = NULL; struct sockaddr *so = NULL; DWORD size; int cnt = 1,isfound; u_long ret; char *binding = (char *)0; if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Linkage", 0, KEY_READ, &key) == ERROR_SUCCESS) { if(RegQueryValueEx(key, L"Bind", NULL, NULL, NULL, &size) == ERROR_SUCCESS) { binding = (char *)_alloca(size); if(RegQueryValueEx (key, L"Bind", NULL, NULL, (unsigned char *)binding, &size) != ERROR_SUCCESS) binding = NULL; } RegCloseKey(key); } if(binding) { char *bp, eth[2] = "/"; int ipsize; char cardkey[256]; char ipaddress[256]; for(bp = binding; *bp; bp+=strlen(bp)+1) { bp += strlen("\\Device\\"); strcpy(cardkey, "SYSTEM\\CurrentControlSet\\Services\\"); strcat(cardkey, bp); strcat(cardkey, "\\Parameters\\Tcpip"); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)cardkey, 0, KEY_READ, &key) != ERROR_SUCCESS) continue; ipsize=256; if(RegQueryValueEx(key, L"IPAddress", NULL, NULL, (unsigned char *) ipaddress, &ipsize) == ERROR_SUCCESS) { char *ip; for(ip = ipaddress; *ip; ip += strlen(ip)+1) { if(!strncmp(bp, "NdisWan", 7)) { strcpy(devname, "ppp"); strcat(devname, bp + 7); } else { eth[0]++; strcpy(devname, "eth"); strcat(devname, eth); } ret=inet_addr(ipaddress); if(!ret) { ipsize=256; if(RegQueryValueEx (key, L"DhcpIPAddress", NULL, NULL, (unsigned char *) ipaddress, &ipsize) == ERROR_SUCCESS) { ret=inet_addr(ipaddress); } } if(addr.s_addr==ret) { isfound=1; break; } else isfound=0; } } RegCloseKey(key); if(isfound) return strdup(devname); } } return NULL; } static u_long lft_getifaddr_2K(const char *ifname) { int cnt = 0; int ethId = 0, pppId = 0, slpId = 0, tokId = 0; DWORD ip_cnt; DWORD siz_ip_table = 0; PMIB_IPADDRTABLE ipt; PMIB_IFROW ifrow; char devname[256]; typedef struct { DWORD ifIndex; size_t count; unsigned int enumerated; // for eth0:1 unsigned int classId; // for eth0, tok0 ... } ifcount_t; ifcount_t *iflist, *ifEntry; if(GetIpAddrTable (NULL, &siz_ip_table, TRUE) == ERROR_INSUFFICIENT_BUFFER) { ifrow = (PMIB_IFROW)_alloca(sizeof(MIB_IFROW)); ipt = (PMIB_IPADDRTABLE)_alloca(siz_ip_table); } if(GetIpAddrTable (ipt, &siz_ip_table, TRUE) != NO_ERROR) return -1; iflist = (ifcount_t *) alloca(sizeof(ifcount_t)*(ipt->dwNumEntries + 1)); memset(iflist, 0, sizeof (ifcount_t) * (ipt->dwNumEntries + 1)); for(ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ++ip_cnt) { ifEntry = iflist; /* search for matching entry (and stop at first free entry) */ while(ifEntry->count != 0) { if(ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex) break; ifEntry++; } if(ifEntry->count == 0) { ifEntry->count = 1; ifEntry->ifIndex = ipt->table[ip_cnt].dwIndex; } else { ifEntry->count++; } } // reset the last element. This is just the stopper for the loop. iflist[ipt->dwNumEntries].count = 0; for(ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ip_cnt++) { ifcount_t *ifEntry = iflist; memset(ifrow, 0, sizeof(MIB_IFROW)); ifrow->dwIndex = ipt->table[ip_cnt].dwIndex; if(GetIfEntry(ifrow) != NO_ERROR) continue; /* search for matching entry (and stop at first free entry) */ while(ifEntry->count != 0) { if(ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex) break; ifEntry++; } /* Setup the interface name */ switch(ifrow->dwType) { case MIB_IF_TYPE_TOKENRING: if(ifEntry->enumerated == 0) { ifEntry->classId = tokId++; sprintf(devname, "tok%u", ifEntry->classId); } else { sprintf(devname, "tok%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; #ifdef IF_TYPE_IEEE80211 case IF_TYPE_IEEE80211: #endif case MIB_IF_TYPE_ETHERNET: if(ifEntry->enumerated == 0) { ifEntry->classId = ethId++; sprintf(devname, "eth%u", ifEntry->classId); } else { sprintf(devname, "eth%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; case MIB_IF_TYPE_PPP: if(ifEntry->enumerated == 0) { ifEntry->classId = pppId++; sprintf(devname, "ppp%u", ifEntry->classId); } else { sprintf(devname, "ppp%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; case MIB_IF_TYPE_SLIP: if(ifEntry->enumerated == 0) { ifEntry->classId = slpId++; sprintf(devname, "slp%u", ifEntry->classId); } else { sprintf(devname, "slp%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; case MIB_IF_TYPE_LOOPBACK: strcpy(devname, "lo"); break; default: continue; } if(!strcmp(devname,ifname)) { return ipt->table[ip_cnt].dwAddr; } } return -1; } static char * lft_getifname_2K(struct in_addr addr) { int cnt = 0; int ethId = 0, pppId = 0, slpId = 0, tokId = 0; DWORD ip_cnt; DWORD siz_ip_table = 0; PMIB_IPADDRTABLE ipt; PMIB_IFROW ifrow; static char devname[256]; static struct in_addr savedaddr; static int FirstTime=1; typedef struct { DWORD ifIndex; size_t count; unsigned int enumerated; // for eth0:1 unsigned int classId; // for eth0, tok0 ... } ifcount_t; ifcount_t *iflist, *ifEntry; if(!FirstTime && savedaddr.s_addr==addr.s_addr) { return strdup(devname); } savedaddr.s_addr=addr.s_addr; if(GetIpAddrTable (NULL, &siz_ip_table, TRUE) == ERROR_INSUFFICIENT_BUFFER) { ifrow = (PMIB_IFROW)_alloca(sizeof(MIB_IFROW)); ipt = (PMIB_IPADDRTABLE)_alloca(siz_ip_table); } if(GetIpAddrTable (ipt, &siz_ip_table, TRUE) != NO_ERROR) return NULL; iflist = (ifcount_t *) alloca(sizeof(ifcount_t)*(ipt->dwNumEntries + 1)); memset(iflist, 0, sizeof (ifcount_t) * (ipt->dwNumEntries + 1)); for(ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ++ip_cnt) { ifEntry = iflist; /* search for matching entry (and stop at first free entry) */ while(ifEntry->count != 0) { if(ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex) break; ifEntry++; } if(ifEntry->count == 0) { ifEntry->count = 1; ifEntry->ifIndex = ipt->table[ip_cnt].dwIndex; } else { ifEntry->count++; } } // reset the last element. This is just the stopper for the loop. iflist[ipt->dwNumEntries].count = 0; for(ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ip_cnt++) { ifcount_t *ifEntry = iflist; memset(ifrow, 0, sizeof(MIB_IFROW)); ifrow->dwIndex = ipt->table[ip_cnt].dwIndex; if(GetIfEntry(ifrow) != NO_ERROR) continue; /* search for matching entry (and stop at first free entry) */ while(ifEntry->count != 0) { if(ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex) break; ifEntry++; } /* Setup the interface name */ switch(ifrow->dwType) { case MIB_IF_TYPE_TOKENRING: if(ifEntry->enumerated == 0) { ifEntry->classId = tokId++; sprintf(devname, "tok%u", ifEntry->classId); } else { sprintf(devname, "tok%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; #ifdef IF_TYPE_IEEE80211 case IF_TYPE_IEEE80211: #endif case MIB_IF_TYPE_ETHERNET: if(ifEntry->enumerated == 0) { ifEntry->classId = ethId++; sprintf(devname, "eth%u", ifEntry->classId); } else { sprintf(devname, "eth%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; case MIB_IF_TYPE_PPP: if(ifEntry->enumerated == 0) { ifEntry->classId = pppId++; sprintf(devname, "ppp%u", ifEntry->classId); } else { sprintf(devname, "ppp%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; case MIB_IF_TYPE_SLIP: if(ifEntry->enumerated == 0) { ifEntry->classId = slpId++; sprintf(devname, "slp%u", ifEntry->classId); } else { sprintf(devname, "slp%u:%u", ifEntry->classId, ifEntry->enumerated - 1); } ifEntry->enumerated++; break; case MIB_IF_TYPE_LOOPBACK: strcpy(devname, "lo"); break; default: continue; } if(addr.s_addr==ipt->table[ip_cnt].dwAddr) { FirstTime=0; return strdup(devname); } } return NULL; } u_long lft_getifaddr (const char *ifname) { switch(VerifyWindowsVersion()) { case KWV_95: return lft_getifaddr_95(ifname); case KWV_2K: case KWV_VISTA: return lft_getifaddr_2K(ifname); case KWV_NT4: return lft_getifaddr_NT4(ifname); } return -1; } char * lft_getifname (struct in_addr addr) { static char ifname[256]; static struct in_addr savedaddr; static int FirstTime=1; char * ret; if(!FirstTime && savedaddr.s_addr==addr.s_addr) { return strdup(ifname); } switch(VerifyWindowsVersion()) { case KWV_95: ret=lft_getifname_95(addr); break; case KWV_2K: case KWV_VISTA: ret=lft_getifname_2K(addr); break; case KWV_NT4: ret=lft_getifname_NT4(addr); break; } savedaddr.s_addr=addr.s_addr; FirstTime=0; strncpy(ifname,ret,255); return ret; } #ifdef LFT_IFADDR_TESTING extern int main (int argc, char *argv[]) { struct in_addr in; char *addr; if (argc > 1) addr = strdup (argv[1]); else addr = strdup ("eth0"); in.s_addr = lft_getifaddr (addr); if (in.s_addr == -1) { fprintf (stderr, "%s: Error reading ifname\n", addr); fflush (stderr); free(addr); exit (-1); } fprintf (stdout, "%s: %s\n", addr, inet_ntoa (in)); fflush (stdout); free (addr); exit (0); } #endif /*LFT_IFNAME_TESTING*/ #endif lft-3.98/include/sys/mbuf.h000755 000765 000024 00000000105 11053131665 015531 0ustar00vicstaff000000 000000 #ifndef MLEN #define MLEN 128 /* needed for slcompress.h */ #endif lft-3.98/include/netinet/tcp.h000755 000765 000024 00000007154 11053131665 016231 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp.h 8.1 (Berkeley) 6/10/93 */ typedef u_long tcp_seq; /* * TCP header. * Per RFC 793, September, 1981. */ struct tcphdr { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN u_char th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN u_char th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; #define TCPOPT_EOL 0 #define TCPOPT_NOP 1 #define TCPOPT_MAXSEG 2 #define TCPOLEN_MAXSEG 4 #define TCPOPT_WINDOW 3 #define TCPOLEN_WINDOW 3 #define TCPOPT_SACK_PERMITTED 4 /* Experimental */ #define TCPOLEN_SACK_PERMITTED 2 #define TCPOPT_SACK 5 /* Experimental */ #define TCPOPT_TIMESTAMP 8 #define TCPOLEN_TIMESTAMP 10 #define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */ #define TCPOPT_TSTAMP_HDR \ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP) /* * Default maximum segment size for TCP. * With an IP MSS of 576, this is 536, * but 512 is probably more convenient. * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)). */ #define TCP_MSS 512 #define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ #define TCP_MAX_WINSHIFT 14 /* maximum window shift */ /* * User-settable options (used with setsockopt). */ #ifndef TCP_NODELAY #define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ #endif #ifndef TCP_MAXSEG #define TCP_MAXSEG 0x02 /* set maximum segment size */ #endif lft-3.98/include/netinet/ip_icmp.h000755 000765 000024 00000014530 11053131665 017057 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 */ /* * Interface Control Message Protocol Definitions. * Per RFC 792, September 1981. */ /* * Structure of an icmp header. */ struct icmp { u_char icmp_type; /* type of message, see below */ u_char icmp_code; /* type sub code */ u_short icmp_cksum; /* ones complement cksum of struct */ union { u_char ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ struct ih_idseq { n_short icd_id; n_short icd_seq; } ih_idseq; int ih_void; /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ struct ih_pmtu { n_short ipm_void; n_short ipm_nextmtu; } ih_pmtu; } icmp_hun; #define icmp_pptr icmp_hun.ih_pptr #define icmp_gwaddr icmp_hun.ih_gwaddr #define icmp_id icmp_hun.ih_idseq.icd_id #define icmp_seq icmp_hun.ih_idseq.icd_seq #define icmp_void icmp_hun.ih_void #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu union { struct id_ts { n_time its_otime; n_time its_rtime; n_time its_ttime; } id_ts; struct id_ip { struct ip idi_ip; /* options and then 64 bits of data */ } id_ip; u_long id_mask; char id_data[1]; } icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime #define icmp_ttime icmp_dun.id_ts.its_ttime #define icmp_ip icmp_dun.id_ip.idi_ip #define icmp_mask icmp_dun.id_mask #define icmp_data icmp_dun.id_data }; /* * Lower bounds on packet lengths for various types. * For the error advice packets must first insure that the * packet is large enought to contain the returned ip header. * Only then can we do the check to see if 64 bits of packet * data have been returned, since we need to check the returned * ip header length. */ #define ICMP_MINLEN 8 /* abs minimum */ #define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */ #define ICMP_MASKLEN 12 /* address mask */ #define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ #define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) /* N.B.: must separately check that ip_hl >= 5 */ /* * Definition of type and code field values. */ #define ICMP_ECHOREPLY 0 /* echo reply */ #define ICMP_UNREACH 3 /* dest unreachable, codes: */ #define ICMP_UNREACH_NET 0 /* bad net */ #define ICMP_UNREACH_HOST 1 /* bad host */ #define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ #define ICMP_UNREACH_PORT 3 /* bad port */ #define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ #define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ #define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ #define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ #define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ #define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ #define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ #define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ #define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ #define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ #define ICMP_REDIRECT 5 /* shorter route, codes: */ #define ICMP_REDIRECT_NET 0 /* for network */ #define ICMP_REDIRECT_HOST 1 /* for host */ #define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ #define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ #define ICMP_ECHO 8 /* echo service */ #define ICMP_ROUTERADVERT 9 /* router advertisement */ #define ICMP_ROUTERSOLICIT 10 /* router solicitation */ #define ICMP_TIMXCEED 11 /* time exceeded, code: */ #define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ #define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ #define ICMP_PARAMPROB 12 /* ip header bad */ #define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ #define ICMP_TSTAMP 13 /* timestamp request */ #define ICMP_TSTAMPREPLY 14 /* timestamp reply */ #define ICMP_IREQ 15 /* information request */ #define ICMP_IREQREPLY 16 /* information reply */ #define ICMP_MASKREQ 17 /* address mask request */ #define ICMP_MASKREPLY 18 /* address mask reply */ #define ICMP_MAXTYPE 18 #define ICMP_INFOTYPE(type) \ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) #ifdef KERNEL void icmp_error __P((struct mbuf *, int, int, n_long, struct ifnet *)); void icmp_input __P((struct mbuf *, int)); void icmp_reflect __P((struct mbuf *)); void icmp_send __P((struct mbuf *, struct mbuf *)); int icmp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); #endif lft-3.98/include/netinet/tcpip.h000755 000765 000024 00000004734 11053131665 016563 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcpip.h 8.1 (Berkeley) 6/10/93 */ /* * Tcp+ip header, after ip options removed. */ struct tcpiphdr { struct ipovly ti_i; /* overlaid ip structure */ struct tcphdr ti_t; /* tcp header */ }; #define ti_next ti_i.ih_next #define ti_prev ti_i.ih_prev #define ti_x1 ti_i.ih_x1 #define ti_pr ti_i.ih_pr #define ti_len ti_i.ih_len #define ti_src ti_i.ih_src #define ti_dst ti_i.ih_dst #define ti_sport ti_t.th_sport #define ti_dport ti_t.th_dport #define ti_seq ti_t.th_seq #define ti_ack ti_t.th_ack #define ti_x2 ti_t.th_x2 #define ti_off ti_t.th_off #define ti_flags ti_t.th_flags #define ti_win ti_t.th_win #define ti_sum ti_t.th_sum #define ti_urp ti_t.th_urp lft-3.98/include/netinet/tcp_var.h000755 000765 000024 00000027663 11053131665 017110 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_var.h 8.4 (Berkeley) 5/24/95 */ /* * Kernel variables for tcp. */ /* * Tcp control block, one per tcp; fields: */ struct tcpcb { struct tcpiphdr *seg_next; /* sequencing queue */ struct tcpiphdr *seg_prev; short t_state; /* state of this connection */ short t_timer[TCPT_NTIMERS]; /* tcp timers */ short t_rxtshift; /* log(2) of rexmt exp. backoff */ short t_rxtcur; /* current retransmit value */ short t_dupacks; /* consecutive dup acks recd */ u_short t_maxseg; /* maximum segment size */ char t_force; /* 1 if forcing out a byte */ u_short t_flags; #define TF_ACKNOW 0x0001 /* ack peer immediately */ #define TF_DELACK 0x0002 /* ack, but try to delay it */ #define TF_NODELAY 0x0004 /* don't delay packets to coalesce */ #define TF_NOOPT 0x0008 /* don't use tcp options */ #define TF_SENTFIN 0x0010 /* have sent FIN */ #define TF_REQ_SCALE 0x0020 /* have/will request window scaling */ #define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */ #define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */ #define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */ #define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */ struct tcpiphdr *t_template; /* skeletal packet for transmit */ struct inpcb *t_inpcb; /* back pointer to internet pcb */ /* * The following fields are used as in the protocol specification. * See RFC783, Dec. 1981, page 21. */ /* send sequence variables */ tcp_seq snd_una; /* send unacknowledged */ tcp_seq snd_nxt; /* send next */ tcp_seq snd_up; /* send urgent pointer */ tcp_seq snd_wl1; /* window update seg seq number */ tcp_seq snd_wl2; /* window update seg ack number */ tcp_seq iss; /* initial send sequence number */ u_long snd_wnd; /* send window */ /* receive sequence variables */ u_long rcv_wnd; /* receive window */ tcp_seq rcv_nxt; /* receive next */ tcp_seq rcv_up; /* receive urgent pointer */ tcp_seq irs; /* initial receive sequence number */ /* * Additional variables for this implementation. */ /* receive variables */ tcp_seq rcv_adv; /* advertised window */ /* retransmit variables */ tcp_seq snd_max; /* highest sequence number sent; * used to recognize retransmits */ /* congestion control (for slow start, source quench, retransmit after loss) */ u_long snd_cwnd; /* congestion-controlled window */ u_long snd_ssthresh; /* snd_cwnd size threshhold for * for slow start exponential to * linear switch */ /* * transmit timing stuff. See below for scale of srtt and rttvar. * "Variance" is actually smoothed difference. */ u_short t_idle; /* inactivity time */ short t_rtt; /* round trip time */ tcp_seq t_rtseq; /* sequence number being timed */ short t_srtt; /* smoothed round-trip time */ short t_rttvar; /* variance in round-trip time */ u_short t_rttmin; /* minimum rtt allowed */ u_long max_sndwnd; /* largest window peer has offered */ /* out-of-band data */ char t_oobflags; /* have some */ char t_iobc; /* input character */ #define TCPOOB_HAVEDATA 0x01 #define TCPOOB_HADDATA 0x02 short t_softerror; /* possible error not yet reported */ /* RFC 1323 variables */ u_char snd_scale; /* window scaling for send window */ u_char rcv_scale; /* window scaling for recv window */ u_char request_r_scale; /* pending window scaling */ u_char requested_s_scale; u_long ts_recent; /* timestamp echo data */ u_long ts_recent_age; /* when last updated */ tcp_seq last_ack_sent; /* TUBA stuff */ caddr_t t_tuba_pcb; /* next level down pcb for TCP over z */ }; #define intotcpcb(ip) ((struct tcpcb *)(ip)->inp_ppcb) #define sototcpcb(so) (intotcpcb(sotoinpcb(so))) /* * The smoothed round-trip time and estimated variance * are stored as fixed point numbers scaled by the values below. * For convenience, these scales are also used in smoothing the average * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed). * With these scales, srtt has 3 bits to the right of the binary point, * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the * binary point, and is smoothed with an ALPHA of 0.75. */ #define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */ #define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */ #define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */ #define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */ /* * The initial retransmission should happen at rtt + 4 * rttvar. * Because of the way we do the smoothing, srtt and rttvar * will each average +1/2 tick of bias. When we compute * the retransmit timer, we want 1/2 tick of rounding and * 1 extra tick because of +-1/2 tick uncertainty in the * firing of the timer. The bias will give us exactly the * 1.5 tick we need. But, because the bias is * statistical, we have to test that we don't drop below * the minimum feasible timer (which is 2 ticks). * This macro assumes that the value of TCP_RTTVAR_SCALE * is the same as the multiplier for rttvar. */ #define TCP_REXMTVAL(tp) \ (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar) /* XXX * We want to avoid doing m_pullup on incoming packets but that * means avoiding dtom on the tcp reassembly code. That in turn means * keeping an mbuf pointer in the reassembly queue (since we might * have a cluster). As a quick hack, the source & destination * port numbers (which are no longer needed once we've located the * tcpcb) are overlayed with an mbuf pointer. */ #define REASS_MBUF(ti) (*(struct mbuf **)&((ti)->ti_t)) /* * TCP statistics. * Many of these should be kept per connection, * but that's inconvenient at the moment. */ struct tcpstat { u_long tcps_connattempt; /* connections initiated */ u_long tcps_accepts; /* connections accepted */ u_long tcps_connects; /* connections established */ u_long tcps_drops; /* connections dropped */ u_long tcps_conndrops; /* embryonic connections dropped */ u_long tcps_closed; /* conn. closed (includes drops) */ u_long tcps_segstimed; /* segs where we tried to get rtt */ u_long tcps_rttupdated; /* times we succeeded */ u_long tcps_delack; /* delayed acks sent */ u_long tcps_timeoutdrop; /* conn. dropped in rxmt timeout */ u_long tcps_rexmttimeo; /* retransmit timeouts */ u_long tcps_persisttimeo; /* persist timeouts */ u_long tcps_keeptimeo; /* keepalive timeouts */ u_long tcps_keepprobe; /* keepalive probes sent */ u_long tcps_keepdrops; /* connections dropped in keepalive */ u_long tcps_sndtotal; /* total packets sent */ u_long tcps_sndpack; /* data packets sent */ u_long tcps_sndbyte; /* data bytes sent */ u_long tcps_sndrexmitpack; /* data packets retransmitted */ u_long tcps_sndrexmitbyte; /* data bytes retransmitted */ u_long tcps_sndacks; /* ack-only packets sent */ u_long tcps_sndprobe; /* window probes sent */ u_long tcps_sndurg; /* packets sent with URG only */ u_long tcps_sndwinup; /* window update-only packets sent */ u_long tcps_sndctrl; /* control (SYN|FIN|RST) packets sent */ u_long tcps_rcvtotal; /* total packets received */ u_long tcps_rcvpack; /* packets received in sequence */ u_long tcps_rcvbyte; /* bytes received in sequence */ u_long tcps_rcvbadsum; /* packets received with ccksum errs */ u_long tcps_rcvbadoff; /* packets received with bad offset */ u_long tcps_rcvshort; /* packets received too short */ u_long tcps_rcvduppack; /* duplicate-only packets received */ u_long tcps_rcvdupbyte; /* duplicate-only bytes received */ u_long tcps_rcvpartduppack; /* packets with some duplicate data */ u_long tcps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */ u_long tcps_rcvoopack; /* out-of-order packets received */ u_long tcps_rcvoobyte; /* out-of-order bytes received */ u_long tcps_rcvpackafterwin; /* packets with data after window */ u_long tcps_rcvbyteafterwin; /* bytes rcvd after window */ u_long tcps_rcvafterclose; /* packets rcvd after "close" */ u_long tcps_rcvwinprobe; /* rcvd window probe packets */ u_long tcps_rcvdupack; /* rcvd duplicate acks */ u_long tcps_rcvacktoomuch; /* rcvd acks for unsent data */ u_long tcps_rcvackpack; /* rcvd ack packets */ u_long tcps_rcvackbyte; /* bytes acked by rcvd acks */ u_long tcps_rcvwinupd; /* rcvd window update packets */ u_long tcps_pawsdrop; /* segments dropped due to PAWS */ u_long tcps_predack; /* times hdr predict ok for acks */ u_long tcps_preddat; /* times hdr predict ok for data pkts */ u_long tcps_pcbcachemiss; u_long tcps_persistdrop; /* timeout in persist state */ u_long tcps_badsyn; /* bogus SYN, e.g. premature ACK */ }; #ifdef KERNEL struct inpcb tcb; /* head of queue of active tcpcb's */ struct tcpstat tcpstat; /* tcp statistics */ u_long tcp_now; /* for RFC 1323 timestamps */ int tcp_attach __P((struct socket *)); void tcp_canceltimers __P((struct tcpcb *)); struct tcpcb * tcp_close __P((struct tcpcb *)); void tcp_ctlinput __P((int, struct sockaddr *, struct ip *)); int tcp_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); struct tcpcb * tcp_disconnect __P((struct tcpcb *)); struct tcpcb * tcp_drop __P((struct tcpcb *, int)); void tcp_dooptions __P((struct tcpcb *, u_char *, int, struct tcpiphdr *, int *, u_long *, u_long *)); void tcp_drain __P((void)); void tcp_fasttimo __P((void)); void tcp_init __P((void)); void tcp_input __P((struct mbuf *, int)); int tcp_mss __P((struct tcpcb *, u_int)); struct tcpcb * tcp_newtcpcb __P((struct inpcb *)); void tcp_notify __P((struct inpcb *, int)); int tcp_output __P((struct tcpcb *)); void tcp_pulloutofband __P((struct socket *, struct tcpiphdr *, struct mbuf *)); void tcp_quench __P((struct inpcb *, int)); int tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *)); void tcp_respond __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *, u_long, u_long, int)); void tcp_setpersist __P((struct tcpcb *)); void tcp_slowtimo __P((void)); struct tcpiphdr * tcp_template __P((struct tcpcb *)); struct tcpcb * tcp_timers __P((struct tcpcb *, int)); void tcp_trace __P((int, int, struct tcpcb *, struct tcpiphdr *, int)); struct tcpcb * tcp_usrclosed __P((struct tcpcb *)); int tcp_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *)); void tcp_xmit_timer __P((struct tcpcb *, int)); #endif lft-3.98/include/netinet/if_ether.h000755 000765 000024 00000006352 11053131665 017227 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_ether.h 8.3 (Berkeley) 5/2/95 */ /* * Ethernet address - 6 octets */ struct ether_addr { u_char ether_addr_octet[6]; }; /* * Structure of a 10Mb/s Ethernet header. */ struct ether_header { u_char ether_dhost[6]; u_char ether_shost[6]; u_short ether_type; }; #define ETHERTYPE_PUP 0x0200 /* PUP protocol */ #define ETHERTYPE_IP 0x0800 /* IP protocol */ #define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */ #define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */ /* * The ETHERTYPE_NTRAILER packet types starting at ETHERTYPE_TRAIL have * (type-ETHERTYPE_TRAIL)*512 bytes of data followed * by an ETHER type (as given above) and then the (variable-length) header. */ #define ETHERTYPE_TRAIL 0x1000 /* Trailer packet */ #define ETHERTYPE_NTRAILER 16 #define ETHERMTU 1500 #define ETHERMIN (60-14) /* * Ethernet Address Resolution Protocol. * * See RFC 826 for protocol description. Structure below is adapted * to resolving internet addresses. Field names used correspond to * RFC 826. */ struct ether_arp { struct arphdr ea_hdr; /* fixed-size header */ u_char arp_sha[6]; /* sender hardware address */ u_char arp_spa[4]; /* sender protocol address */ u_char arp_tha[6]; /* target hardware address */ u_char arp_tpa[4]; /* target protocol address */ }; #define arp_hrd ea_hdr.ar_hrd #define arp_pro ea_hdr.ar_pro #define arp_hln ea_hdr.ar_hln #define arp_pln ea_hdr.ar_pln #define arp_op ea_hdr.ar_op lft-3.98/include/netinet/udp_var.h000755 000765 000024 00000006774 11053131665 017112 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 */ /* * UDP kernel structures and variables. */ struct udpiphdr { struct ipovly ui_i; /* overlaid ip structure */ struct udphdr ui_u; /* udp header */ }; #define ui_next ui_i.ih_next #define ui_prev ui_i.ih_prev #define ui_x1 ui_i.ih_x1 #define ui_pr ui_i.ih_pr #define ui_len ui_i.ih_len #define ui_src ui_i.ih_src #define ui_dst ui_i.ih_dst #define ui_sport ui_u.uh_sport #define ui_dport ui_u.uh_dport #define ui_ulen ui_u.uh_ulen #define ui_sum ui_u.uh_sum struct udpstat { /* input statistics: */ u_long udps_ipackets; /* total input packets */ u_long udps_hdrops; /* packet shorter than header */ u_long udps_badsum; /* checksum error */ u_long udps_badlen; /* data length larger than packet */ u_long udps_noport; /* no socket on port */ u_long udps_noportbcast; /* of above, arrived as broadcast */ u_long udps_fullsock; /* not delivered, input socket full */ u_long udpps_pcbcachemiss; /* input packets missing pcb cache */ /* output statistics: */ u_long udps_opackets; /* total output packets */ }; /* * Names for UDP sysctl objects */ #define UDPCTL_CHECKSUM 1 /* checksum UDP packets */ #define UDPCTL_MAXID 2 #define UDPCTL_NAMES { \ { 0, 0 }, \ { "checksum", CTLTYPE_INT }, \ } #ifdef KERNEL struct inpcb udb; struct udpstat udpstat; void udp_ctlinput __P((int, struct sockaddr *, struct ip *)); void udp_init __P((void)); void udp_input __P((struct mbuf *, int)); int udp_output __P((struct inpcb *, struct mbuf *, struct mbuf *, struct mbuf *)); int udp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); int udp_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *)); #endif lft-3.98/include/netinet/ip.h000755 000765 000024 00000013215 11053131665 016046 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip.h 8.2 (Berkeley) 6/1/94 */ /* * Definitions for internet protocol version 4. * Per RFC 791, September 1981. */ #define IPVERSION 4 /* * Structure of an internet header, naked of options. * * We declare ip_len and ip_off to be short, rather than u_short * pragmatically since otherwise unsigned comparisons can result * against negative integers quite easily, and fail in subtle ways. */ struct ip { #if BYTE_ORDER == LITTLE_ENDIAN u_char ip_hl:4, /* header length */ ip_v:4; /* version */ #endif #if BYTE_ORDER == BIG_ENDIAN u_char ip_v:4, /* version */ ip_hl:4; /* header length */ #endif u_char ip_tos; /* type of service */ short ip_len; /* total length */ u_short ip_id; /* identification */ short ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; #define IP_MAXPACKET 65535 /* maximum packet size */ /* * Definitions for IP type of service (ip_tos) */ #define IPTOS_LOWDELAY 0x10 #define IPTOS_THROUGHPUT 0x08 #define IPTOS_RELIABILITY 0x04 /* * Definitions for IP precedence (also in ip_tos) (hopefully unused) */ #define IPTOS_PREC_NETCONTROL 0xe0 #define IPTOS_PREC_INTERNETCONTROL 0xc0 #define IPTOS_PREC_CRITIC_ECP 0xa0 #define IPTOS_PREC_FLASHOVERRIDE 0x80 #define IPTOS_PREC_FLASH 0x60 #define IPTOS_PREC_IMMEDIATE 0x40 #define IPTOS_PREC_PRIORITY 0x20 #define IPTOS_PREC_ROUTINE 0x00 /* * Definitions for options. */ #define IPOPT_COPIED(o) ((o)&0x80) #define IPOPT_CLASS(o) ((o)&0x60) #define IPOPT_NUMBER(o) ((o)&0x1f) #define IPOPT_CONTROL 0x00 #define IPOPT_RESERVED1 0x20 #define IPOPT_DEBMEAS 0x40 #define IPOPT_RESERVED2 0x60 #define IPOPT_EOL 0 /* end of option list */ #define IPOPT_NOP 1 /* no operation */ #define IPOPT_RR 7 /* record packet route */ #define IPOPT_TS 68 /* timestamp */ #define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ #define IPOPT_LSRR 131 /* loose source route */ #define IPOPT_SATID 136 /* satnet id */ #define IPOPT_SSRR 137 /* strict source route */ /* * Offsets to fields in options other than EOL and NOP. */ #define IPOPT_OPTVAL 0 /* option ID */ #define IPOPT_OLEN 1 /* option length */ #define IPOPT_OFFSET 2 /* offset within option */ #define IPOPT_MINOFF 4 /* min value of above */ /* * Time stamp option structure. */ struct ip_timestamp { u_char ipt_code; /* IPOPT_TS */ u_char ipt_len; /* size of structure (variable) */ u_char ipt_ptr; /* index of current entry */ #if BYTE_ORDER == LITTLE_ENDIAN u_char ipt_flg:4, /* flags, see below */ ipt_oflw:4; /* overflow counter */ #endif #if BYTE_ORDER == BIG_ENDIAN u_char ipt_oflw:4, /* overflow counter */ ipt_flg:4; /* flags, see below */ #endif union ipt_timestamp { n_long ipt_time[1]; struct ipt_ta { struct in_addr ipt_addr; n_long ipt_time; } ipt_ta[1]; } ipt_timestamp; }; /* flag bits for ipt_flg */ #define IPOPT_TS_TSONLY 0 /* timestamps only */ #define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ #define IPOPT_TS_PRESPEC 3 /* specified modules only */ /* bits for security (not byte swapped) */ #define IPOPT_SECUR_UNCLASS 0x0000 #define IPOPT_SECUR_CONFID 0xf135 #define IPOPT_SECUR_EFTO 0x789a #define IPOPT_SECUR_MMMM 0xbc4d #define IPOPT_SECUR_RESTR 0xaf13 #define IPOPT_SECUR_SECRET 0xd788 #define IPOPT_SECUR_TOPSECRET 0x6bc5 /* * Internet implementation parameters. */ #define MAXTTL 255 /* maximum time to live (seconds) */ #define IPDEFTTL 64 /* default ttl, from RFC 1340 */ #define IPFRAGTTL 60 /* time to live for frags, slowhz */ #define IPTTLDEC 1 /* subtracted when forwarding */ #define IP_MSS 576 /* default maximum segment size */ lft-3.98/include/netinet/in_systm.h000755 000765 000024 00000004662 11053131665 017311 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_systm.h 8.1 (Berkeley) 6/10/93 */ /* * Miscellaneous internetwork * definitions for kernel. */ /* * Network types. * * Internally the system keeps counters in the headers with the bytes * swapped so that VAX instructions will work on them. It reverses * the bytes before transmission at each protocol level. The n_ types * represent the types with the bytes in ``high-ender'' order. */ typedef u_short n_short; /* short as received from the net */ typedef u_long n_long; /* long as received from the net */ typedef u_long n_time; /* ms since 00:00 GMT, byte rev */ #ifdef KERNEL n_time iptime __P((void)); #endif lft-3.98/include/netinet/ip_var.h000755 000765 000024 00000015652 11053131665 016725 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_var.h 8.2 (Berkeley) 1/9/95 */ #include /* * Overlay for ip header used by other protocols (tcp, udp). */ struct ipovly { caddr_t ih_next, ih_prev; /* for protocol sequence q's */ u_char ih_x1; /* (unused) */ u_char ih_pr; /* protocol */ short ih_len; /* protocol length */ struct in_addr ih_src; /* source internet address */ struct in_addr ih_dst; /* destination internet address */ }; /* * Ip reassembly queue structure. Each fragment * being reassembled is attached to one of these structures. * They are timed out after ipq_ttl drops to 0, and may also * be reclaimed if memory becomes tight. */ struct ipq { struct ipq *next,*prev; /* to other reass headers */ u_char ipq_ttl; /* time for reass q to live */ u_char ipq_p; /* protocol of this fragment */ u_short ipq_id; /* sequence id for reassembly */ struct ipasfrag *ipq_next,*ipq_prev; /* to ip headers of fragments */ struct in_addr ipq_src,ipq_dst; }; /* * Ip header, when holding a fragment. * * Note: ipf_next must be at same offset as ipq_next above */ struct ipasfrag { #if BYTE_ORDER == LITTLE_ENDIAN u_char ip_hl:4, ip_v:4; #endif #if BYTE_ORDER == BIG_ENDIAN u_char ip_v:4, ip_hl:4; #endif u_char ipf_mff; /* XXX overlays ip_tos: use low bit * to avoid destroying tos; * copied from (ip_off&IP_MF) */ short ip_len; u_short ip_id; short ip_off; u_char ip_ttl; u_char ip_p; u_short ip_sum; struct ipasfrag *ipf_next; /* next fragment */ struct ipasfrag *ipf_prev; /* previous fragment */ }; /* * Structure stored in mbuf in inpcb.ip_options * and passed to ip_output when ip options are in use. * The actual length of the options (including ipopt_dst) * is in m_len. */ #define MAX_IPOPTLEN 40 struct ipoption { struct in_addr ipopt_dst; /* first-hop dst if source routed */ char ipopt_list[MAX_IPOPTLEN]; /* options proper */ }; struct ipstat { u_long ips_total; /* total packets received */ u_long ips_badsum; /* checksum bad */ u_long ips_tooshort; /* packet too short */ u_long ips_toosmall; /* not enough data */ u_long ips_badhlen; /* ip header length < data size */ u_long ips_badlen; /* ip length < ip header length */ u_long ips_fragments; /* fragments received */ u_long ips_fragdropped; /* frags dropped (dups, out of space) */ u_long ips_fragtimeout; /* fragments timed out */ u_long ips_forward; /* packets forwarded */ u_long ips_cantforward; /* packets rcvd for unreachable dest */ u_long ips_redirectsent; /* packets forwarded on same net */ u_long ips_noproto; /* unknown or unsupported protocol */ u_long ips_delivered; /* datagrams delivered to upper level*/ u_long ips_localout; /* total ip packets generated here */ u_long ips_odropped; /* lost packets due to nobufs, etc. */ u_long ips_reassembled; /* total packets reassembled ok */ u_long ips_fragmented; /* datagrams sucessfully fragmented */ u_long ips_ofragments; /* output fragments created */ u_long ips_cantfrag; /* don't fragment flag was set, etc. */ u_long ips_badoptions; /* error in option processing */ u_long ips_noroute; /* packets discarded due to no route */ u_long ips_badvers; /* ip version != 4 */ u_long ips_rawout; /* total raw ip packets generated */ }; #ifdef KERNEL /* flags passed to ip_output as last parameter */ #define IP_FORWARDING 0x1 /* most of ip header exists */ #define IP_RAWOUTPUT 0x2 /* raw ip header exists */ #define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */ #define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */ struct ipstat ipstat; struct ipq ipq; /* ip reass. queue */ u_short ip_id; /* ip packet ctr, for ids */ int ip_defttl; /* default IP ttl */ int in_control __P((struct socket *, u_long, caddr_t, struct ifnet *)); int ip_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); void ip_deq __P((struct ipasfrag *)); int ip_dooptions __P((struct mbuf *)); void ip_drain __P((void)); void ip_enq __P((struct ipasfrag *, struct ipasfrag *)); void ip_forward __P((struct mbuf *, int)); void ip_freef __P((struct ipq *)); void ip_freemoptions __P((struct ip_moptions *)); int ip_getmoptions __P((int, struct ip_moptions *, struct mbuf **)); void ip_init __P((void)); int ip_mforward __P((struct mbuf *, struct ifnet *)); int ip_optcopy __P((struct ip *, struct ip *)); int ip_output __P((struct mbuf *, struct mbuf *, struct route *, int, struct ip_moptions *)); int ip_pcbopts __P((struct mbuf **, struct mbuf *)); struct ip * ip_reass __P((struct ipasfrag *, struct ipq *)); struct in_ifaddr * ip_rtaddr __P((struct in_addr)); int ip_setmoptions __P((int, struct ip_moptions **, struct mbuf *)); void ip_slowtimo __P((void)); struct mbuf * ip_srcroute __P((void)); void ip_stripoptions __P((struct mbuf *, struct mbuf *)); int ip_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); void ipintr __P((void)); int rip_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); void rip_init __P((void)); void rip_input __P((struct mbuf *)); int rip_output __P((struct mbuf *, struct socket *, u_long)); int rip_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *)); #endif lft-3.98/include/netinet/udp.h000755 000765 000024 00000004101 11053131665 016220 0ustar00vicstaff000000 000000 /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)udp.h 8.1 (Berkeley) 6/10/93 */ /* * Udp protocol header. * Per RFC 768, September, 1981. */ struct udphdr { u_short uh_sport; /* source port */ u_short uh_dport; /* destination port */ short uh_ulen; /* udp length */ u_short uh_sum; /* udp checksum */ }; lft-3.98/include/net/slip.h000755 000765 000024 00000002632 11053131665 015526 0ustar00vicstaff000000 000000 /* * Definitions that user level programs might need to know to interact * with serial line IP (slip) lines. * * Copyright (c) 1990 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * ioctl to get slip interface unit number (e.g., sl0, sl1, etc.) * assigned to some terminal line with a slip module pushed on it. */ #ifdef __STDC__ #define SLIOGUNIT _IOR('B', 1, int) #else #define SLIOGUNIT _IOR(B, 1, int) #endif /* * definitions of the pseudo- link-level header attached to slip * packets grabbed by the packet filter (bpf) traffic monitor. */ #define SLIP_HDRLEN 16 #define SLX_DIR 0 #define SLX_CHDR 1 #define CHDR_LEN 15 #define SLIPDIR_IN 0 #define SLIPDIR_OUT 1 lft-3.98/include/net/slcompress.h000755 000765 000024 00000013166 11053131665 016755 0ustar00vicstaff000000 000000 /* * Definitions for tcp compression routines. * * Copyright (c) 1989, 1990, 1992, 1993 Regents of the University of * California. All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Van Jacobson (van@ee.lbl.gov), Dec 31, 1989: * - Initial distribution. */ #define MAX_STATES 16 /* must be > 2 and < 256 */ #define MAX_HDR MLEN /* XXX 4bsd-ism: should really be 128 */ /* * Compressed packet format: * * The first octet contains the packet type (top 3 bits), TCP * 'push' bit, and flags that indicate which of the 4 TCP sequence * numbers have changed (bottom 5 bits). The next octet is a * conversation number that associates a saved IP/TCP header with * the compressed packet. The next two octets are the TCP checksum * from the original datagram. The next 0 to 15 octets are * sequence number changes, one change per bit set in the header * (there may be no changes and there are two special cases where * the receiver implicitly knows what changed -- see below). * * There are 5 numbers which can change (they are always inserted * in the following order): TCP urgent pointer, window, * acknowlegement, sequence number and IP ID. (The urgent pointer * is different from the others in that its value is sent, not the * change in value.) Since typical use of SLIP links is biased * toward small packets (see comments on MTU/MSS below), changes * use a variable length coding with one octet for numbers in the * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the * range 256 - 65535 or 0. (If the change in sequence number or * ack is more than 65535, an uncompressed packet is sent.) */ /* * Packet types (must not conflict with IP protocol version) * * The top nibble of the first octet is the packet type. There are * three possible types: IP (not proto TCP or tcp with one of the * control flags set); uncompressed TCP (a normal IP/TCP packet but * with the 8-bit protocol field replaced by an 8-bit connection id -- * this type of packet syncs the sender & receiver); and compressed * TCP (described above). * * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and * is logically part of the 4-bit "changes" field that follows. Top * three bits are actual packet type. For backward compatibility * and in the interest of conserving bits, numbers are chosen so the * IP protocol version number (4) which normally appears in this nibble * means "IP packet". */ /* packet types */ #define TYPE_IP 0x40 #define TYPE_UNCOMPRESSED_TCP 0x70 #define TYPE_COMPRESSED_TCP 0x80 #define TYPE_ERROR 0x00 /* Bits in first octet of compressed packet */ #define NEW_C 0x40 /* flag bits for what changed in a packet */ #define NEW_I 0x20 #define NEW_S 0x08 #define NEW_A 0x04 #define NEW_W 0x02 #define NEW_U 0x01 /* reserved, special-case values of above */ #define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ #define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) #define TCP_PUSH_BIT 0x10 /* * "state" data for each active tcp conversation on the wire. This is * basically a copy of the entire IP/TCP header from the last packet * we saw from the conversation together with a small identifier * the transmit & receive ends of the line use to locate saved header. */ struct cstate { struct cstate *cs_next; /* next most recently used cstate (xmit only) */ u_short cs_hlen; /* size of hdr (receive only) */ u_char cs_id; /* connection # associated with this state */ u_char cs_filler; union { char csu_hdr[MAX_HDR]; struct ip csu_ip; /* ip/tcp hdr from most recent packet */ } slcs_u; }; #define cs_ip slcs_u.csu_ip #define cs_hdr slcs_u.csu_hdr /* * all the state data for one serial line (we need one of these * per line). */ struct slcompress { struct cstate *last_cs; /* most recently used tstate */ u_char last_recv; /* last rcvd conn. id */ u_char last_xmit; /* last sent conn. id */ u_short flags; #ifndef SL_NO_STATS u_int sls_packets; /* outbound packets */ u_int sls_compressed; /* outbound compressed packets */ u_int sls_searches; /* searches for connection state */ u_int sls_misses; /* times couldn't find conn. state */ u_int sls_uncompressedin;/* inbound uncompressed packets */ u_int sls_compressedin; /* inbound compressed packets */ u_int sls_errorin; /* inbound unknown type packets */ u_int sls_tossed; /* inbound packets tossed because of error */ #endif struct cstate tstate[MAX_STATES]; /* xmit connection states */ struct cstate rstate[MAX_STATES]; /* receive connection states */ }; /* flag values */ #define SLF_TOSS 1 /* tossing rcvd frames because of input err */ #ifdef KERNEL #ifdef __STDC__ extern void sl_compress_init(struct slcompress *); extern u_char sl_compress_tcp(struct mbuf *, struct ip *, struct slcompress *); extern int sl_uncompress_tcp(struct mbuf *, int, u_int, struct slcompress *); #else extern void sl_compress_init(); extern u_char sl_compress_tcp(); extern int sl_uncompress_tcp(); #endif #endif lft-3.98/include/net/if_arp.h000755 000765 000024 00000015037 11053131665 016022 0ustar00vicstaff000000 000000 /* Definitions for Address Resolution Protocol. Copyright (C) 1997, 1999, 2001 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 1997. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* Based on the 4.4BSD and Linux version of this file. */ #ifndef _NET_IF_ARP_H #define _NET_IF_ARP_H 1 /* Some internals from deep down in the kernel. */ #define MAX_ADDR_LEN 7 /* This structure defines an ethernet arp header. */ /* ARP protocol opcodes. */ #define ARPOP_REQUEST 1 /* ARP request. */ #define ARPOP_REPLY 2 /* ARP reply. */ #define ARPOP_RREQUEST 3 /* RARP request. */ #define ARPOP_RREPLY 4 /* RARP reply. */ #define ARPOP_InREQUEST 8 /* InARP request. */ #define ARPOP_InREPLY 9 /* InARP reply. */ #define ARPOP_NAK 10 /* (ATM)ARP NAK. */ /* See RFC 826 for protocol description. ARP packets are variable in size; the arphdr structure defines the fixed-length portion. Protocol type values are the same as those for 10 Mb/s Ethernet. It is followed by the variable-sized fields ar_sha, arp_spa, arp_tha and arp_tpa in that order, according to the lengths specified. Field names used correspond to RFC 826. */ struct arphdr { unsigned short int ar_hrd; /* Format of hardware address. */ unsigned short int ar_pro; /* Format of protocol address. */ unsigned char ar_hln; /* Length of hardware address. */ unsigned char ar_pln; /* Length of protocol address. */ unsigned short int ar_op; /* ARP opcode (command). */ #if 0 /* Ethernet looks like this : This bit is variable sized however... */ unsigned char __ar_sha[ETH_ALEN]; /* Sender hardware address. */ unsigned char __ar_sip[4]; /* Sender IP address. */ unsigned char __ar_tha[ETH_ALEN]; /* Target hardware address. */ unsigned char __ar_tip[4]; /* Target IP address. */ #endif }; /* ARP protocol HARDWARE identifiers. */ #define ARPHRD_NETROM 0 /* From KA9Q: NET/ROM pseudo. */ #define ARPHRD_ETHER 1 /* Ethernet 10/100Mbps. */ #define ARPHRD_EETHER 2 /* Experimental Ethernet. */ #define ARPHRD_AX25 3 /* AX.25 Level 2. */ #define ARPHRD_PRONET 4 /* PROnet token ring. */ #define ARPHRD_CHAOS 5 /* Chaosnet. */ #define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB. */ #define ARPHRD_ARCNET 7 /* ARCnet. */ #define ARPHRD_APPLETLK 8 /* APPLEtalk. */ #define ARPHRD_DLCI 15 /* Frame Relay DLCI. */ #define ARPHRD_ATM 19 /* ATM. */ #define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id). */ /* Dummy types for non ARP hardware */ #define ARPHRD_SLIP 256 #define ARPHRD_CSLIP 257 #define ARPHRD_SLIP6 258 #define ARPHRD_CSLIP6 259 #define ARPHRD_RSRVD 260 /* Notional KISS type. */ #define ARPHRD_ADAPT 264 #define ARPHRD_ROSE 270 #define ARPHRD_X25 271 /* CCITT X.25. */ #define ARPHDR_HWX25 272 /* Boards with X.25 in firmware. */ #define ARPHRD_PPP 512 #define ARPHRD_CISCO 513 /* Cisco HDLC. */ #define ARPHRD_HDLC ARPHRD_CISCO #define ARPHRD_LAPB 516 /* LAPB. */ #define ARPHRD_DDCMP 517 /* Digital's DDCMP. */ #define ARPHRD_RAWHDLC 518 /* Raw HDLC. */ #define ARPHRD_TUNNEL 768 /* IPIP tunnel. */ #define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel. */ #define ARPHRD_FRAD 770 /* Frame Relay Access Device. */ #define ARPHRD_SKIP 771 /* SKIP vif. */ #define ARPHRD_LOOPBACK 772 /* Loopback device. */ #define ARPHRD_LOCALTLK 773 /* Localtalk device. */ #define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface. */ #define ARPHRD_BIF 775 /* AP1000 BIF. */ #define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4. */ #define ARPHRD_IPDDP 777 /* IP-in-DDP tunnel. */ #define ARPHRD_IPGRE 778 /* GRE over IP. */ #define ARPHRD_PIMREG 779 /* PIMSM register interface. */ #define ARPHRD_HIPPI 780 /* High Performance Parallel I'face. */ #define ARPHRD_ASH 781 /* (Nexus Electronics) Ash. */ #define ARPHRD_ECONET 782 /* Acorn Econet. */ #define ARPHRD_IRDA 783 /* Linux-IrDA. */ #define ARPHRD_FCPP 784 /* Point to point fibrechanel. */ #define ARPHRD_FCAL 785 /* Fibrechanel arbitrated loop. */ #define ARPHRD_FCPL 786 /* Fibrechanel public loop. */ #define ARPHRD_FCPFABRIC 787 /* Fibrechanel fabric. */ #define ARPHRD_IEEE802_TR 800 /* Magic type ident for TR. */ #define ARPHRD_IEEE80211 801 /* IEEE 802.11. */ /* ARP ioctl request. */ struct arpreq { struct sockaddr arp_pa; /* Protocol address. */ struct sockaddr arp_ha; /* Hardware address. */ int arp_flags; /* Flags. */ struct sockaddr arp_netmask; /* Netmask (only for proxy arps). */ char arp_dev[16]; }; struct arpreq_old { struct sockaddr arp_pa; /* Protocol address. */ struct sockaddr arp_ha; /* Hardware address. */ int arp_flags; /* Flags. */ struct sockaddr arp_netmask; /* Netmask (only for proxy arps). */ }; /* ARP Flag values. */ #define ATF_COM 0x02 /* Completed entry (ha valid). */ #define ATF_PERM 0x04 /* Permanent entry. */ #define ATF_PUBL 0x08 /* Publish entry. */ #define ATF_USETRAILERS 0x10 /* Has requested trailers. */ #define ATF_NETMASK 0x20 /* Want to use a netmask (only for proxy entries). */ #define ATF_DONTPUB 0x40 /* Don't answer this addresses. */ #define ATF_MAGIC 0x80 /* Automatically added entry. */ /* Support for the user space arp daemon, arpd. */ #define ARPD_UPDATE 0x01 #define ARPD_LOOKUP 0x02 #define ARPD_FLUSH 0x03 struct arpd_request { unsigned short int req; /* Request type. */ u_int32_t ip; /* IP address of entry. */ unsigned long int dev; /* Device entry is tied to. */ unsigned long int stamp; unsigned long int updated; unsigned char ha[MAX_ADDR_LEN]; /* Hardware address. */ }; #endif /* net/if_arp.h */ lft-3.98/config/acconfig.h.in000644 000765 000024 00000012353 15173526114 015773 0ustar00vicstaff000000 000000 /* acconfig.h.in. */ /* Host system type */ #undef HOST_SYSTEM_TYPE /* Define if using alloca.c. */ #undef C_ALLOCA /* Define to empty if the keyword does not work. */ #undef const /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ #undef CRAY_STACKSEG_END /* Define if you have alloca, as a function or macro. */ #undef HAVE_ALLOCA /* Define if you have and it should be used (not on Ultrix). */ #undef HAVE_ALLOCA_H /* Define to `unsigned' if doesn't define. */ #undef size_t /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ #undef STACK_DIRECTION /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have alloca, as a function or macro. */ #define HAVE_ALLOCA 1 /* Define if you have and it should be used (not on Ultrix). */ #define HAVE_ALLOCA_H 1 /* Define to `unsigned' if doesn't define. */ /* #undef size_t */ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you can safely include both and . */ #define TIME_WITH_SYS_TIME 1 /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have alloca, as a function or macro. */ #define HAVE_ALLOCA 1 /* Define if you have and it should be used (not on Ultrix). */ #define HAVE_ALLOCA_H 1 /* Define to `unsigned' if doesn't define. */ /* #undef size_t */ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you can safely include both and . */ #define TIME_WITH_SYS_TIME 1 /* Linux requires this in order to have the right network structures. */ #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif /* Define if you have the gettimeofday function. */ #undef HAVE_GETTIMEOFDAY /* Define if you have the select function. */ #undef HAVE_SELECT /* Define if you have the socket function. */ #undef HAVE_SOCKET /* Define if you have the strdup function. */ #undef HAVE_STRDUP /* Define if you have the strstr function. */ #undef HAVE_STRSTR /* Define if you have the header file. */ #undef HAVE_FCNTL_H /* Define if you have the header file. */ #undef HAVE_LIMITS_H /* Define if you have the header file. */ #undef HAVE_STRINGS_H /* Define if you have the header file. */ #undef HAVE_SYS_IOCTL_H /* Define if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define if you have the header file. */ #undef HAVE_UNISTD_H /* Define if you have the m library (-lm). */ #undef HAVE_LIBM /* Define if you have the nsl library (-lnsl). */ #undef HAVE_LIBNSL /* Define if you have the pcap library (-lpcap). */ #undef HAVE_LIBPCAP /* Define if you have the socket library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define if this is a BSD TCP/IP stack */ #undef BSD_IP_STACK /* Define if this is Darwin */ #undef DARWIN /* Define if this is NetBSD */ #undef NETBSD /* Define if user enabled gettimeofday (GTOD) */ #undef USE_GTOD /* Define if building universal binaries */ #undef UNIVERSAL /* Define if this is OpenBSD */ #undef OPENBSD /* Define if solaris munges the th_sum field in its own special way */ #undef SOLARIS_LENGTH_IN_CHECKSUM /* Define if your system has a sa_len member in struct sockaddr */ #undef HAVE_SOCKADDR_SA_LEN /* Define if an IPv6 network stack is detected */ #undef INET6 /* Define to 1 if the c-ares async DNS library is available */ #undef HAVE_CARES /* Define to 1 if the ncurses library is available */ #undef HAVE_NCURSES lft-3.98/config/install-sh000755 000765 000024 00000036122 15165341545 015454 0ustar00vicstaff000000 000000 #!/bin/sh # install - install a program, script, or datafile scriptversion=2025-06-18.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Report bugs to . GNU Automake home page: . General help using GNU software: ." while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 (GNU Automake) $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibility with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp nil t) # time-stamp-start: "scriptversion=" # time-stamp-format: "%Y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: lft-3.98/config/configure.ac000644 000765 000024 00000047570 15173526114 015743 0ustar00vicstaff000000 000000 dnl Process this file with autoconf to produce a configure script. AC_INIT([lft],[3.93],[lft@oppleman.com]) AC_CONFIG_SRCDIR([lft_ifname.h]) AC_CONFIG_HEADERS([config/acconfig.h]) AC_CONFIG_AUX_DIR([config]) dnl This keeps configure quiet about the datarootdir setting AC_DEFUN([AC_DATAROOTDIR_CHECKED]) AC_CANONICAL_HOST case $host_os in *cygwin* ) CYGWIN=yes;; * ) CYGWIN=no;; esac AC_DEFINE_UNQUOTED([HOST_SYSTEM_TYPE], ["$host"], [Host system type triple]) dnl Checks for programs. AC_PROG_CC AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_INSTALL AC_CHECK_LIB([nsl], [getaddrinfo]) AC_CHECK_LIB([socket], [connect]) AC_CHECK_LIB([resolv], [inet_aton]) AC_CHECK_LIB([m], [sin]) dnl Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/ioctl.h sys/socket.h sys/time.h unistd.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T dnl Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_STRFTIME AC_CHECK_FUNCS([getaddrinfo getnameinfo gettimeofday inet_ntoa memset select setenv socket strchr strdup strstr]) dnl OS specific checks case "$host" in *darwin*) AC_DEFINE([BSD_IP_STACK], [1], [Define if using a BSD IP stack]) dnl we need to define DARWIN to turn off dnl some BSD-type features like BIOC_IMMEDIATE AC_DEFINE([DARWIN], [1], [Define if building on Darwin/macOS]) ;; *netbsd*) AC_DEFINE([BSD_IP_STACK], [1], [Define if using a BSD IP stack]) dnl we need to define NETBSD to turn off dnl some BSD-type features like BIOC_IMMEDIATE AC_DEFINE([NETBSD], [1], [Define if building on NetBSD]) ;; *openbsd*) AC_DEFINE([OPENBSD], [1], [Define if building on OpenBSD]) AC_DEFINE([BSD_IP_STACK], [1], [Define if using a BSD IP stack]) ;; *bsd*) AC_DEFINE([BSD_IP_STACK], [1], [Define if using a BSD IP stack]) ;; *linux*) AC_DEFINE([_BSD_SOURCE], [1], [Enable BSD extensions on Linux]) ;; *solaris*) dnl some versions of solaris need special treatment dnl for the th_sum field, and it varies between versions case "$host" in *solaris2.4*|*solaris2.5*) AC_DEFINE([SOLARIS_LENGTH_IN_CHECKSUM], [1], [Define if Solaris mangles th_sum]) ;; esac ;; esac case "$host" in *cygwin*) dnl Windows requires winsock; no pcap needed on this path LIBS="-lws2_32 $LIBS" ;; *) dnl Option to build universal binaries on macOS (Apple Silicon + Intel x86_64) AC_ARG_ENABLE([universal], [AS_HELP_STRING([--enable-universal], [build a universal binary for arm64 and x86_64 on macOS])], [UNIVERSAL="$enableval"], [UNIVERSAL="no"]) dnl Option to enable gettimeofday checks in addition to pcap header timestamp AC_ARG_ENABLE([gtod], [AS_HELP_STRING([--enable-gtod], [use gettimeofday() instead of pcap timestamps for per-packet timing])], [GTOD="$enableval"], [GTOD="no"]) dnl ------------------------------------------------------------------ dnl Build a list of candidate third-party prefix directories to probe dnl when pkg-config is unavailable or does not know about a library. dnl Order matters: first match wins. dnl /opt/homebrew -- Homebrew on Apple Silicon (default since 2021) dnl /usr/local -- Homebrew on Intel Mac; FreeBSD ports; common Linux dnl /opt/local -- MacPorts dnl /opt/pkg -- pkgsrc (NetBSD, some Linux) dnl ------------------------------------------------------------------ lft_candidate_prefixes="/opt/homebrew /usr/local /opt/local /opt/pkg" dnl Optional: manually specify the path to the pcap library pcap_path_override=no AC_ARG_WITH([pcap], [AS_HELP_STRING([--with-pcap=PATH], [specify prefix path to the pcap library (e.g. /usr/local)])], [ AC_MSG_CHECKING([for --with-pcap option]) case "$withval" in yes|no) AC_MSG_ERROR([please specify a PATH in --with-pcap option!]) ;; *) if test '!' -d "$withval"; then AC_MSG_ERROR([$withval does not exist!]) else AC_MSG_RESULT([$withval]) pcap_path_override=yes if test -d "$withval/include"; then CFLAGS="$CFLAGS -I$withval/include" CPPFLAGS="$CPPFLAGS -I$withval/include" else CFLAGS="$CFLAGS -I$withval" CPPFLAGS="$CPPFLAGS -I$withval" fi if test -d "$withval/lib"; then LIBS="$LIBS -L$withval/lib" else LIBS="$LIBS -L$withval" fi fi ;; esac ] ) dnl Check whether sockaddr has a sa_len member (BSD-derived stacks) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[u_int i = sizeof(((struct sockaddr *)0)->sa_len)]])], [AC_DEFINE([HAVE_SOCKADDR_SA_LEN], [1], [Define if struct sockaddr has a sa_len member])], []) dnl ------------------------------------------------------------------ dnl Detect libpcap. dnl dnl pcap_findalldevs() has been available since libpcap 0.7 (2002) and dnl is what lft_lib.c actually calls at runtime via lft_pcap_lookupdev(). dnl We check for it here instead of the deprecated pcap_lookupdev(), dnl which was removed in libpcap 1.10 (2021). dnl dnl Try pkg-config first when the user has not given us an explicit path dnl (preferred on modern Linux distros and FreeBSD with pkgconf installed), dnl then fall back to manual header + library detection. We use dnl AC_PATH_PROG + shell calls rather than PKG_CHECK_MODULES so that no dnl extra m4 macro files are needed when regenerating the configure script. dnl ------------------------------------------------------------------ have_pcap=no AS_IF([test "x$pcap_path_override" = "xno"], [AC_PATH_PROG([PKGCONFIG], [pkg-config], [no]) AS_IF([test "x$PKGCONFIG" != "xno"], [AC_MSG_CHECKING([for libpcap via pkg-config]) AS_IF([$PKGCONFIG --exists libpcap 2>/dev/null], [pcap_cflags=`$PKGCONFIG --cflags libpcap` pcap_libs=`$PKGCONFIG --libs libpcap` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $pcap_cflags" LIBS="$LIBS $pcap_libs" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[pcap_if_t *d; pcap_findalldevs(&d, 0);]])], [have_pcap=yes AC_MSG_RESULT([yes ($pcap_libs)])], [dnl Link failed (e.g. architecture mismatch); discard and fall through CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" AC_MSG_RESULT([no (link test failed; wrong architecture?)])])], [AC_MSG_RESULT([no])])])]) dnl If pkg-config didn't find pcap, probe candidate prefixes before giving up. AS_IF([test "x$have_pcap" = "xno" && test "x$pcap_path_override" = "xno"], [AC_MSG_CHECKING([for libpcap in candidate prefixes]) for _pfx in $lft_candidate_prefixes; do if test -f "$_pfx/include/pcap.h"; then CPPFLAGS="$CPPFLAGS -I$_pfx/include" CFLAGS="$CFLAGS -I$_pfx/include" LDFLAGS="$LDFLAGS -L$_pfx/lib" AC_MSG_RESULT([$_pfx]) break fi done AS_IF([test "x$_pfx" = "x"], [AC_MSG_RESULT([not found])])]) AS_IF([test "x$have_pcap" = "xno"], [AC_CHECK_LIB([pcap], [pcap_findalldevs], [have_pcap=yes; LIBS="-lpcap $LIBS"], [AC_MSG_ERROR([ libpcap was not found. It is required to build LFT. Please install the libpcap development library and re-run ./configure. On Debian/Ubuntu: sudo apt-get install libpcap-dev On RHEL/Fedora: sudo dnf install libpcap-devel On FreeBSD ports: net/libpcap On macOS Homebrew: brew install libpcap On macOS MacPorts: sudo port install libpcap Or specify its location with: ./configure --with-pcap=PATH])]) AC_CHECK_HEADER([pcap.h], , [AC_MSG_ERROR([ pcap.h was not found. The libpcap library was detected but its headers are missing. Please install the libpcap development package. On Debian/Ubuntu: sudo apt-get install libpcap-dev On RHEL/Fedora: sudo dnf install libpcap-devel On FreeBSD ports: net/libpcap On macOS Homebrew: brew install libpcap On macOS MacPorts: sudo port install libpcap Or specify its location with: ./configure --with-pcap=PATH When building libpcap from source, run both 'make install' and 'make install-incl'.])])]) dnl ------------------------------------------------------------------ dnl Detect c-ares for asynchronous DNS (optional; used by default if found). dnl dnl Detection order: dnl 1. --disable-async-dns skips all detection. dnl 2. --with-cares=PATH sets an explicit prefix (skips auto-detect). dnl 3. pkg-config libcares (most reliable on modern systems). dnl 4. Probe lft_candidate_prefixes for ares.h. dnl 5. AC_CHECK_LIB / AC_CHECK_HEADER in whatever paths are now set. dnl ------------------------------------------------------------------ AC_ARG_ENABLE([async-dns], [AS_HELP_STRING([--disable-async-dns], [disable asynchronous DNS via c-ares (default: auto-detect and use if available)])], [enable_async_dns="$enableval"], [enable_async_dns="yes"]) cares_path_override=no AC_ARG_WITH([cares], [AS_HELP_STRING([--with-cares=PATH], [specify prefix path to the c-ares library (e.g. /opt/homebrew)])], [case "$withval" in yes|no) AC_MSG_ERROR([please specify a PATH in --with-cares option!]) ;; *) if test '!' -d "$withval"; then AC_MSG_ERROR([$withval does not exist!]) fi cares_path_override=yes if test -d "$withval/include"; then CPPFLAGS="$CPPFLAGS -I$withval/include" CFLAGS="$CFLAGS -I$withval/include" else CPPFLAGS="$CPPFLAGS -I$withval" CFLAGS="$CFLAGS -I$withval" fi if test -d "$withval/lib"; then LDFLAGS="$LDFLAGS -L$withval/lib" else LDFLAGS="$LDFLAGS -L$withval" fi ;; esac]) AS_IF([test "x$enable_async_dns" = "xyes"], [have_cares=no dnl 1. Try pkg-config (skipped when user gave an explicit path) AS_IF([test "x$cares_path_override" = "xno"], [AS_IF([test "x$PKGCONFIG" != "xno"], [AC_MSG_CHECKING([for libcares via pkg-config]) AS_IF([$PKGCONFIG --exists libcares 2>/dev/null], [cares_cflags=`$PKGCONFIG --cflags libcares` cares_libs=`$PKGCONFIG --libs libcares` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $cares_cflags" LIBS="$LIBS $cares_libs" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ares_channel c; ares_init(&c);]])], [have_cares=yes AC_MSG_RESULT([yes ($cares_libs)])], [dnl Link failed (e.g. architecture mismatch); discard and fall through CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" AC_MSG_RESULT([no (link test failed; wrong architecture?)])])], [AC_MSG_RESULT([no])])])]) dnl 2. Probe candidate prefixes if still not found AS_IF([test "x$have_cares" = "xno" && test "x$cares_path_override" = "xno"], [AC_MSG_CHECKING([for c-ares in candidate prefixes]) for _pfx in $lft_candidate_prefixes; do if test -f "$_pfx/include/ares.h"; then CPPFLAGS="$CPPFLAGS -I$_pfx/include" CFLAGS="$CFLAGS -I$_pfx/include" LDFLAGS="$LDFLAGS -L$_pfx/lib" AC_MSG_RESULT([$_pfx]) break fi done AS_IF([test "x$_pfx" = "x"], [AC_MSG_RESULT([not found])])]) dnl 3. Final check with whatever paths are now set AS_IF([test "x$have_cares" = "xno"], [AC_CHECK_LIB([cares], [ares_init], [have_cares=maybe], []) AS_IF([test "x$have_cares" = "xmaybe"], [AC_CHECK_HEADER([ares.h], [have_cares=yes LIBS="-lcares $LIBS" AC_DEFINE([HAVE_CARES], [1], [Define to 1 if the c-ares async DNS library is available])], [AC_MSG_WARN([c-ares library found but ares.h not found; disabling async DNS])])])]) AS_IF([test "x$have_cares" = "xyes"], [AS_IF([test "x$cares_path_override" = "xyes" || test "x$PKGCONFIG" = "xno"], [dnl pkg-config not used — ensure LIBS has -lcares if not already added AS_CASE([$LIBS], [*-lcares*], [], [LIBS="-lcares $LIBS" AC_DEFINE([HAVE_CARES], [1], [Define to 1 if the c-ares async DNS library is available])])])]) AS_IF([test "x$have_cares" != "xyes"], [lft_warn_no_cares=yes])]) dnl ------------------------------------------------------------------ dnl Detect ncurses for interactive continuous-monitoring mode (optional). dnl dnl Detection order: dnl 1. --disable-ncurses skips all detection. dnl 2. --with-ncurses=PATH sets an explicit prefix (skips auto-detect). dnl 3. pkg-config ncursesw (wide-char variant preferred), then ncurses. dnl 4. Probe lft_candidate_prefixes for ncurses.h. dnl 5. AC_CHECK_LIB / AC_CHECK_HEADER in whatever paths are now set. dnl ------------------------------------------------------------------ AC_ARG_ENABLE([ncurses], [AS_HELP_STRING([--disable-ncurses], [disable ncurses interactive mode (default: auto-detect and use if available)])], [enable_ncurses="$enableval"], [enable_ncurses="yes"]) ncurses_path_override=no AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses=PATH], [specify prefix path to the ncurses library (e.g. /opt/homebrew)])], [case "$withval" in yes|no) AC_MSG_ERROR([please specify a PATH in --with-ncurses option!]) ;; *) if test '!' -d "$withval"; then AC_MSG_ERROR([$withval does not exist!]) fi ncurses_path_override=yes if test -d "$withval/include"; then CPPFLAGS="$CPPFLAGS -I$withval/include" CFLAGS="$CFLAGS -I$withval/include" else CPPFLAGS="$CPPFLAGS -I$withval" CFLAGS="$CFLAGS -I$withval" fi if test -d "$withval/lib"; then LDFLAGS="$LDFLAGS -L$withval/lib" else LDFLAGS="$LDFLAGS -L$withval" fi ;; esac]) AS_IF([test "x$enable_ncurses" = "xyes"], [have_ncurses=no dnl 1. Try pkg-config (ncursesw preferred, then ncurses; skipped when user gave explicit path) AS_IF([test "x$ncurses_path_override" = "xno"], [AS_IF([test "x$PKGCONFIG" != "xno"], [AC_MSG_CHECKING([for ncursesw via pkg-config]) AS_IF([$PKGCONFIG --exists ncursesw 2>/dev/null], [ncurses_cflags=`$PKGCONFIG --cflags ncursesw` ncurses_libs=`$PKGCONFIG --libs ncursesw` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $ncurses_cflags" LIBS="$LIBS $ncurses_libs" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[initscr(); endwin();]])], [have_ncurses=yes AC_MSG_RESULT([yes ($ncurses_libs)])], [CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" AC_MSG_RESULT([no (link test failed; wrong architecture?)])])], [AC_MSG_RESULT([no])]) dnl Try plain ncurses via pkg-config if ncursesw not found AS_IF([test "x$have_ncurses" = "xno"], [AC_MSG_CHECKING([for ncurses via pkg-config]) AS_IF([$PKGCONFIG --exists ncurses 2>/dev/null], [ncurses_cflags=`$PKGCONFIG --cflags ncurses` ncurses_libs=`$PKGCONFIG --libs ncurses` save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $ncurses_cflags" LIBS="$LIBS $ncurses_libs" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[initscr(); endwin();]])], [have_ncurses=yes AC_MSG_RESULT([yes ($ncurses_libs)])], [CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" AC_MSG_RESULT([no (link test failed; wrong architecture?)])])], [AC_MSG_RESULT([no])])])])]) dnl 2. Probe candidate prefixes if still not found AS_IF([test "x$have_ncurses" = "xno" && test "x$ncurses_path_override" = "xno"], [AC_MSG_CHECKING([for ncurses in candidate prefixes]) for _pfx in $lft_candidate_prefixes; do if test -f "$_pfx/include/ncurses.h"; then CPPFLAGS="$CPPFLAGS -I$_pfx/include" CFLAGS="$CFLAGS -I$_pfx/include" LDFLAGS="$LDFLAGS -L$_pfx/lib" AC_MSG_RESULT([$_pfx]) break fi done AS_IF([test "x$_pfx" = "x"], [AC_MSG_RESULT([not found])])]) dnl 3. Final check with whatever paths are now set (ncursesw preferred, then ncurses) AS_IF([test "x$have_ncurses" = "xno"], [AC_CHECK_LIB([ncursesw], [initscr], [have_ncurses=maybe_w], []) AS_IF([test "x$have_ncurses" = "xmaybe_w"], [AC_CHECK_HEADER([ncurses.h], [have_ncurses=yes LIBS="-lncursesw $LIBS" AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the ncurses library is available])], [have_ncurses=no AC_MSG_WARN([ncursesw library found but ncurses.h not found; disabling ncurses mode])])])]) AS_IF([test "x$have_ncurses" = "xno"], [AC_CHECK_LIB([ncurses], [initscr], [have_ncurses=maybe], []) AS_IF([test "x$have_ncurses" = "xmaybe"], [AC_CHECK_HEADER([ncurses.h], [have_ncurses=yes LIBS="-lncurses $LIBS" AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the ncurses library is available])], [have_ncurses=no AC_MSG_WARN([ncurses library found but ncurses.h not found; disabling ncurses mode])])])]) AS_IF([test "x$have_ncurses" = "xyes"], [AS_IF([test "x$ncurses_path_override" = "xyes" || test "x$PKGCONFIG" = "xno"], [dnl pkg-config not used — ensure LIBS has -lncurses* if not already added AS_CASE([$LIBS], [*-lncurses*], [], [LIBS="-lncurses $LIBS" AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the ncurses library is available])])])]) AS_IF([test "x$have_ncurses" != "xyes"], [lft_warn_no_ncurses=yes])]) dnl Conditionally compile lft_watch.o only when ncurses is available AS_IF([test "x$have_ncurses" = "xyes"], [WATCH_OBJS="lft_watch.o"], [WATCH_OBJS=""]) AC_SUBST([WATCH_OBJS]) esac dnl Set universal binary options AC_MSG_CHECKING([if we should build universal binaries]) if test "$UNIVERSAL" = "yes"; then case $host_os in *darwin*) CFLAGS="$CFLAGS -arch x86_64 -arch arm64" LDFLAGS="$LDFLAGS -arch x86_64 -arch arm64" AC_DEFINE([UNIVERSAL], [1], [Define if building a universal binary]) AC_MSG_RESULT([yes (x86_64 + arm64)]) ;; *) AC_MSG_RESULT([no (--enable-universal only applies to macOS)]) ;; esac else AC_MSG_RESULT([no]) fi dnl Use gettimeofday() on each packet instead of pcap header timestamp AC_MSG_CHECKING([if we should call gettimeofday for each packet]) if test "$GTOD" = "yes"; then AC_DEFINE([USE_GTOD], [1], [Define to use gettimeofday() instead of pcap timestamps]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi AC_CONFIG_FILES([Makefile]) dnl Emit any deferred advisory warnings here, just before AC_OUTPUT, dnl so they appear at the bottom of configure's output where they are noticed. AS_IF([test "x$lft_warn_no_cares" = "xyes"], [AC_MSG_WARN([ LFT is much faster with asynchronous DNS provided by the c-ares library, which was not detected. It is not required, but strongly recommended. Please consider installing it and re-running ./configure. On Debian/Ubuntu: sudo apt-get install libc-ares-dev On RHEL/Fedora: sudo dnf install c-ares-devel On macOS Homebrew: brew install c-ares On macOS MacPorts: sudo port install c-ares Or specify its location with: ./configure --with-cares=PATH])]) AS_IF([test "x$lft_warn_no_ncurses" = "xyes"], [AC_MSG_WARN([ LFT looks great using ncurses with a text-mode UI, which was not detected. The interactive continuous-monitoring mode (--watch) requires it. Please consider installing the ncurses development library and re-running ./configure. On Debian/Ubuntu: sudo apt-get install libncurses-dev On RHEL/Fedora: sudo dnf install ncurses-devel On macOS Homebrew: brew install ncurses On macOS MacPorts: sudo port install ncurses Or specify its location with: ./configure --with-ncurses=PATH])]) AC_OUTPUT lft-3.98/config/config/000755 000765 000024 00000000000 15174131661 014705 5ustar00vicstaff000000 000000 lft-3.98/config/acconfig.win.h000644 000765 000024 00000012235 11053131665 016155 0ustar00vicstaff000000 000000 /* config/acconfig.h. Generated by configure. */ /* acconfig.h.in. */ /* Host system type */ #define HOST_SYSTEM_TYPE "i686-pc-cygwin" /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have alloca, as a function or macro. */ #define HAVE_ALLOCA 1 /* Define if you have and it should be used (not on Ultrix). */ #define HAVE_ALLOCA_H 1 /* Define to `unsigned' if doesn't define. */ /* #undef size_t */ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you can safely include both and . */ #define TIME_WITH_SYS_TIME 1 /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have alloca, as a function or macro. */ #define HAVE_ALLOCA 1 /* Define if you have and it should be used (not on Ultrix). */ #define HAVE_ALLOCA_H 1 /* Define to `unsigned' if doesn't define. */ /* #undef size_t */ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you can safely include both and . */ #define TIME_WITH_SYS_TIME 1 /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have alloca, as a function or macro. */ #define HAVE_ALLOCA 1 /* Define if you have and it should be used (not on Ultrix). */ #define HAVE_ALLOCA_H 1 /* Define to `unsigned' if doesn't define. */ /* #undef size_t */ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you can safely include both and . */ #define TIME_WITH_SYS_TIME 1 /* Linux requires this in order to have the right network structures. */ #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif /* Define if you have the gettimeofday function. */ #define HAVE_GETTIMEOFDAY 1 /* Define if you have the select function. */ #define HAVE_SELECT 1 /* Define if you have the socket function. */ #define HAVE_SOCKET 1 /* Define if you have the strdup function. */ #define HAVE_STRDUP 1 /* Define if you have the strstr function. */ #define HAVE_STRSTR 1 /* Define if you have the header file. */ #define HAVE_FCNTL_H 1 /* Define if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define if you have the header file. */ #define HAVE_SYS_IOCTL_H 1 /* Define if you have the header file. */ #define HAVE_SYS_TIME_H 1 /* Define if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define if you have the m library (-lm). */ #define HAVE_LIBM 1 /* Define if you have the nsl library (-lnsl). */ /* #undef HAVE_LIBNSL */ /* Define if you have the pcap library (-lpcap). */ /* #undef HAVE_LIBPCAP */ /* Define if you have the socket library (-lsocket). */ /* #undef HAVE_LIBSOCKET */ /* Define if this is a BSD TCP/IP stack */ /* #undef BSD_IP_STACK */ /* Define if this is Darwin */ /* #undef DARWIN */ /* Define if this is NETBSD */ /* #undef NETBSD */ /* Define if this is OpenBSD */ /* #undef OPENBSD */ /* Define if solaris munges the th_sum field in its own special way */ /* #undef SOLARIS_LENGTH_IN_CHECKSUM */ /* Define if your system has a sa_len member in struct sockaddr */ /* #undef HAVE_SOCKADDR_SA_LEN */ /* Define if an IPv6 network stack is detected */ /* #undef INET6 */ lft-3.98/config/config.guess000755 000765 000024 00000143242 15165341545 015772 0ustar00vicstaff000000 000000 #! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2025 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2025-07-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system '$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2025 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # Just in case it came from the environment. GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still # use 'HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c17 c99 c89 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #if defined(__ANDROID__) LIBC=android #else #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #elif defined(__LLVM_LIBC__) LIBC=llvm #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case $UNAME_VERSION in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; sun4*:SunOS:*:*) case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like '4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case `/bin/arch` in sun3) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) GUESS="$UNAME_MACHINE-pc-managarm-mlibc" ;; *:[Mm]anagarm:*:*) GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) set_cc_for_build CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then ABI=64 sed 's/^ //' << EOF > "$dummy.c" #ifdef __ARM_EABI__ #ifdef __ARM_PCS_VFP ABI=eabihf #else ABI=eabi #endif #endif EOF cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` eval "$cc_set_abi" case $ABI in eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; esac fi GUESS=$CPU-unknown-linux-$LIBCABI ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; kvx:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; kvx:cos:*:*) GUESS=$UNAME_MACHINE-unknown-cos ;; kvx:mbr:*:*) GUESS=$UNAME_MACHINE-unknown-mbr ;; loongarch32:Linux:*:* | loongarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then ABI=64 sed 's/^ //' << EOF > "$dummy.c" #ifdef __i386__ ABI=x86 #else #ifdef __ILP32__ ABI=x32 #endif #endif EOF cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` eval "$cc_set_abi" case $ABI in x86) CPU=i686 ;; x32) LIBCABI=${LIBC}x32 ;; esac fi GUESS=$CPU-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. GUESS=i386-sequent-sysv4 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find 'uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; ppc:Haiku:*:*) # Haiku running on Apple PowerPC GUESS=powerpc-apple-haiku ;; *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) GUESS=$UNAME_MACHINE-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "${cputype-}" = 386; then UNAME_MACHINE=i386 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; x86_64:[Ii]ronclad:*:*|i?86:[Ii]ronclad:*:*) GUESS=$UNAME_MACHINE-pc-ironclad-mlibc ;; *:[Ii]ronclad:*:*) GUESS=$UNAME_MACHINE-unknown-ironclad-mlibc ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif int main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp nil t) # time-stamp-start: "timestamp='" # time-stamp-format: "%Y-%02m-%02d" # time-stamp-end: "'" # End: lft-3.98/config/config.sub000755 000765 000024 00000116007 15165341545 015434 0ustar00vicstaff000000 000000 #! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2025 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale timestamp='2025-07-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2025 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in cloudabi*-eabi* \ | kfreebsd*-gnu* \ | knetbsd*-gnu* \ | kopensolaris*-gnu* \ | ironclad-* \ | linux-* \ | managarm-* \ | netbsd*-eabi* \ | netbsd*-gnu* \ | nto-qnx* \ | os2-emx* \ | rtmk-nova* \ | storm-chaos* \ | uclinux-gnu* \ | uclinux-uclibc* \ | windows-* ) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) case $field1-$field2 in # Shorthands that happen to contain a single dash convex-c[12] | convex-c3[248]) basic_machine=$field2-convex basic_os= ;; decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Do not treat sunos as a manufacturer sun*os*) basic_machine=$field1 basic_os=$field2 ;; # Manufacturers 3100* \ | 32* \ | 3300* \ | 3600* \ | 7300* \ | acorn \ | altos* \ | apollo \ | apple \ | atari \ | att* \ | axis \ | be \ | bull \ | cbm \ | ccur \ | cisco \ | commodore \ | convergent* \ | convex* \ | cray \ | crds \ | dec* \ | delta* \ | dg \ | digital \ | dolphin \ | encore* \ | gould \ | harris \ | highlevel \ | hitachi* \ | hp \ | ibm* \ | intergraph \ | isi* \ | knuth \ | masscomp \ | microblaze* \ | mips* \ | motorola* \ | ncr* \ | news \ | next \ | ns \ | oki \ | omron* \ | pc533* \ | rebel \ | rom68k \ | rombug \ | semi \ | sequent* \ | sgi* \ | siemens \ | sim \ | sni \ | sony* \ | stratus \ | sun \ | sun[234]* \ | tektronix \ | tti* \ | ultra \ | unicom* \ | wec \ | winbond \ | wrs) basic_machine=$field1-$field2 basic_os= ;; tock* | zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) cpu=m68k vendor=motorola ;; # This used to be dpx2*, but that gets the RS6000-based # DPX/20 and the x86-based DPX/2-100 wrong. See # https://oldskool.silicium.org/stations/bull_dpx20.htm # https://www.feb-patrimoine.com/english/bull_dpx2.htm # https://www.feb-patrimoine.com/english/unix_and_bull.htm dpx2 | dpx2[23]00 | dpx2[23]xx) cpu=m68k vendor=bull ;; dpx2100 | dpx21xx) cpu=i386 vendor=bull ;; dpx20) cpu=rs6000 vendor=bull ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x"$basic_os" != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. obj= case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) saved_IFS=$IFS IFS="-" read kernel os <&2 fi ;; *) echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 exit 1 ;; esac case $obj in aout* | coff* | elf* | pe*) ;; '') # empty is fine ;; *) echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 exit 1 ;; esac # Here we handle the constraint that a (synthetic) cpu and os are # valid only in combination with each other and nowhere else. case $cpu-$os in # The "javascript-unknown-ghcjs" triple is used by GHC; we # accept it here in order to tolerate that, but reject any # variations. javascript-ghcjs) ;; javascript-* | *-ghcjs) echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os-$obj in linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ | linux-mlibc*- | linux-musl*- | linux-newlib*- \ | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) ;; uclinux-uclibc*- | uclinux-gnu*- ) ;; ironclad-mlibc*-) ;; managarm-mlibc*- | managarm-kernel*- ) ;; windows*-msvc*-) ;; -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ | -uclibc*- ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 exit 1 ;; -kernel*- ) echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 exit 1 ;; *-kernel*- ) echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 exit 1 ;; *-msvc*- ) echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 exit 1 ;; kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) ;; vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) ;; nto-qnx*-) ;; os2-emx-) ;; rtmk-nova-) ;; *-eabi*- | *-gnueabi*-) ;; ios*-simulator- | tvos*-simulator- | watchos*-simulator- ) ;; none--*) # None (no kernel, i.e. freestanding / bare metal), # can be paired with an machine code file format ;; -*-) # Blank kernel with real OS is always fine. ;; --*) # Blank kernel and OS with real machine code file format is always fine. ;; *-*-*) echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos* | *-solaris*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be ;; *-hpux*) vendor=hp ;; *-mpeix*) vendor=hp ;; *-hiux*) vendor=hitachi ;; *-unos*) vendor=crds ;; *-dgux*) vendor=dg ;; *-luna*) vendor=omron ;; *-genix*) vendor=ns ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) vendor=ibm ;; *-ptx*) vendor=sequent ;; *-tpf*) vendor=ibm ;; *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; *-aux*) vendor=apple ;; *-hms*) vendor=hitachi ;; *-mpw* | *-macos*) vendor=apple ;; *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; *-vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp nil t) # time-stamp-start: "timestamp='" # time-stamp-format: "%Y-%02m-%02d" # time-stamp-end: "'" # End: