#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "nbcache.h"
enum { /* service type */
STdfs = 0x00800000,
STwin95 = 0x00400000, /* win 95 or later */
STvms = 0x00200000,
STosf = 0x00100000,
STdomain_master = 0x00080000,
STmaster_browser = 0x00040000,
STbackup_browser = 0x00020000,
STpotential_browser = 0x00010000,
STnt_server = 0x00008000,
STmfpn = 0x00004000, /* no one knows what this is */
STwin_for_workgroups = 0x00002000,
STnt_client = 0x00001000,
STunix = 0x00000800,
STdialin = 0x00000400,
STprintq = 0x00000200,
STdomain_member = 0x00000100,
STnovell = 0x00000080, /* is a novell server offering SMB */
STapple = 0x00000040, /* is an apple server offering SMB */
STtime_source = 0x00000020,
STdomain_bakctrl = 0x00000010,
STdomain_ctrl = 0x00000008,
STsqlserver = 0x00000004,
STserver = 0x00000002,
STworkstation = 0x00000001,
};
static void
nbrole(Attr *a, int type)
{
int i, len;
char buf[1023];
struct {
int n;
char *s;
} tab[] = {
{ STdomain_master, "DMB" },
{ STmaster_browser, "LMB" },
{ STbackup_browser, "BMB" },
/* { STpotential_browser, "PMB" }, */
};
len = 0;
*buf = 0;
for(i = 0; i < nelem(tab); i++)
if(tab[i].n & type)
len += snprint(buf+len, sizeof(buf)-len, "%s ", tab[i].s);
if(len >1){
buf[len-1] = 0;
addval(a, "nb-role", "%s", buf);
}
}
static void
domrole(Attr *a, int type)
{
int i, len;
char buf[1023];
struct {
int n;
char *s;
} tab[] = {
{ STdomain_ctrl, "DC" },
{ STdomain_bakctrl, "BDC" },
};
len = 0;
*buf = 0;
for(i = 0; i < nelem(tab); i++)
if(tab[i].n & type)
len += snprint(buf+len, sizeof(buf)-len, "%s ", tab[i].s);
if(len >1){
buf[len-1] = 0;
addval(a, "dom-role", "%s", buf);
}
}
static void
host_info(Node *n, int type)
{
int i, len;
char buf[1023];
struct {
int n;
char *s;
} tab[] = {
{ STsqlserver, "SQL" },
{ STtime_source, "time" },
{ STprintq, "print" },
{ STdialin, "dialup" },
{ STdfs, "DFS" }
};
len = 0;
*buf = 0;
for(i = 0; i < nelem(tab); i++)
if(tab[i].n & type)
len += snprint(buf+len, sizeof(buf)-len, "%s ", tab[i].s);
if(len >1){
buf[len-1] = 0;
setval(n, "srv", "%s", buf);
}
}
static void
osversion(Node *n, int svs, int osver, int brover)
{
char buf[32], *base, *os;
/* see http://msdn.microsoft.com/en-us/library/ms724833(VS.85).aspx */
switch(osver){
case 0x2104:
os = "Windows NT 4";
break;
case 0x0f01:
if(svs & STunix)
os = "FreeBSD Samba";
else
os = "Windows NT4";
break;
case 0x0601:
if(svs & STworkstation)
os = "Windows 7";
else
os = "Windows 2008 R2";
break;
case 0x0600:
if(svs & STworkstation)
os = "Windows Vista";
else
os = "Windows 2008";
break;
case 0x0502:
if(svs & STworkstation)
os = "Windows XP pro";
else
os = "Windows 2003";
break;
case 0x0501:
os = "Windows XP";
break;
case 0x0500:
os = "Windows 2000";
break;
case 0x045a:
os = "Windows ME";
break;
case 0x040a:
os = "Windows 98";
break;
case 0x041f:
os = "HP laserjet";
break;
/*
* samba is somwhat unreliable as it is configurable
* using the "announce version" parameter in smb.conf,
* however these appear to be common values.
*/
case 0x0409:
case 0x0402: /* Pluto disk stores - FreeBSD and samba */
case 0x0405:
os = "Unix Samba";
break;
case 0x0403:
os = "Windows 95 osr2";
break;
case 0x0400:
if(svs & STwin95)
os = "Windowsn 95";
else
if(brover == 0x0f01)
os = "Netware NFA";
else
os = "Windows NT4";
break;
case 0x0333:
os = "Windows NS 3.15";
break;
case 0x0300:
os = "Windows CE 3.0";
break;
case 0x0201:
os = "Windows CE 2.1";
break;
case 0x0200:
os = "Windows CE 2.0";
break;
case 0x0100:
os = "Windows CE 1.0";
break;
default:
base = "Windows";
if(svs & STunix)
base = "Unix";
if(svs & STosf)
base = "OSF Unix";
if(svs & STvms)
base = "VMS Pathworks";
if(svs & STwin_for_workgroups)
base = "Windows for workgroups";
if(svs & STnovell)
base = "Novell";
if(svs & STapple)
base = "Apple";
snprint(buf, sizeof(buf), "%s os=%d.%d brow=%d.%d", base,
osver >> 8, osver & 0xff,
brover >> 8, brover & 0xff);
os = buf;
break;
}
setval(n, "hinfo", "%s", os);
}
/* NB: we don't need dom= if it is same as the name without its suffix */
void
browse(Pkt *p)
{
Node *n;
Attr *a;
ulong ttl, period;
uchar ip[IPv4addrlen];
int type, brover, osver, cmd;
char *s, name[20], comment[48], srcname[64], dstname[64];
static uchar zeros[IPv4addrlen];
/**************************
* Netbios header
*/
if(r8(p) != 0x11) /* type != direct datagram */
return;
r8(p); /* flags */
rl16(p); /* datagram ID */
rmem(p, ip, IPv4addrlen); /* source IP */
rl16(p); /* source port */
rl16(p); /* datagram length */
rl16(p); /* packet offset */
rnbname(p, srcname, sizeof(srcname)); /* source name */
srcname[15] = 0;
strlwr(srcname);
trim(srcname, ' ');
rnbname(p, dstname, sizeof(dstname)); /* destination name */
dstname[15] = 0;
strlwr(dstname);
trim(dstname, ' ');
/**************************
* SMB header
*/
skip(p, 32);
/**************************
* SMB mailslot protocol header
*/
skip(p, 36 +18);
/**************************
* Browser protocol packet
*/
cmd = r8(p); /* command */
if(cmd != 1 && cmd != 12 && cmd != 15)
return;
r8(p); /* update count */
period = rl32(p); /* update period in milliseconds */
rmem(p, name, 16); /* netbios name */
name[15] = 0;
strlwr(name);
osver = rb16(p); /* OS version */
type = rl32(p); /* server type */
/*
* Update period is in milliseconds.
* Names expire after 3 re-broadcast intervals.
* This appears to be 3 * 12 mins.
*/
ttl = period / 333;
switch(cmd){
case 1: /* workstation announcemenet */
case 15: /* Local master announcement */
brover = rb16(p); /* protocol version */
rl16(p); /* magic number */
rstr(p, comment, sizeof(comment));
n = getnode(Thost, ttl, "%s", name);
if(memcmp(ip, zeros, IPv4addrlen) != 0){
a = setval(n, "ip", "%V", ip);
adapter_status(a, ttl, ip);
}
else
if((s = csgetvalue(Netdir, "sys", name, "ip", nil)) != nil){
a = setval(n, "ip", "%s", s);
v4parseip(ip, s);
adapter_status(a, ttl, ip);
}
if(*comment)
setval(n, "txt", "%s", comment);
osversion(n, type, osver, brover);
host_info(n, type);
if(Debug)
setval(n, "src", "browse");
n = getnode(Tdomain, ttl, "%s", dstname);
a = setval(n, "member", "%s", name);
nbrole(a, type);
domrole(a, type);
break;
}
}
|