Generate ADC PID more reliably.
[doldaconnect.git] / daemon / fnet-adc.c
index 459ef9a..c874b6e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Dolda Connect - Modular multiuser Direct Connect-style client
- *  Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
+ *  Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
  *  
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <stdarg.h>
 #include <string.h>
 #include <netinet/in.h>
@@ -58,7 +59,13 @@ struct qcmd {
     wchar_t **args;
 };
 
+struct qcmdqueue {
+    struct qcmd *f, *l;
+    int size;
+};
+
 struct adchub {
+    struct socket *sk;
     char *inbuf;
     size_t inbufdata, inbufsize;
     wchar_t *sid;
@@ -68,7 +75,7 @@ struct adchub {
     iconv_t ich;
     int state;
     struct wcspair *hubinf;
-    struct qcmd *queue;
+    struct qcmdqueue queue;
 };
 
 static wchar_t *eoc;
@@ -162,26 +169,31 @@ static void sendadc(struct socket *sk, int sep, ...)
     va_end(args);
 }
 
-static struct qcmd *newqcmd(struct qcmd **queue, wchar_t **args)
+static struct qcmd *newqcmd(struct qcmdqueue *queue, wchar_t **args)
 {
     struct qcmd *new;
     
-    while(*queue != NULL)
-       queue = &((*queue)->next);
     new = smalloc(sizeof(*new));
     new->next = NULL;
     new->args = args;
-    *queue = new;
+    if(queue->l == NULL)
+       queue->f = new;
+    else
+       queue->l->next = new;
+    queue->l = new;
+    queue->size++;
     return(new);
 }
 
-static struct qcmd *ulqcmd(struct qcmd **queue)
+static struct qcmd *ulqcmd(struct qcmdqueue *queue)
 {
     struct qcmd *ret;
     
-    if((ret = *queue) == NULL)
+    if((ret = queue->f) == NULL)
        return(NULL);
-    *queue = ret->next;
+    if((queue->f = ret->next) == NULL)
+       queue->l = NULL;
+    queue->size--;
     return(ret);
 }
 
@@ -192,7 +204,14 @@ static void freeqcmd(struct qcmd *qcmd)
 }
 
 #define ADC_CMDFN(name) static void name(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv)
-#define ADC_CMDCOM struct socket *sk = fn->sk; struct adchub *hub = fn->data;
+#ifdef __GNUC__
+#define UNUSED __attribute__ ((unused))
+#else
+#define UNUSED
+#endif
+#define ADC_CMDCOM \
+       struct adchub *hub UNUSED = fn->data; \
+       struct socket *sk UNUSED = hub->sk;
 
 ADC_CMDFN(cmd_sup)
 {
@@ -279,14 +298,14 @@ static void dispatch(struct qcmd *qcmd, struct fnetnode *fn)
        if(!wcscmp(cmd->name, qcmd->args[0] + 1)) {
            if(argc < cmd->minargs)
                return;
-           cmd->func(fn, cmdnm, sender, argc, qcmd->args);
+           cmd->func(fn, cmdnm, sender, argc, argv);
            return;
        }
     }
     flog(LOG_DEBUG, "unknown adc command: %ls", qcmd->args[0]);
 }
 
-static int hubread(struct socket *sk, struct fnetnode *fn)
+static void hubread(struct socket *sk, struct fnetnode *fn)
 {
     int ret;
     struct adchub *hub;
@@ -296,7 +315,7 @@ static int hubread(struct socket *sk, struct fnetnode *fn)
     
     hub = fn->data;
     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
-       return(0);
+       return;
     if(hub->inbufdata > 1024)
        hub->inbufdata = 0;
     bufcat(hub->inbuf, newbuf, datalen);
@@ -321,14 +340,14 @@ static int hubread(struct socket *sk, struct fnetnode *fn)
            if(errno == EILSEQ) {
                flog(LOG_DEBUG, "adc fnetnode %i sent illegal utf-8 sequence", fn->id);
                killfnetnode(fn);
-               return(0);
+               return;
            } else if(errno == EINVAL) {
                break;
            } else if(errno == E2BIG) {
                /* continue; */
            } else {
                flog(LOG_WARNING, "bug? iconv returned unexpected error: %s", strerror(errno));
-               return(0);
+               return;
            }
        }
     }
@@ -337,39 +356,38 @@ static int hubread(struct socket *sk, struct fnetnode *fn)
        newqcmd(&hub->queue, parseadc(hub->cb));
        memmove(hub->cb, p, (hub->cbdata -= (p - hub->cb)) * sizeof(*(hub->cb)));
     }
-    return(0);
 }
 
-static int huberr(struct socket *sk, int err, struct fnetnode *fn)
+static void huberr(struct socket *sk, int err, struct fnetnode *fn)
 {
     killfnetnode(fn);
-    return(0);
 }
 
-static void hubconnect(struct fnetnode *fn)
+static void hubconnect(struct fnetnode *fn, struct socket *sk)
 {
     struct adchub *hub;
     
-    CBREG(fn->sk, socket_read, (int (*)(struct socket *, void *))hubread, (void (*)(void *))putfnetnode, fn);
-    CBREG(fn->sk, socket_err, (int (*)(struct socket *, int, void *))huberr, (void (*)(void *))putfnetnode, fn);
+    sk->readcb = (void (*)(struct socket *, void *))hubread;
+    sk->errcb = (void (*)(struct socket *, int, void *))huberr;
+    sk->data = fn;
     
     hub = smalloc(sizeof(*hub));
     memset(hub, 0, sizeof(*hub));
+    getsock(hub->sk = sk);
     if((hub->ich = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1) {
        flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
        killfnetnode(fn);
        return;
     }
     fn->data = hub;
-    sendadc(fn->sk, 0, L"HSUP", L"ADBASE", eoc, NULL);
+    sendadc(sk, 0, L"HSUP", L"ADBASE", L"ADTIGR", eoc, NULL);
 }
 
 static void hubdestroy(struct fnetnode *fn)
 {
     struct adchub *hub;
     
-    if((hub = fn->data) == NULL)
-       return;
+    hub = fn->data;
     iconv_close(hub->ich);
     if(hub->inbuf != NULL)
        free(hub->inbuf);
@@ -378,6 +396,14 @@ static void hubdestroy(struct fnetnode *fn)
     free(hub);
 }
 
+static void hubkill(struct fnetnode *fn)
+{
+    struct adchub *hub;
+    
+    hub = fn->data;
+    hub->sk->close = 1;
+}
+
 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
 {
     return(0);
@@ -391,6 +417,7 @@ static int hubreqconn(struct fnetpeer *peer)
 static struct fnet adcnet_store = {
     .connect = hubconnect,
     .destroy = hubdestroy,
+    .kill = hubkill,
     .setnick = hubsetnick,
     .reqconn = hubreqconn,
     .name = L"adc"
@@ -413,7 +440,7 @@ static int run(void)
        if((hub = fn->data) == NULL)
            continue;
        if((qcmd = ulqcmd(&hub->queue)) != NULL) {
-           if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
+           if((hub->sk != NULL) && (hub->sk->state == SOCK_EST))
                dispatch(qcmd, fn);
            freeqcmd(qcmd);
            ret = 1;
@@ -430,9 +457,31 @@ static void preinit(int hup)
     regfnet(adcnet);
 }
 
-static int init(int hup)
+static void makepid(char *idbuf)
 {
     int i;
+    int fd, ret;
+    
+    i = 0;
+    if((fd = open("/dev/urandom", O_RDONLY)) >= 0) {
+       for(i = 0; i < 24; i += ret) {
+           if((ret = read(fd, idbuf + i, 24 - i)) <= 0) {
+               flog(LOG_WARNING, "could not read random data from /dev/urandom for ADC PID: %s", (errno == 0)?"EOF":strerror(errno));
+               break;
+           }
+       }
+       close(fd);
+    } else {
+       flog(LOG_WARNING, "could not open /dev/urandom: %s", strerror(errno));
+    }
+    if(i != 24) {
+       for(i = 0; i < sizeof(idbuf); i++)
+           idbuf[i] = rand() % 256;
+    }
+}
+
+static int init(int hup)
+{
     char idbuf[24], *id32;
     struct tigerhash th;
     
@@ -440,8 +489,7 @@ static int init(int hup)
        eoc = swcsdup(L"");
        
        if((privid = fetchvar("adc.pid", NULL)) == NULL) {
-           for(i = 0; i < sizeof(idbuf); i++)
-               idbuf[i] = rand() % 256;
+           makepid(idbuf);
            id32 = base32encode(idbuf, sizeof(idbuf));
            id32[39] = 0;
            privid = icmbstowcs(id32, "us-ascii");
@@ -469,7 +517,15 @@ static void terminate(void)
 }
 
 static struct configvar myvars[] = {
+    /** Specifies a specific UDP port to use for ADC search
+     * results. If left unspecified, a port is allocated
+     * dynamically. Useful for NAT routers (see also the
+     * net.visibleipv4 address for those cases). */
     {CONF_VAR_INT, "udpport", {.num = 0}},
+    /** Specifies a specific TCP port to use for ADC peer
+     * connections. If left unspecified, a port is allocated
+     * dynamically. Useful for NAT routers (see also the
+     * net.visibleipv4 address for those cases). */
     {CONF_VAR_INT, "tcpport", {.num = 0}},
     {CONF_VAR_END}
 };