Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * netbios dial, read, write
 */

#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "cifs.h"

enum {
        MAXNBPKT                = 8096,         /* max netbios packet size */
        NBquery                 = 0,            /* packet type - query */

        NBAdapterStatus         = 0x21,         /* get host interface info */
        NBInternet              = 1,            /* scope for info */

        NBmessage               = 0x00,         /* Netbios packet types */
        NBrequest               = 0x81,
        NBpositive,
        NBnegative,
        NBretarget,
        NBkeepalive,

        ISgroup                 = 0x8000,
};


static char *NBerr[] = {
        [0]     "not listening on called name",
        [1]     "not listening for calling name",
        [2]     "called name not present",
        [3]     "insufficient resources",
        [15]    "unspecified error"
};


static ulong
GL32(uchar **p)
{
        ulong n;

        n  = *(*p)++;
        n |= *(*p)++ << 8;
        n |= *(*p)++ << 16;
        n |= *(*p)++ << 24;
        return n;
}

static ushort
GL16(uchar **p)
{
        ushort n;

        n  = *(*p)++;
        n |= *(*p)++ << 8;
        return n;
}

void
Gmem(uchar **p, void *v, int n)
{
        uchar *str = v;

        while(n--)
                *str++ = *(*p)++;
}


static ulong
GB32(uchar **p)
{
        ulong n;

        n  = *(*p)++ << 24;
        n |= *(*p)++ << 16;
        n |= *(*p)++ << 8;
        n |= *(*p)++;
        return n;
}

static ushort
GB16(uchar **p)
{
        ushort n;

        n  = *(*p)++ << 8;
        n |= *(*p)++;
        return n;
}

static uchar
G8(uchar **p)
{
        return *(*p)++;
}

static void
PB16(uchar **p, uint n)
{
        *(*p)++ = n >> 8;
        *(*p)++ = n;
}

static void
P8(uchar **p, uint n)
{
        *(*p)++ = n;
}


static void
nbname(uchar **p, char *name, char pad)
{
        char c;
        int i;
        int done = 0;

        *(*p)++ = 0x20;
        for(i = 0; i < 16; i++) {
                c = pad;
                if(!done && name[i] == '\0')
                        done = 1;
                if(!done)
                        c = toupper(name[i]);
                *(*p)++ = ((uchar)c >> 4) + 'A';
                *(*p)++ = (c & 0xf) + 'A';
        }
        *(*p)++ = 0;
}

int
calledname(char *host, char *name)
{
        char *addr;
        uchar buf[1024], *p;
        static char tmp[20];
        int num, flg, svs, j, i, fd, trn;

        trn = (getpid() ^ time(0)) & 0xffff;
        if((addr = netmkaddr(host, "udp", "137")) == nil)
                return -1;

        if((fd = dial(addr, "137", 0, 0)) < 0)
                return -1;
        p = buf;

        PB16(&p, trn);                  /* TRNid */
        P8(&p, 0);                      /* flags */
        P8(&p, 0x10);                   /* type */
        PB16(&p, 1);                    /* # questions */
        PB16(&p, 0);                    /* # answers */
        PB16(&p, 0);                    /* # authority RRs */
        PB16(&p, 0);                    /* # Aditional RRs */
        nbname(&p, "*", 0);
        PB16(&p, NBAdapterStatus);
        PB16(&p, NBInternet);

        if(Debug && strstr(Debug, "dump"))
                xd(nil, buf, p-buf);

        if(write(fd, buf, p-buf) != p-buf)
                return -1;

        p = buf;
        for(i = 0; i < 3; i++){
                memset(buf, 0, sizeof(buf));
                alarm(NBNSTOUT);
                read(fd, buf, sizeof(buf));
                alarm(0);
                if(GB16(&p) == trn)
                        break;
        }
        close(fd);
        if(i >= 3)
                return -1;

        p = buf +56;
        num = G8(&p);                   /* number of names */

        for(i = 0; i < num; i++){
                memset(tmp, 0, sizeof(tmp));
                Gmem(&p, tmp, 15);
                svs = G8(&p);
                flg = GB16(&p);
                for(j = 14; j >= 0 && tmp[j] == ' '; j--)
                        tmp[j] = 0;
                if(svs == 0 && !(flg & ISgroup))
                        strcpy(name, tmp);
        }
        return 0;
}


int
nbtdial(char *addr, char *called, char *sysname)
{
        char redir[20];
        uchar *p, *lenp, buf[1024];
        int type, len, err, fd, nkeepalive, nretarg;

        nretarg = 0;
        nkeepalive = 0;
Redial:
        if((addr = netmkaddr(addr, "tcp", "139")) == nil ||
            (fd = dial(addr, 0, 0, 0)) < 0)
                return -1;

        memset(buf, 0, sizeof(buf));

        p = buf;
        P8(&p, NBrequest);              /* type */
        P8(&p, 0);                      /* flags */
        lenp = p; PB16(&p, 0);          /* length placeholder */
        nbname(&p, called, ' ');        /* remote NetBios name */
        nbname(&p, sysname, ' ');       /* our machine name */
        PB16(&lenp, p-lenp -2);         /* length re-write */

        if(Debug && strstr(Debug, "dump"))
                xd(nil, buf, p-buf);
        if(write(fd, buf, p-buf) != p-buf)
                goto Error;
Reread:
        p = buf;
        memset(buf, 0, sizeof(buf));
        if(readn(fd, buf, 4) < 4)
                goto Error;

        type = G8(&p);
        G8(&p);                         /* flags */
        len = GB16(&p);

        if(readn(fd, buf +4, len -4) < len -4)
                goto Error;

        if(Debug && strstr(Debug, "dump"))
                xd(nil, buf, len+4);

        switch(type) {
        case NBpositive:
                return fd;
        case NBnegative:
                if(len < 1) {
                        werrstr("nbdial: bad error pkt");
                        goto Error;
                }
                err = G8(&p);
                if(err < 0 || err > nelem(NBerr) || NBerr[err] == nil)
                        werrstr("NBT: %d - unknown error", err);
                else
                        werrstr("NBT: %s", NBerr[err]);

                goto Error;
        case NBkeepalive:
                if(++nkeepalive >= 16){
                        werrstr("nbdial: too many keepalives");
                        goto Error;
                }
                goto Reread;

        case NBretarget:
                if(++nretarg >= 16) {
                        werrstr("nbdial: too many redirects");
                        goto Error;
                }
                if(len < 4) {
                        werrstr("nbdial: bad redirect pkt");
                        goto Error;
                }
                sprint(redir, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
                addr = redir;
                goto Redial;

        default:
                werrstr("nbdial: 0x%x - unknown packet in netbios handshake", type);
                goto Error;
        }
Error:
        close(fd);
        return -1;
}

void
nbthdr(Pkt *p)
{
        p->pos = p->buf;
        memset(p->buf, 0xa5, MTU);

        p8(p, NBmessage);               /* type */
        p8(p, 0);                       /* flags */
        pb16(p, 0);                     /* length (filled in later) */
}

int
nbtrpc(Pkt *p)
{
        int len, got, type, nkeep;

        len = p->pos - p->buf;

        p->pos = p->buf +2;
        pb16(p, len - NBHDRLEN);        /* length */

        if(Debug && strstr(Debug, "dump"))
                xd("tx", p->buf, len);

        alarm(NBRPCTOUT);
        if(write(p->s->fd, p->buf, len) != len){
                werrstr("nbtrpc: write failed - %r");
                alarm(0);
                return -1;
        }

        nkeep = 0;
retry:
        p->pos = p->buf;
        memset(p->buf, 0xa5, MTU);

        got = readn(p->s->fd, p->buf, NBHDRLEN);

        if(got < NBHDRLEN){
                werrstr("nbtrpc: short read - %r");
                alarm(0);
                return -1;
        }
        p->eop = p->buf + got;

        type = g8(p);                   /* NBT type (session) */
        if(type == NBkeepalive){
                if(++nkeep > 16) {
                        werrstr("nbtrpc: too many keepalives (%d attempts)", nkeep);
                        alarm(0);
                        return -1;
                }
                goto retry;
        }

        g8(p);                          /* NBT flags (none) */

        len = gb16(p);                  /* NBT payload length */
        if((len +NBHDRLEN) > MTU){
                werrstr("nbtrpc: packet bigger than MTU, (%d > %d)", len, MTU);
                alarm(0);
                return -1;
        }

        got = readn(p->s->fd, p->buf +NBHDRLEN, len);
        alarm(0);

        if(Debug && strstr(Debug, "dump"))
                xd("rx", p->buf, got +NBHDRLEN);

        if(got < 0)
                return -1;
        p->eop = p->buf + got +NBHDRLEN;
        return got+NBHDRLEN;
}


void
xd(char *str, void *buf, int n)
{
        int fd, flg, flags2, cmd;
        uint sum;
        long err;
        uchar *p, *end;

        if(n == 0)
                return;

        p = buf;
        end = (uchar *)buf +n;

        if(Debug && strstr(Debug, "log") != nil){
                if((fd = open("pkt.log", ORDWR)) == -1)
                        return;
                seek(fd, 0, 2);
                fprint(fd, "%d  ", 0);
                while(p < end)
                        fprint(fd, "%02x ", *p++);
                fprint(fd, "\n");
                close(fd);
                return;
        }

        if(!str)
                goto Raw;

        p = (uchar *)buf + 4;
        if(GL32(&p) == 0x424d53ff){
                buf = (uchar *)buf + 4;
                n -= 4;
        }
        end = (uchar *)buf + n;

        sum = 0;
        p = buf;
        while(p < end)
                sum += *p++;
        p = buf;

        fprint(2, "%s : len=%ud sum=%d\n", str, n, sum);

        fprint(2, "mag=0x%ulx ", GL32(&p));
        fprint(2, "cmd=0x%ux ", cmd = G8(&p));
        fprint(2, "err=0x%ulx ", err=GL32(&p));
        fprint(2, "flg=0x%02ux ", flg = G8(&p));
        fprint(2, "flg2=0x%04ux\n", flags2= GL16(&p));
        fprint(2, "dfs=%s\n", (flags2 & FL2_DFS)? "y": "n");

        fprint(2, "pidl=%ud ", GL16(&p));
        fprint(2, "res=%uld ", GL32(&p));
        fprint(2, "sid=%ud ", GL16(&p));
        fprint(2, "seq=0x%ux ", GL16(&p));
        fprint(2, "pad=%ud ", GL16(&p));

        fprint(2, "tid=%ud ", GL16(&p));
        fprint(2, "pid=%ud ", GL16(&p));
        fprint(2, "uid=%ud ", GL16(&p));
        fprint(2, "mid=%ud\n", GL16(&p));

        if(cmd == 0x32 && (flg & 0x80) == 0){           /* TRANS 2, TX */
                fprint(2, "words=%ud ", G8(&p));
                fprint(2, "totparams=%ud ", GL16(&p));
                fprint(2, "totdata=%ud ", GL16(&p));
                fprint(2, "maxparam=%ud ", GL16(&p));
                fprint(2, "maxdata=%ud\n", GL16(&p));
                fprint(2, "maxsetup=%ud ", G8(&p));
                fprint(2, "reserved=%ud ", G8(&p));
                fprint(2, "flags=%ud ", GL16(&p));
                fprint(2, "timeout=%uld\n", GL32(&p));
                fprint(2, "reserved=%ud ", GL16(&p));
                fprint(2, "paramcnt=%ud ", GL16(&p));
                fprint(2, "paramoff=%ud ", GL16(&p));
                fprint(2, "datacnt=%ud ", GL16(&p));
                fprint(2, "dataoff=%ud ", GL16(&p));
                fprint(2, "setupcnt=%ud ", G8(&p));
                fprint(2, "reserved=%ud\n", G8(&p));
                fprint(2, "trans2=0x%02x ", GL16(&p));
                fprint(2, "data-words=%d ", G8(&p));
                fprint(2, "padding=%d\n", G8(&p));
        }
        if(cmd == 0x32 && (flg & 0x80) == 0x80){        /* TRANS 2, RX */
                fprint(2, "words=%ud ", G8(&p));
                fprint(2, "totparams=%ud ", GL16(&p));
                fprint(2, "totdata=%ud ", GL16(&p));
                fprint(2, "reserved=%ud ", GL16(&p));
                fprint(2, "paramcnt=%ud\n", GL16(&p));
                fprint(2, "paramoff=%ud ", GL16(&p));
                fprint(2, "paramdisp=%ud ", GL16(&p));
                fprint(2, "datacnt=%ud\n", GL16(&p));
                fprint(2, "dataoff=%ud ", GL16(&p));
                fprint(2, "datadisp=%ud ", GL16(&p));
                fprint(2, "setupcnt=%ud ", G8(&p));
                fprint(2, "reserved=%ud\n", G8(&p));
        }
        if(err)
                if(flags2 & FL2_NT_ERRCODES)
                        fprint(2, "err=%s\n", nterrstr(err));
                else
                        fprint(2, "err=%s\n", doserrstr(err));
Raw:
        fprint(2, "\n");
        for(; p < end; p++){
                if((p - (uchar *)buf) % 16 == 0)
                        fprint(2, "\n%06lx\t", p - (uchar *)buf);
                if(isprint((char)*p))
                        fprint(2, "%c  ", (char )*p);
                else
                        fprint(2, "%02ux ", *p);
        }
        fprint(2, "\n");
}