#include #include #include #include #include #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; } }