Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "headers.h"

typedef struct Args Args;

struct Args {
        ulong pcount;
        ulong poffset;
        ulong pdisplacement;
        ulong dcount;
        ulong doffset;
        ulong ddisplacement;
        uchar scount;
};

int
_smbtransactiondecodeprimary(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, int hasname, char **errmsgp)
{
        ushort poffset, doffset;

        if (h->wordcount < 14) {
                smbstringprint(errmsgp, "word count less than 14");
                return -1;
        }
        t->in.scount = pdata[13 * 2];
        if (h->wordcount != 14 + t->in.scount) {
                smbstringprint(errmsgp, "smbcomtransaction: word count invalid\n");
                return -1;
        }
        t->in.tpcount = smbnhgets(pdata); pdata += 2;
        t->in.tdcount = smbnhgets(pdata); pdata += 2;
        t->in.maxpcount = smbnhgets(pdata); pdata += 2;
        t->in.maxdcount = smbnhgets(pdata); pdata += 2;
        t->in.maxscount = *pdata++;
        pdata++;
        t->in.flags = smbnhgets(pdata); pdata += 2;
        pdata += 4; /* timeout */
        pdata += 2;
        t->in.pcount = smbnhgets(pdata); pdata += 2;
        poffset = smbnhgets(pdata); pdata += 2;
        t->in.dcount = smbnhgets(pdata); pdata += 2;
        doffset = smbnhgets(pdata); pdata += 2;
        pdata++; /* scount */
        pdata++; /* reserved */
        smbfree(&t->in.setup);
        if (t->in.scount) {
                int x;
                t->in.setup = smbemalloc(t->in.scount * sizeof(ushort));
                for (x = 0; x < t->in.scount; x++) {
                        t->in.setup[x] = smbnhgets(pdata);
                        pdata += 2;
                }
        }
        smbfree(&t->in.name);
        if (hasname && !smbbuffergetstring(b, h, SMB_STRING_PATH, &t->in.name)) {
                smbstringprint(errmsgp, "not enough bdata for name");
                return -1;
        }
        if (poffset + t->in.pcount > smbbufferwriteoffset(b)) {
                smbstringprint(errmsgp, "not enough bdata for parameters");
                return -1;
        }
        if (t->in.pcount > t->in.tpcount) {
                smbstringprint(errmsgp, "too many parameters");
                return -1;
        }
        smbfree(&t->in.parameters);
        t->in.parameters = smbemalloc(t->in.tpcount);
        memcpy(t->in.parameters, smbbufferpointer(b, poffset), t->in.pcount);
        if (doffset + t->in.dcount > smbbufferwriteoffset(b)) {
                smbstringprint(errmsgp, "not enough bdata for data");
                return -1;
        }
        if (t->in.dcount > t->in.tdcount) {
                smbstringprint(errmsgp, "too much data");
                return -1;
        }
        smbfree(&t->in.data);
        t->in.data = smbemalloc(t->in.tdcount);
        memcpy(t->in.data, smbbufferpointer(b, doffset), t->in.dcount);
        if (t->in.dcount < t->in.tdcount || t->in.pcount < t->in.tpcount)
                return 0;
        return 1;
}

int
decoderesponse(SmbTransaction *t, Args *a, SmbBuffer *b, char **errmsgp)
{
        if (t->out.tpcount > smbbufferwritemaxoffset(t->out.parameters)) {
                smbstringprint(errmsgp, "decoderesponse: too many parameters for buffer");
                return 0;
        }
        if (t->out.tdcount > smbbufferwritemaxoffset(t->out.data)) {
                smbstringprint(errmsgp, "decoderesponse: too much data for buffer");
                return 0;
        }
        if (a->pdisplacement + a->pcount > t->out.tpcount) {
                smbstringprint(errmsgp, "decoderesponse: more parameters than tpcount");
                return 0;
        }
        if (a->pdisplacement != smbbufferwriteoffset(t->out.parameters)) {
                smbstringprint(errmsgp, "decoderesponse: parameter displacement inconsistent");
                return 0;
        }
        if (a->ddisplacement + a->dcount > t->out.tdcount) {
                smbstringprint(errmsgp, "decoderesponse: more data than tdcount");
                return 0;
        }
        if (a->ddisplacement != smbbufferwriteoffset(t->out.data)) {
                smbstringprint(errmsgp, "decoderesponse: data displacement inconsistent");
                return 0;
        }
        assert(a->scount == 0);
        if (a->pcount) {
                if (!smbbufferreadskipto(b, a->poffset)) {
                        smbstringprint(errmsgp, "smbtransactiondecoderesponse: invalid parameter offset");
                        return 0;
                }
                if (!smbbuffercopy(t->out.parameters, b, a->pcount)) {
                        smbstringprint(errmsgp, "smbtransactiondecoderesponse: not enough data for parameters");
                        return 0;
                }
        }
        if (a->dcount) {
                if (!smbbufferreadskipto(b, a->doffset)) {
                        smbstringprint(errmsgp, "smbtransactiondecoderesponse: invalid data offset");
                        return 0;
                }
                if (!smbbuffercopy(t->out.data, b, a->dcount)) {
                        smbstringprint(errmsgp, "smbtransactiondecoderesponse: not enough data for data");
                        return 0;
                }
        }
        return 1;
}

int
smbtransactiondecoderesponse(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
{
        Args a;

        if (h->command != SMB_COM_TRANSACTION) {
                smbstringprint(errmsgp, "smbtransactiondecoderesponse: not an SMB_COM_TRANSACTION");
                return 0;
        }
        if (h->wordcount < 10) {
                smbstringprint(errmsgp, "smbtransactiondecoderesponse: word count less than 10");
                return -1;
        }
        t->out.tpcount = smbnhgets(pdata); pdata += 2;
        t->out.tdcount = smbnhgets(pdata); pdata += 2;
        pdata += 2;
        a.pcount = smbnhgets(pdata); pdata += 2;
        a.poffset =  smbnhgets(pdata); pdata += 2;
        a.pdisplacement = smbnhgets(pdata); pdata += 2;
        a.dcount = smbnhgets(pdata); pdata += 2;
        a.doffset =  smbnhgets(pdata); pdata += 2;
        a.ddisplacement = smbnhgets(pdata); pdata += 2;
        a.scount = *pdata;
        if (a.scount != h->wordcount - 10) {
                smbstringprint(errmsgp, "smbtransactiondecoderesponse: scount inconsistent");
                return 0;
        }
        return decoderesponse(t, &a, b, errmsgp);
}

int
smbtransactiondecoderesponse2(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
{
        Args a;

        if (h->command != SMB_COM_TRANSACTION2) {
                smbstringprint(errmsgp, "smbtransactiondecoderesponse2: not an SMB_COM_TRANSACTION2");
                return 0;
        }
        if (h->wordcount < 10) {
                smbstringprint(errmsgp, "smbtransactiondecoderesponse2: word count less than 10");
                return -1;
        }
        t->out.tpcount = smbnhgets(pdata); pdata += 2;
        t->out.tdcount = smbnhgets(pdata); pdata += 2;
        pdata += 2;
        a.pcount = smbnhgets(pdata); pdata += 2;
        a.poffset =  smbnhgets(pdata); pdata += 2;
        a.pdisplacement = smbnhgets(pdata); pdata += 2;
        a.dcount = smbnhgets(pdata); pdata += 2;
        a.doffset =  smbnhgets(pdata); pdata += 2;
        a.ddisplacement = smbnhgets(pdata); pdata += 2;
        a.scount = *pdata;
        if (a.scount != h->wordcount - 10) {
                smbstringprint(errmsgp, "smbtransactiondecoderesponse2: scount inconsistent");
                return 0;
        }
        return decoderesponse(t, &a, b, errmsgp);
}

int
smbtransactiondecodeprimary(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
{
        return _smbtransactiondecodeprimary(t, h, pdata, b, 1, errmsgp);
}

int
smbtransactiondecodeprimary2(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
{
        return _smbtransactiondecodeprimary(t, h, pdata, b, 0, errmsgp);
}

void
smbtransactionfree(SmbTransaction *t)
{
        free(t->in.parameters);
        free(t->in.data);
        free(t->in.setup);
        free(t->in.name);
        smbbufferfree(&t->out.parameters);
        smbbufferfree(&t->out.data);
}

static int
_transactionencodeprimary(SmbTransaction *t, uchar cmd, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob,
        uchar *wordcountp, ushort *bytecountp, char **errmsgp)
{
        SmbHeader mh;
        ulong countsfixupoffset, bytecountfixupoffset;
        int x;
        mh = *h;
        *wordcountp = mh.wordcount = 14 + t->in.scount;
        mh.flags &= ~SMB_FLAGS_SERVER_TO_REDIR;
        mh.command = cmd;
        if (!smbbufferputheader(ob, &mh, p)) {
        toosmall:
                smbstringprint(errmsgp, "output buffer too small");
                return 0;
        }
        if (t->in.tpcount > 65535 || t->in.tdcount > 65535 || t->in.maxpcount > 65535 || t->in.maxdcount > 65535) {
                smbstringprint(errmsgp, "counts too big");
                return 0;
        }
        if (!smbbufferputs(ob, t->in.tpcount)
                || !smbbufferputs(ob, t->in.tdcount)
                || !smbbufferputs(ob, t->in.maxpcount)
                || !smbbufferputs(ob, t->in.maxdcount)
                || !smbbufferputb(ob, t->in.maxscount)
                || !smbbufferputb(ob, 0)
                || !smbbufferputs(ob, t->in.flags)
                || !smbbufferputl(ob, 0)
                || !smbbufferputs(ob, 0))
                goto toosmall;
        countsfixupoffset = smbbufferwriteoffset(ob);
        if (!smbbufferputs(ob, 0)
                || !smbbufferputs(ob, 0)
                || !smbbufferputs(ob, 0)
                || !smbbufferputs(ob, 0))
                goto toosmall;
        if (!smbbufferputb(ob, t->in.scount)
                || !smbbufferputb(ob, 0))
                goto toosmall;
        for (x = 0; x < t->in.scount; x++)
                if (!smbbufferputs(ob, t->in.setup[x]))
                        goto toosmall;
        bytecountfixupoffset = smbbufferwriteoffset(ob);
        if (!smbbufferputs(ob, 0))
                goto toosmall;
        smbbufferwritelimit(ob, smbbufferwriteoffset(ob) + 65535);
        if (!smbbufferputstring(ob, p, SMB_STRING_UPCASE, t->in.name))
                goto toosmall;
        if (t->in.pcount < t->in.tpcount) {
                ulong align = smbbufferwriteoffset(ob) & 1;
                ulong pthistime;
                pthistime = smbbufferwritespace(ob) - align;
                if (pthistime > t->in.tpcount - t->in.pcount)
                        pthistime = t->in.tpcount - t->in.pcount;
                if (pthistime > 65535)
                        pthistime = 65535;
                if (smbbufferwriteoffset(ob) > 65535)
                        pthistime = 0;
                if (pthistime) {
                        assert(smbbufferalignl2(ob, 0));
                        assert(smbbufferoffsetputs(ob, countsfixupoffset, pthistime));
                        assert(smbbufferoffsetputs(ob, countsfixupoffset + 2, smbbufferwriteoffset(ob)));
                        assert(smbbufferputbytes(ob, t->in.parameters + t->in.pcount, pthistime));
                }
                t->in.pcount += pthistime;
        }
        if (t->in.dcount < t->in.tdcount) {
                ulong align = smbbufferwriteoffset(ob) & 1;
                ulong dthistime;
                dthistime = smbbufferwritespace(ob) - align;
                if (dthistime > t->in.tdcount - t->in.dcount)
                        dthistime = t->in.tdcount - t->in.dcount;
                if (dthistime > 65535)
                        dthistime = 65535;
                if (smbbufferwriteoffset(ob) > 65535)
                        dthistime = 0;
                if (dthistime) {
                        assert(smbbufferalignl2(ob, 0));
                        assert(smbbufferoffsetputs(ob, countsfixupoffset + 4, dthistime));
                        assert(smbbufferoffsetputs(ob, countsfixupoffset + 6, smbbufferwriteoffset(ob)));
                        assert(smbbufferputbytes(ob, t->in.data + t->in.dcount, dthistime));
                }
                t->in.dcount += dthistime;
        }
        *bytecountp = smbbufferwriteoffset(ob) - bytecountfixupoffset - 2;
        assert(smbbufferoffsetputs(ob, bytecountfixupoffset, *bytecountp));
        return 1;
}

int
smbtransactionencodeprimary(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob,
        uchar *wordcountp, ushort *bytecountp, char **errmsgp)
{
        return _transactionencodeprimary(t, SMB_COM_TRANSACTION, h, p,ob, wordcountp, bytecountp, errmsgp);
};

int
smbtransactionencodeprimary2(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob,
        uchar *wordcountp, ushort *bytecountp, char **errmsgp)
{
        return _transactionencodeprimary(t, SMB_COM_TRANSACTION2, h, p,ob, wordcountp, bytecountp, errmsgp);
};

int
_transactionencoderesponse(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, uchar cmd,
        char **errmsgp)
{
        SmbHeader mh;
        ulong countsfixupoffset, bytecountfixupoffset;
        int palign, dalign;
        ulong pbytecount, dbytecount;
        ulong poffset, doffset;

        if (t->in.maxpcount > 65535 || t->in.maxdcount > 65535) {
                smbstringprint(errmsgp, "counts too big");
                return 0;
        }
        mh = *h;
        mh.wordcount = 10;
        mh.flags &= ~SMB_FLAGS_SERVER_TO_REDIR;
        mh.command = cmd;
        mh.errclass = SUCCESS;
        mh.error = SUCCESS;
        if (!smbbufferputheader(ob, &mh, p)
                || !smbbufferputs(ob, smbbufferwriteoffset(t->out.parameters))
                || !smbbufferputs(ob, smbbufferwriteoffset(t->out.data))
                || !smbbufferputs(ob, 0)) {
        toosmall:
                smbstringprint(errmsgp, "output buffer too small");
                goto toosmall;
        }
        countsfixupoffset = smbbufferwriteoffset(ob);
        if (!smbbufferputbytes(ob, nil, 6 * sizeof(ushort))
                || !smbbufferputb(ob, 0)        // scount == 0
                || !smbbufferputb(ob, 0))       // reserved2
                goto toosmall;
        /* now the byte count */
        bytecountfixupoffset = smbbufferwriteoffset(ob);
        if (!smbbufferputs(ob, 0))
                goto toosmall;
        smbbufferwritelimit(ob, smbbufferwriteoffset(ob) + 65535);
        palign = bytecountfixupoffset & 1;
        if (palign && !smbbufferputb(ob, 0))
                goto toosmall;
        pbytecount = smbbufferreadspace(t->out.parameters);
        if (pbytecount > smbbufferwritespace(ob))
                pbytecount = smbbufferwritespace(ob);
        poffset = smbbufferwriteoffset(ob);
        if (poffset > 65535)
                goto toosmall;
        if (!smbbufferputbytes(ob, smbbufferreadpointer(t->out.parameters), pbytecount))
                goto toosmall;
        dalign = smbbufferwritespace(ob) > 0 && (smbbufferwriteoffset(ob) & 1) != 0;
        if (dalign && !smbbufferputb(ob, 0))
                goto toosmall;
        dbytecount = smbbufferreadspace(t->out.data);
        if (dbytecount > smbbufferwritespace(ob))
                dbytecount = smbbufferwritespace(ob);
        doffset = smbbufferwriteoffset(ob);
        if (doffset > 65535)
                goto toosmall;
        if (!smbbufferputbytes(ob, smbbufferreadpointer(t->out.data), dbytecount))
                goto toosmall;
        if (!smbbufferoffsetputs(ob, bytecountfixupoffset, palign + pbytecount + dalign + dbytecount)
                || !smbbufferoffsetputs(ob, countsfixupoffset, pbytecount)
                || !smbbufferoffsetputs(ob, countsfixupoffset + 2, poffset)
                || !smbbufferoffsetputs(ob, countsfixupoffset + 4, smbbufferreadoffset(t->out.parameters))
                || !smbbufferoffsetputs(ob, countsfixupoffset + 6, dbytecount)
                || !smbbufferoffsetputs(ob, countsfixupoffset + 8, doffset)
                || !smbbufferoffsetputs(ob, countsfixupoffset + 10, smbbufferreadoffset(t->out.data)))
                goto toosmall;
        assert(smbbufferoffsetputs(ob, bytecountfixupoffset, smbbufferwriteoffset(ob) - bytecountfixupoffset - 2));
        smbbuffergetbytes(t->out.parameters, nil, pbytecount);
        smbbuffergetbytes(t->out.data, nil, dbytecount);
        return 1;
}

int
smbtransactionencoderesponse(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, char **errmsgp)
{
        return _transactionencoderesponse(t, h, p, ob, SMB_COM_TRANSACTION, errmsgp);
}

int
smbtransactionencoderesponse2(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, char **errmsgp)
{
        return _transactionencoderesponse(t, h, p, ob, SMB_COM_TRANSACTION2, errmsgp);
}

int
smbtransactionrespond(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, SmbTransactionMethod *method, void *magic, char **errmsgp)
{
        /* generate one or more responses */
        while (smbbufferreadspace(t->out.parameters) || smbbufferreadspace(t->out.data)) {
                assert(method->encoderesponse);
                if (!(*method->encoderesponse)(t, h, p, ob, errmsgp))
                        return 0;
                assert(method->sendresponse);
                if (!(*method->sendresponse)(magic, ob, errmsgp))
                        return 0;
        }
        return 1;
}

int
smbtransactionnbdgramsend(void *magic, SmbBuffer *ob, char **errmsgp)
{
        NbDgramSendParameters *p = magic;
//print("sending to %B\n", p->to);
//nbdumpdata(smbbufferreadpointer(ob), smbbufferreadspace(ob));
        if (!nbdgramsend(p, smbbufferreadpointer(ob), smbbufferreadspace(ob))) {
                smbstringprint(errmsgp, "dgram send failed");
                return 0;
        }
        return 1;
}

SmbTransactionMethod smbtransactionmethoddgram = {
        .encodeprimary = smbtransactionencodeprimary,
        .sendrequest = smbtransactionnbdgramsend,
        .encoderesponse = smbtransactionencoderesponse,
};

int
smbtransactionexecute(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *iob, SmbTransactionMethod *method, void *magic, SmbHeader *rhp, char **errmsgp)
{
        uchar sentwordcount;
        ushort sentbytecount;
        SmbHeader rh;
        smbbufferreset(iob);
        if (!(*method->encodeprimary)(t, h, p, iob, &sentwordcount, &sentbytecount, errmsgp))
                return 0;
//      smblogprint(-1, "sent...\n");
//      smblogdata(-1, smblogprint, smbbufferreadpointer(iob), smbbufferreadspace(iob));
        if (!(*method->sendrequest)(magic, iob, errmsgp))
                return 0;
        if (t->in.pcount < t->in.tpcount || t->in.dcount < t->in.tdcount) {
                uchar wordcount;
                ushort bytecount;
                /* secondary needed */
                if (method->encodesecondary == nil || method->receiveintermediate == nil) {
                        smbstringprint(errmsgp, "buffer too small and secondaries not allowed");
                        return 0;
                }
                if (!(*method->receiveintermediate)(magic, &wordcount, &bytecount, errmsgp))
                        return 0;
                if (sentwordcount != wordcount || sentbytecount != bytecount) {
                        smbstringprint(errmsgp, "server intermediate reply counts differ");
                        return 0;
                }
                do {
                        if (!(*method->encodesecondary)(t, h, iob, errmsgp))
                                return 0;
                        if (!(*method->sendrequest)(magic, iob, errmsgp))
                                return 0;
                } while (t->in.pcount < t->in.tpcount || t->in.dcount < t->in.tdcount);
        }
        if (method->receiveresponse == nil || method->decoderesponse == nil)
                return 1;
        do {
                uchar *pdata;
                ushort bytecount;

                if (!(*method->receiveresponse)(magic, iob, errmsgp))
                        return 0;
                if (!smbbuffergetheader(iob, &rh, &pdata, &bytecount)) {
                        smbstringprint(errmsgp, "smbtransactionexecute: invalid response header");
                        return 0;
                }
                if (!smbcheckheaderdirection(&rh, 1, errmsgp))
                        return 0;
                if (rh.errclass != SUCCESS) {
                        smbstringprint(errmsgp, "smbtransactionexecute: remote error %d/%d", rh.errclass, rh.error);
                        return 0;
                }
                if (!smbbuffertrimreadlen(iob, bytecount)) {
                        smbstringprint(errmsgp, "smbtransactionexecute: invalid bytecount");
                        return 0;
                }
//              smblogprint(-1, "received...\n");
//              smblogdata(-1, smblogprint, smbbufferreadpointer(iob), smbbufferreadspace(iob));
                if (!(*method->decoderesponse)(t, &rh, pdata, iob, errmsgp))
                        return 0;
        } while (smbbufferwriteoffset(t->out.parameters) < t->out.tpcount || smbbufferwriteoffset(t->out.data) < t->out.tdcount);
        if (rhp)
                *rhp = rh;
        return 1;
}