/src/nbdgm.c
https://bitbucket.org/opexxx/lanmap2 · C · 215 lines · 166 code · 16 blank · 33 comment · 27 complexity · 661b03ee4eaf473a623d81159d0de0e8 MD5 · raw file
- /* ex: set ff=dos ts=2 et: */
- /* $Id$ */
- /*
- * Copyright 2008 Ryan Flynn
- * All rights reserved.
- */
- /*
- * NetBIOS Datagram
- */
-
- #include <assert.h>
- #include <stdio.h>
- #include <arpa/inet.h>
- #include "env.h"
- #include "types.h"
- #include "prot.h"
- #include "report.h"
- #include "util.h"
- #include "udp.h"
- #include "ipv4.h"
- #include "nbdgm.h"
-
- static const nb_dgm Dgm;
- static const nb_dgm_name DgmName;
- static int init (void);
- static size_t parse(char *, size_t, parse_frame *, const parse_status *);
- static size_t dump(const parse_frame *, int options, FILE *);
-
- static int test_udp(const char *, size_t, const parse_status *);
- static const prot_parent Test[] = {
- { PROT_UDP, test_udp }
- };
-
- /**
- * exported interface
- */
- const prot_iface Iface_NBDgm = {
- DINIT(id, PROT_NBDGM),
- DINIT(osi, OSI_App),
- DINIT(shortname, "NB-Dgm"),
- DINIT(propername, "NetBIOS Datagram"),
- DINIT(init, init),
- DINIT(unload, NULL),
- DINIT(parse, parse),
- DINIT(dump, dump),
- DINIT(addr_type, NULL),
- DINIT(addr_from, NULL),
- DINIT(addr_to, NULL),
- DINIT(addr_format, NULL),
- DINIT(addr_local, NULL),
- DINIT(parents, sizeof Test / sizeof Test[0]),
- DINIT(parent, Test)
- };
-
- /**
- * Windows NB Dgms are sent between reserved ports on both machines (TODO: Reference needed)
- */
- static int test_udp(const char *buf, size_t len, const parse_status *st)
- {
- const udp *u = (udp *)st->frame[st->frames - 1].off;
- return NBDGM_UDP_PORT == u->srcport &&
- NBDGM_UDP_PORT == u->dstport;
- }
-
- /**
- * Each octet in a NetBIOS name is split into 4-bit nibbles and stored as
- * an uppercase ASCII character 'A' + n
- * @ref #2
- */
- size_t nb_decode_name(char *wr, size_t wrlen, const char *rd, size_t rdlen)
- {
- const char *owr = wr;
- assert((unsigned)rdlen < 2048);
- assert(wrlen >= rdlen / 2);
- if (0 == (rdlen & 1)) {
- while (rdlen -= 2) {
- *wr++ = (u8)(((rd[0]-'A') << 4) | ((rd[1]-'A') & 0xF));
- rd += 2;
- if ('\x20' == *(wr-1)) {
- wr--;
- break;
- }
- }
- }
- return (size_t)(wr-owr);
- }
-
- static ptrdiff_t do_parse_names(nb_dgm *d, size_t len, parse_frame *f)
- {
- static nb_dgm_name name;
- char *orig = (char *)d + sizeof *d, /* byte after */
- *curr = orig;
- size_t l;
- /* attach and initialize */
- name.head = d;
- name.srcname = NULL;
- name.dstname = NULL;
- name.srcnamelen = 0;
- name.dstnamelen = 0;
- f->pass = &name;
- /* parse src name */
- l = memcspn(curr, len, "\0", 1);
- if (l >= len - 2) /* too long, no space for dest */
- return 0;
- name.srcname = curr;
- name.srcnamelen = l;
- curr += l, len -= l;
- /* skip \0 */
- l = memspn(curr, len, "\0", 1);
- curr += l, len -= l;
- /* parse dst name */
- l = memcspn(curr, len, "\0", 1);
- name.dstname = curr;
- name.dstnamelen = l;
- curr += l, len -= l;
- /* skip \0 */
- l = memspn(curr, len, "\0", 1);
- curr += l, len -= l;
-
- /* adjust strings */
- if (0 == name.srcnamelen || '\x20' != name.srcname[0] ||
- 0 == name.dstnamelen || '\x20' != name.dstname[0])
- return 0;
-
- name.srcname++;
- name.srcnamelen--;
- name.srcnamelen = nb_decode_name(name.srcname, name.srcnamelen, name.srcname, name.srcnamelen);
-
- name.dstname++;
- name.dstnamelen--;
- name.dstnamelen = nb_decode_name(name.dstname, name.dstnamelen, name.dstname, name.dstnamelen);
-
- return curr - orig;
- }
-
- /**
- * @return number of octets used by this protocol, or zero upon error
- */
- static size_t parse(char *buf, size_t len, parse_frame *f, const parse_status *st)
- {
- nb_dgm *d = (nb_dgm *)buf;
- size_t consumed;
- /* sanity check packet */
- if (sizeof *d > len)
- return 0;
- /* convert endianness */
- d->id = ntohs(d->id);
- d->len = ntohs(d->len);
- d->srcport = ntohs(d->srcport);
- d->off = ntohs(d->off);
- /* parse variable-length names off end */
- consumed = do_parse_names(d, len - sizeof *d, f);
- if (0 == consumed)
- return 0;
- consumed += sizeof *d;
- return consumed;
- }
-
- size_t dump(const parse_frame *f, int options, FILE *out)
- {
- const nb_dgm_name *n = f->pass;
- const nb_dgm *d = n->head;
- int bytes = fprintf(out,
- "%s "
- "msgtype=%u snt=%u frag(f=%u more=%u) id=0x%04x "
- "src=%u.%u.%u.%u:%hu "
- "len=%hu off=%hu srcname=\"",
- Iface_NBDgm.shortname,
- d->msgtype, d->snt, d->f, d->m, d->id,
- d->srcip[0], d->srcip[1], d->srcip[2], d->srcip[3], d->srcport,
- d->len, d->off);
- bytes += dump_chars((char *)n->srcname, n->srcnamelen, out);
- bytes += fprintf(out, "\" dstname=\"");
- bytes += dump_chars((char *)n->dstname, n->dstnamelen, out);
- bytes += fprintf(out, "\"\n");
- /* report */
- {
- char srcbuf[64],
- ipbuf[64];
- const parse_frame *fi = f-2;
- const ipv4 *ip = fi->off;
- assert(PROT_IPv4 == fi->id);
- ipv4_addr_format(ipbuf, sizeof ipbuf, ip->src);
- dump_chars_buf(srcbuf, sizeof srcbuf, (char *)n->srcname, n->srcnamelen);
- if ('\0' != srcbuf[0])
- rep_addr("4", ipbuf, "N", srcbuf, Iface_NBDgm.shortname, 1);
- }
- return (size_t)bytes;
- }
-
- static int init(void)
- {
- printf("offsetof(nb_dgm, id) -> %u\n",
- offsetof(nb_dgm, id));
- assert(2 == offsetof(nb_dgm, id));
- assert(4 == offsetof(nb_dgm, srcip));
- assert(8 == offsetof(nb_dgm, srcport));
- printf("offsetof(nb_dgm, srcport) -> %u\n",
- offsetof(nb_dgm, srcport));
- assert(10 == offsetof(nb_dgm, len));
- printf("offsetof(nb_dgm, len) -> %u\n",
- offsetof(nb_dgm, len));
- assert(12 == offsetof(nb_dgm, off));
- printf("sizeof Dgm -> %u\n", sizeof Dgm);
- assert(14 == sizeof Dgm);
- return 1;
- }
-
- #ifdef TEST
- int main(void)
- {
- return 0;
- }
- #endif