Net::NBName(3pm)

πŸ“¦
version 0.26 Β· 5 versions
, in ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– Net::NBName πŸ’–

okay bestie, listen. this one's called Net::NBName, it lives in the User Contributed Perl Documentation, and it is literally your backstage pass to NetBIOS Name Service Requests. she's giving network detective energy and honestly? obsessed. βœ¨πŸ’…


✨ SYNOPSIS ✨

use Net::NBName;
my $nb = Net::NBName->new;

# a unicast node status request
my $ns = $nb->node_status("10.0.0.1");
if ($ns) {
    print $ns->as_string;
}

# a unicast name query request
my $nq = $nb->name_query("10.0.1.80", "SPARK", 0x00);
if ($nq) {
    print $nq->as_string;
}

# a broadcast name query request
my $nq = $nb->name_query(undef, "SPARK", 0x00);
if ($nq) {
    print $nq->as_string;
}

πŸ¦‹ DESCRIPTION πŸ¦‹

so Net::NBName is a class that lets you perform simple NetBIOS Name Service Requests right inside your own Perl code. it does all these NetBIOS operations over TCP/IP, using Perl's own built-in socket support. no extra modules, no drama, we love a low-maintenance queen. 🫢

rn there are two NBNS requests implemented in here: the node status request and the name query request. two icons. both ate.

🌸 NetBIOS Node Status Request 🌸

this one lets you figure out the registered NetBIOS names for a remote host you point at.

the decoded response comes back to you as a Net::NBName::NodeStatus object. slay.

querying 192.168.0.10 for node status...
SPARK          <20> UNIQUE M-node Registered Active
SPARK          <00> UNIQUE M-node Registered Active
PLAYGROUND     <00> GROUP  M-node Registered Active
PLAYGROUND     <1C> GROUP  M-node Registered Active
PLAYGROUND     <1B> UNIQUE M-node Registered Active
PLAYGROUND     <1E> GROUP  M-node Registered Active
SPARK          <03> UNIQUE M-node Registered Active
PLAYGROUND     <1D> UNIQUE M-node Registered Active
..__MSBROWSE__.<01> GROUP  M-node Registered Active
MAC Address = 00-1C-2B-3A-49-58

πŸŽ€ NetBIOS Name Query Request πŸŽ€

this one resolves a name to an IP address using NetBIOS Name Resolution. you can send these either as a unicast (say, if you're querying an NBNS server) or as a broadcast out on the local subnet. main character either way.

and whichever way you go, the decoded response comes back to you as an Net::NBName::NameQuery object.

querying 192.168.0.10 for playground<00>...
255.255.255.255 GROUP  B-node
ttl = 0 (default is 300000)
RA set, this was an NBNS server

broadcasting for playground<1C>...
192.168.0.10    GROUP  B-node
ttl = 0 (default is 300000)
RA set, this was an NBNS server

broadcasting for spark<20>...
192.168.0.10    UNIQUE H-node
ttl = 0 (default is 300000)
RA set, this was an NBNS server

πŸ‘‘ CONSTRUCTOR πŸ‘‘

  • $nb = Net::NBName->new β€” builds you a brand new Net::NBName object. this is the girl you use to perform NetBIOS Name Service requests. πŸ’–

πŸ’œ METHODS πŸ’œ

  • $ns = $nb->node_status( $host [, $timeout] ) β€” this queries $host for its node status, and the response comes back as a Net::NBName::NodeStatus object. if the host ghosts you and no response arrives, the method returns undef. 😭

you can also pass in the optional timeout, in seconds, for the node status request. it defaults to .25 seconds β€” kinda impatient of her but go off.

  • $nq = $nb->name_query( $host, $name, $suffix [, $flags [, $timeout] ] ) β€” this queries $host for the name you asked for, and the response comes back as a Net::NBName::NameQuery object. ✨

if $host is undef, you get a broadcast name query; otherwise you get a unicast name query.

heads up bestie: broadcast name queries can sometimes get multiple responses back. only the first positive response gets decoded and returned as a Net::NBName::NameQuery object. the rest? left on read. πŸ’€

if no response comes back, or you get a negative name query response, the method returns undef.

you can override the flags in the NetBIOS name request if you really want to. peep the notes on Hacking Name Query Flags below.

you can also optionally pass the timeout in seconds for the name query request. it defaults to .25 seconds for unicast name queries and 1 second for broadcast name queries. ⏰


πŸ”₯ EXAMPLES πŸ”₯

Querying NetBIOS Names

you can use this example to query for a NetBIOS name. pass a host and it does a unicast query; don't pass a host and it does a broadcast query. the shorthand here is writing the name as <name>#<suffix>, where the suffix is in hex. iconic honestly.

"namequery.pl spark#0"

"namequery.pl spark#20 192.168.0.10"

use strict;
use Net::NBName;

my $nb = Net::NBName->new;
my $param = shift;
my $host = shift;
if ($param =~ /^([\w-]+)\#(\w{1,2})$/) {
    my $name = $1;
    my $suffix = hex $2;

    my $nq;
    if (defined($host) && $host =~ /\d+\.\d+\.\d+\.\d+/) {
        printf "querying %s for %s<%02X>...\n", $host, $name, $suffix;
        $nq = $nb->name_query($host, $name, $suffix);
    } else {
        printf "broadcasting for %s<%02X>...\n", $name, $suffix;
        $nq = $nb->name_query(undef, $name, $suffix);
    }
    if ($nq) {
        print $nq->as_string;
    }
} else {
    die "expected: <name>#<suffix> [<host>]\n";
}

Querying Remote Name Table

this example emulates the windows nbtstat -A command. give it the ip address of the remote host and you can peek at its NetBIOS Name Table. nosy in a cute way. πŸ™ˆ

"nodestat.pl 192.168.0.10"

use Net::NBName;

my $nb = Net::NBName->new;
my $host = shift;
if (defined($host) && $host =~ /\d+\.\d+\.\d+\.\d+/) {
    my $ns = $nb->node_status($host);
    if ($ns) {
        print $ns->as_string;
    } else {
        print "no response\n";
    }
} else {
    die "expected: <host>\n";
}

Scanning for NetBIOS hosts

this example scans a subnet for NetBIOS hosts. it uses Net::Netmask to parse the subnet parameter and enumerate all the hosts in that subnet. look at her doing recon, we stan a curious queen. πŸ”₯

"nodescan.pl 192.168.0.0/24"

use Net::NBName;
use Net::Netmask;

$mask = shift or die "expected: <subnet>\n";

$nb = Net::NBName->new;
$subnet = Net::Netmask->new2($mask);
for $ip ($subnet->enumerate) {
    print "$ip ";
    $ns = $nb->node_status($ip);
    if ($ns) {
        for my $rr ($ns->names) {
            if ($rr->suffix == 0 && $rr->G eq "GROUP") {
                $domain = $rr->name;
            }
            if ($rr->suffix == 3 && $rr->G eq "UNIQUE") {
                $user = $rr->name;
            }
            if ($rr->suffix == 0 && $rr->G eq "UNIQUE") {
                $machine = $rr->name unless $rr->name =~ /^IS~/;
            }
        }
        $mac_address = $ns->mac_address;
        print "$mac_address $domain\\$machine $user";
    }
    print "\n";
}

πŸ“ NOTES πŸ“

Microsoft's WINS Server Implementation

when you're doing name queries, keep this in mind: when Microsoft implemented their NBNS Name Server (the Microsoft WINS Server), they mapped group names to the single IP address 255.255.255.255 β€” that's the limited broadcast address. and to actually support real group names, Microsoft modified WINS to support special groups. these special groups show up differently in WINS. for example, the Domain Controllers (0x1C) group appears as "Domain Name" instead of "Group". the way that everything is a rebrand 😭

the complete set of WINS mapping types is:

Unique
Group
Domain Name
Internet group
Multihomed

Unique and Group map to a single IP address. Domain Name, Internet group, and Multihomed are the special groups, and those can include up to 25 IP addresses. πŸ‘‘

Hacking Name Query Flags

NetBIOS Name Service Requests have a bunch of flags attached to them. the code sets these to sensible defaults for you when it sends node status and name query requests. thoughtful of her. 🫢

but bestie, you can override those settings if you want β€” just call the name_query method on a Net::NBName object with a fourth parameter:

$nb->name_query( $host, $name, $suffix, $flags );

for a unicast name query, the flags default to 0x0100, which sets the RD (recursion desired) flag. for a broadcast name query, the flags default to 0x0010, which sets the B (broadcast) flag.

and experimentation gave the following results, no cap:

  • if B is set, the remote name table will be used. if the queried name isn't present, there will be no response.
  • if B is not set and the host is an NBNS server, then the NBNS server gets used before the remote name table, and you'll get a negative response if the name isn't present. but if the host is not an NBNS server, you'll get no response if the name isn't present.

Copyright (c) 2002, 2003, 2004 James Macfarlane. All rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


okay that's the whole page, you're officially educated πŸ’…βœ¨ this little tour was brought to you by perl v5.36.0, dated 2022-10-15 β€” go query something, bestie, and remember: if the host doesn't answer, it's not you, it's undef. πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from