asphyxia 0.10.0

A fast and efficient network scanner written in Rust
Documentation
//! Frequency-ordered list of the most commonly open TCP ports.
//!
//! The ordering is seeded from Nmap's `nmap-services` top-port frequencies, so
//! `--top-ports N` scans the N ports most likely to be open first. The list is
//! baked into the binary; no external data file is needed.

/// The 1000 most common TCP ports, ordered from most to least frequently open.
///
/// `--top-ports N` takes the first `N` entries. Requesting more than
/// [`MAX_TOP_PORTS`] is an error surfaced to the user.
pub static TOP_PORTS: [u16; 1000] = [
    80, 23, 443, 21, 22, 25, 3389, 110, 445, 139, 143, 53, 135, 3306, 8080, 1723, 111, 995, 993,
    5900, 1025, 587, 8888, 199, 1720, 465, 548, 113, 81, 6001, 10000, 514, 5060, 179, 1026, 2000,
    8443, 8000, 32768, 554, 26, 1433, 49152, 2001, 515, 8008, 49154, 1027, 5666, 646, 5000, 5631,
    631, 49153, 8081, 2049, 88, 79, 5800, 106, 2121, 1110, 49155, 6000, 513, 990, 5357, 427, 49156,
    543, 544, 5101, 144, 7, 389, 8009, 3128, 444, 9999, 5009, 7070, 5190, 3000, 5432, 1900, 3986,
    13, 1029, 9, 5051, 6646, 49157, 1028, 873, 1755, 2717, 4899, 9100, 119, 37, 1000, 3001, 5001,
    82, 10010, 1030, 9090, 2107, 1024, 2103, 6004, 1801, 5050, 19, 8031, 1041, 255, 1048, 1049,
    1053, 1054, 1056, 1064, 1065, 1521, 830, 1201, 3703, 17, 808, 3689, 1031, 1044, 1071, 5901,
    100, 9102, 8010, 2869, 1039, 5120, 4001, 9000, 2105, 636, 1038, 2601, 7000, 1, 1066, 1069, 625,
    311, 280, 254, 4000, 1761, 5003, 2002, 2005, 1998, 1032, 1050, 6112, 3690, 2161, 6002, 2401,
    4045, 902, 7937, 787, 1058, 2383, 32771, 1033, 1040, 1059, 50000, 5555, 10001, 1494, 3, 593,
    2301, 3268, 7938, 1234, 1022, 1074, 8002, 1036, 1035, 9001, 1037, 464, 497, 1935, 6666, 2003,
    6543, 1352, 24, 3269, 1111, 407, 500, 20, 2006, 3260, 15000, 1218, 1034, 4444, 264, 2004, 33,
    1042, 42510, 999, 3052, 1023, 222, 1068, 888, 7100, 563, 1717, 2008, 992, 32770, 32772, 7001,
    8082, 2007, 740, 5550, 2009, 5801, 1043, 512, 2701, 7019, 50001, 1700, 4662, 2065, 2010, 42,
    9535, 3283, 1051, 1060, 1052, 2702, 9595, 2725, 163, 2381, 689, 1045, 1047, 1055, 3011, 1067,
    1063, 1004, 32774, 32773, 2404, 2500, 3372, 2135, 1046, 3801, 5010, 3766, 1057, 1062, 7011,
    4443, 1061, 32775, 3200, 9010, 3130, 4321, 4004, 1092, 220, 9002, 5601, 9200, 27017, 27018,
    28017, 6379, 11211, 5984, 9042, 7474, 9160, 50070, 8020, 9083, 10002, 50075, 8088, 60010,
    60030, 2181, 2888, 3888, 9092, 8161, 61616, 5672, 15672, 4369, 25672, 9300, 5044, 8125, 8126,
    25826, 8086, 8089, 4242, 7777, 4567, 8834, 9443, 8140, 10050, 10051, 161, 162, 5985, 5986,
    47001, 2, 4, 5, 6, 8, 10, 11, 12, 14, 15, 16, 18, 27, 28, 29, 30, 31, 32, 34, 35, 36, 38, 39,
    40, 41, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
    66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 94,
    95, 96, 97, 98, 99, 101, 102, 103, 104, 105, 107, 108, 109, 112, 114, 115, 116, 117, 118, 120,
    121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 136, 137, 138, 140, 141,
    142, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 164, 165,
    166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 180, 181, 182, 183, 184, 185,
    186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 200, 201, 202, 203, 204, 205,
    206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 221, 223, 224, 225, 226,
    227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
    246, 247, 248, 249, 250, 251, 252, 253, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 267,
    268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 281, 282, 283, 284, 285, 286, 287,
    288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306,
    307, 308, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326,
    327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345,
    346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
    365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383,
    384, 385, 386, 387, 388, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
    404, 405, 406, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
    424, 425, 426, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 446,
    447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 466, 467,
    468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486,
    487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 498, 499, 501, 502, 503, 504, 505, 506, 507,
    508, 509, 510, 511, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530,
    531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 545, 546, 547, 549, 550, 551, 552,
    553, 555, 556, 557, 558, 559, 560, 561, 562, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573,
    574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 588, 589, 590, 591, 592, 594,
    595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613,
    614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 626, 627, 628, 629, 630, 632, 633, 634,
    635, 637, 638, 639, 640, 641, 642, 643, 644, 645, 647, 648, 649, 650, 651, 652, 653, 654, 655,
    656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674,
    675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 690, 691, 692, 693, 694,
    695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713,
    714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731,
];

/// The largest `N` accepted by `--top-ports` (the length of [`TOP_PORTS`]).
pub const MAX_TOP_PORTS: usize = TOP_PORTS.len();

/// Named port sets for quick recon, selected with `--ports <name>`.
///
/// Each set is a small, curated group of ports for a common surface. Names are
/// matched case-insensitively.
pub static NAMED_PORT_SETS: &[(&str, &[u16])] = &[
    ("web", &[80, 443, 8080, 8443, 8000, 8008, 8888]),
    ("mail", &[25, 110, 143, 465, 587, 993, 995]),
    ("db", &[1433, 1521, 3306, 5432, 6379, 9042, 11211, 27017]),
    ("remote", &[22, 23, 3389, 5900, 5985, 5986]),
    ("windows", &[135, 139, 445, 3389, 5985, 5986]),
];

/// Return the first `n` entries of [`TOP_PORTS`], or an error when `n` is 0 or
/// exceeds [`MAX_TOP_PORTS`].
pub fn top_ports(n: usize) -> Result<Vec<u16>, String> {
    if n == 0 {
        return Err("--top-ports must be at least 1".to_string());
    }
    if n > MAX_TOP_PORTS {
        return Err(format!(
            "--top-ports supports at most {} ports (requested {})",
            MAX_TOP_PORTS, n
        ));
    }
    Ok(TOP_PORTS[..n].to_vec())
}

/// Resolve a named port set (case-insensitive) to its ports, or an error that
/// lists the available names.
pub fn named_port_set(name: &str) -> Result<Vec<u16>, String> {
    let lower = name.to_ascii_lowercase();
    NAMED_PORT_SETS
        .iter()
        .find(|(n, _)| *n == lower)
        .map(|(_, ports)| ports.to_vec())
        .ok_or_else(|| {
            let names: Vec<&str> = NAMED_PORT_SETS.iter().map(|(n, _)| *n).collect();
            format!(
                "Unknown port set '{}'; available: {}",
                name,
                names.join(", ")
            )
        })
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    #[test]
    fn top_ports_list_is_exactly_1000_unique_ports() {
        assert_eq!(TOP_PORTS.len(), 1000);
        let unique: HashSet<u16> = TOP_PORTS.iter().copied().collect();
        assert_eq!(unique.len(), 1000, "TOP_PORTS must not contain duplicates");
    }

    #[test]
    fn common_ports_rank_near_the_top() {
        let top10: HashSet<u16> = TOP_PORTS[..10].iter().copied().collect();
        for p in [80u16, 443, 22] {
            assert!(top10.contains(&p), "port {} should be in the top 10", p);
        }
    }

    #[test]
    fn top_ports_returns_requested_count() {
        assert_eq!(top_ports(100).unwrap().len(), 100);
        assert_eq!(top_ports(1000).unwrap().len(), 1000);
        assert_eq!(top_ports(1).unwrap(), vec![TOP_PORTS[0]]);
    }

    #[test]
    fn top_ports_rejects_zero_and_overflow() {
        assert!(top_ports(0).is_err());
        assert!(top_ports(MAX_TOP_PORTS + 1).is_err());
    }

    #[test]
    fn named_sets_resolve_case_insensitively() {
        assert_eq!(
            named_port_set("web").unwrap(),
            vec![80, 443, 8080, 8443, 8000, 8008, 8888]
        );
        assert_eq!(
            named_port_set("WEB").unwrap(),
            named_port_set("web").unwrap()
        );
        assert!(named_port_set("nope").is_err());
    }
}