Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <ip.h>
#include <thread.h>
#include "netbios.h"

static struct {
        int thread;
        QLock;
        int fd;
} udp = { -1 };

typedef struct Listen Listen;

struct Listen {
        NbName to;
        int (*deliver)(void *magic, NbDgram *s);
        void *magic;
        Listen *next;
};

static struct {
        QLock;
        Listen *head;
} listens;

static void
udplistener(void *)
{
//print("udplistener - starting\n");
        for (;;) {
                uchar msg[Udphdrsize + 576];
                int len = read(udp.fd, msg, sizeof(msg));
                if (len < 0)
                        break;
                if (len >= nbudphdrsize) {
                        NbDgram s;
//                      Udphdr *uh;
                        uchar *p;
                        int n;

//                      uh = (Udphdr*)msg;
                        p = msg + nbudphdrsize;
                        len -= nbudphdrsize;
                        n = nbdgramconvM2S(&s, p, p + len);
                        if (n) {
                                switch (s.type) {
                                case NbDgramError:
                                        print("nbdgramlisten: error: ip %I port %d code 0x%.2ux\n", s.srcip, s.srcport, s.error.code);
                                        break;
                                case NbDgramDirectUnique:
                                case NbDgramDirectGroup:
                                case NbDgramBroadcast: {
                                        int delivered = 0;
                                        Listen **lp, *l;
                                        if ((s.flags & NbDgramMore) || s.datagram.offset != 0)
                                                break;
                                        if (!nbnameisany(s.datagram.dstname)
                                                && !nbnametablefind(s.datagram.dstname, 0)) {
/* - only do this if a broadcast node, and can tell when packets are broadcast...
                                                s.flags &= 3;
                                                ipmove(s.srcip, nbglobals.myipaddr);
                                                s.srcport = NbDgramPort;
                                                s.type = NbDgramError;  
                                                s.error.code = NbDgramErrorDestinationNameNotPresent;
                                                nbdgramsendto(uh->raddr, nhgets(uh->rport), &s);
*/
                                                break;
                                        }
                                        qlock(&listens);
                                        for (lp = &listens.head; (l = *lp) != nil;) {
                                                if (nbnameisany(l->to) || nbnameequal(l->to, s.datagram.dstname)) {
                                                        switch ((*l->deliver)(l->magic, &s)) {
                                                        case 0:
                                                                delivered = 1;
                                                                /* fall through */
                                                        case -1:
                                                                *lp = l->next;
                                                                free(l);
                                                                continue;
                                                        default:
                                                                delivered = 1;
                                                                break;
                                                        }
                                                }
                                                lp = &l->next;
                                        }
                                        qunlock(&listens);
                                        USED(delivered);
                                }
                                default:
                                        ;
                                }
                        }
                }
        }
print("udplistener - exiting\n");
        qlock(&udp);
        udp.thread = -1;
        qunlock(&udp);
}

static char *
startlistener(void)
{
        qlock(&udp);
        if (udp.thread < 0) {
                char *e;
                e = nbudpannounce(NbDgramPort, &udp.fd);
                if (e) {
                        qunlock(&udp);
                        return e;
                }
                udp.thread = proccreate(udplistener, nil, 16384);
        }
        qunlock(&udp);
        return nil;
}

char *
nbdgramlisten(NbName to, int (*deliver)(void *magic, NbDgram *s), void *magic)
{
        Listen *l;
        char *e;
        nbnametablefind(to, 1);
        e = startlistener();
        if (e)
                return e;
        l = nbemalloc(sizeof(Listen));
        nbnamecpy(l->to, to);
        l->deliver = deliver;
        l->magic = magic;
        qlock(&listens);
        l->next = listens.head;
        listens.head = l;
        qunlock(&listens);
        return 0;
}

int
nbdgramsendto(uchar *ipaddr, ushort port, NbDgram *s)
{
        Udphdr *u;
        uchar msg[NbDgramMaxPacket + Udphdrsize];
        int l;
        int rv;
        char *e;

        e = startlistener();
        if (e != nil)
                return 0;

        l = nbdgramconvS2M(msg + nbudphdrsize, msg + sizeof(msg), s);
        if (l == 0) {
                print("conv failed\n");
                return 0;
        }
        u = (Udphdr *)msg;
        ipmove(u->laddr, nbglobals.myipaddr);
        hnputs(u->lport, NbDgramPort);
        ipmove(u->raddr, ipaddr);
        hnputs(u->rport, port);
//nbdumpdata(msg, l + nbudphdrsize);
//print("transmitting\n");
        rv = write(udp.fd, msg, l + nbudphdrsize);
//print("rv %d l %d hdrsize %d error %r\n", rv, l, nbudphdrsize);
        return rv == l + nbudphdrsize;
}

static struct {
        Lock;
        ushort id;
} id;

static ushort
nextdgramid(void)
{
        ushort v;
        lock(&id);
        v = id.id++;
        unlock(&id);
        return v;
}

int
nbdgramsend(NbDgramSendParameters *p, uchar *data, long datalen)
{
        NbDgram s;
        uchar dstip[IPaddrlen];
        s.type = p->type;
        switch (p->type) {
        case NbDgramBroadcast:
        case NbDgramDirectGroup:
                ipmove(dstip, nbglobals.bcastaddr);
                break;
        case NbDgramDirectUnique:
                if (!nbnameresolve(p->to, dstip)) {
                        werrstr("nbdgramsend: name resolution failed");
                        return 0;
                }
                break;
        default:
                werrstr("nbdgramsend: illegal datagram type");
                return 0;
        }
        s.flags = NbDgramFirst;
        s.id = nextdgramid();
        ipmove(s.srcip, nbglobals.myipaddr);
        s.srcport = NbDgramPort;
        s.datagram.offset = 0;
        s.datagram.data = data;
        s.datagram.length = datalen;
        nbnamecpy(s.datagram.dstname, p->to);
        nbnamecpy(s.datagram.srcname, nbglobals.myname);
        return nbdgramsendto(dstip, NbDgramPort, &s);
}