Add anti-segv check.
[doldaconnect.git] / daemon / fnet-dc.c
index ed01d06..f7455ae 100644 (file)
@@ -106,7 +106,7 @@ struct dchub
     char *inbuf;
     size_t inbufdata, inbufsize;
     struct qcommand *queue;
-    int extended;
+    int extended, dcppemu;
     char *nativename;
     char *nativenick;
 };
@@ -128,15 +128,16 @@ struct dcpeer
     size_t inbufdata, inbufsize;
     size_t curread, totalsize;
     int freeing;
+    struct timer *timeout;
     struct qcommand *queue;
     struct transfer *transfer;
     int state;
     int ptclose;      /* Close after transfer is complete */
     int accepted;     /* If false, we connected, otherwise, we accepted */
-    int extended;
+    int extended, dcppemu;
     int direction;    /* Using the constants from transfer.h */
     int compress;
-    int hascurpos, notthl;
+    int hascurpos, fetchingtthl, notthl;
     struct tigertreehash tth;
     void *cprsdata;
     char *key;
@@ -444,6 +445,14 @@ static void hubrecvchat(struct socket *sk, struct fnetnode *fn, char *from, char
     free(chat);
 }
 
+static void peertimeout(int cancelled, struct dcpeer *peer)
+{
+    peer->timeout = NULL;
+    if(cancelled)
+       return;
+    freedcpeer(peer);
+}
+
 static void sendadc(struct socket *sk, char *arg)
 {
     char *buf;
@@ -625,20 +634,18 @@ static void sendmynick(struct dcpeer *peer)
 
 static void sendpeerlock(struct dcpeer *peer)
 {
-#ifdef DCPP_MASQUERADE
-    qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DCPLUSPLUS0.674ABCABC|");
-#else
-    qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DOLDA%sABCABCABC|", VERSION);
-#endif
+    if(peer->dcppemu)
+       qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DCPLUSPLUS0.674ABCABC|");
+    else
+       qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DOLDA%sABCABCABC|", VERSION);
 }
 
 static void sendsupports(struct dcpeer *peer)
 {
-#ifdef DCPP_MASQUERADE
-    qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG |");
-#else
-    qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG|");
-#endif
+    if(peer->dcppemu)
+       qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG |");
+    else
+       qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG|");
 }
 
 static void requestfile(struct dcpeer *peer)
@@ -676,6 +683,7 @@ static void requestfile(struct dcpeer *peer)
            sendadc(peer->sk, "0");
            sendadc(peer->sk, "-1");
            qstr(peer->sk, "|");
+           peer->fetchingtthl = 1;
            return;
        }
     }
@@ -729,23 +737,30 @@ static void sendmyinfo(struct socket *sk, struct fnetnode *fn)
     struct dchub *hub;
     char *buf;
     struct fnetnode *cfn;
-    int numhubs;
+    int hn1, hn2, hn3;
     
     hub = fn->data;
     qstrf(sk, "$MyINFO $ALL %s ", hub->nativenick);
     buf = tr(icswcstombs(confgetstr("dc", "desc"), DCCHARSET, "Charset_conv_failure"), "$_|_");
     qstrf(sk, "%s", buf);
-    numhubs = 0;
+    hn1 = hn2 = hn3 = 0;
     for(cfn = fnetnodes; cfn != NULL; cfn = cfn->next)
     {
        if((cfn->state == FNN_EST) || (cfn->state == FNN_HS))
-           numhubs++;
+       {
+           if(cfn->regstatus == FNNS_OP)
+               hn3++;
+           else if(cfn->regstatus == FNNS_REG)
+               hn2++;
+           else
+               hn1++;
+       }
     }
-    qstrf(sk, "<%s V:%s,M:%c,H:%i/0/0,S:%i>",
-         DCIDTAG,
-         DCIDTAGV,
+    qstrf(sk, "<%s V:%s,M:%c,H:%i/%i/%i,S:%i>",
+         (hub->dcppemu)?"++":"Dolda",
+         (hub->dcppemu)?"0.674":VERSION,
          (tcpsock == NULL)?'P':'A',
-         numhubs,
+         hn1, hn2, hn3,
          confgetint("transfer", "slots")
          );
     qstrf(sk, "$ $");
@@ -790,11 +805,10 @@ static void cmd_lock(struct socket *sk, struct fnetnode *fn, char *cmd, char *ar
        *(p++) = 0;
     if(hub->extended)
     {
-#ifdef DCPP_MASQUERADE
-       qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock |");
-#else
-       qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock|");
-#endif
+       if(hub->dcppemu)
+           qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock |");
+       else
+           qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock|");
     }
     key = dcmakekey(args);
     qstrf(sk, "$Key %s|", key);
@@ -931,7 +945,7 @@ static void cmd_myinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     *p2 = 0;
     if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
        return;
-    if((wcslen(buf) > 0) && (buf[wcslen(buf) - 1] == L'>') && ((wp = wcschr(buf, L'<')) != NULL))
+    if((wcslen(buf) > 0) && (buf[wcslen(buf) - 1] == L'>') && ((wp = wcsrchr(buf, L'<')) != NULL))
     {
        buf[wcslen(buf) - 1] = L'\0';
        *(wp++) = L'\0';
@@ -1429,28 +1443,35 @@ static void cmd_usercommand(struct socket *sk, struct fnetnode *fn, char *cmd, c
 static void cmd_getpass(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
 {
     struct dchub *hub;
-    struct wcspair *arg;
+    wchar_t *pw;
     char *mbspw;
     
     hub = fn->data;
-    for(arg = fn->args; arg != NULL; arg = arg->next)
-    {
-       if(!wcscmp(arg->key, L"password"))
-           break;
-    }
-    if((arg == NULL) || ((mbspw = icwcstombs(arg->val, DCCHARSET)) == NULL))
+    pw = wpfind(fn->args, L"password");
+    if((pw == NULL) || ((mbspw = icwcstombs(pw, DCCHARSET)) == NULL))
     {
        killfnetnode(fn);
        return;
     }
     qstrf(sk, "$MyPass %s|", mbspw);
     free(mbspw);
+    fn->regstatus = FNNS_REG;
+    hubhandleaction(sk, fn, cmd, args);
+}
+
+static void cmd_logedin(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
+{
+    struct dchub *hub;
+    
+    hub = fn->data;
+    fn->regstatus = FNNS_OP;
     hubhandleaction(sk, fn, cmd, args);
 }
 
 static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
     struct dcexppeer *expect;
+    struct dchub *hub;
 
     if(peer->nativename != NULL)
        free(peer->nativename);
@@ -1473,8 +1494,10 @@ static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        {
            peer->fn = NULL;
        } else {
+           hub = expect->fn->data;
            peer->fn = expect->fn;
            getfnetnode(peer->fn);
+           peer->dcppemu = hub->dcppemu;
            freeexppeer(expect);
        }
     }
@@ -1582,6 +1605,8 @@ static void cmd_key(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
 
 static void startdl(struct dcpeer *peer)
 {
+    if(peer->timeout != NULL)
+       canceltimer(peer->timeout);
     peer->state = PEER_TRNS;
     transferstartdl(peer->transfer, peer->sk);
     peer->sk->readcb = (void (*)(struct socket *, void *))transread;
@@ -1590,6 +1615,8 @@ static void startdl(struct dcpeer *peer)
 
 static void startul(struct dcpeer *peer)
 {
+    if(peer->timeout != NULL)
+       canceltimer(peer->timeout);
     peer->state = PEER_TRNS;
     transferstartul(peer->transfer, peer->sk);
     peer->sk->writecb = (void (*)(struct socket *, void *))transwrite;
@@ -1598,6 +1625,7 @@ static void startul(struct dcpeer *peer)
 static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
     int size;
+    struct transfer *transfer;
     
     if(peer->transfer == NULL)
     {
@@ -1608,7 +1636,9 @@ static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, ch
     if(peer->transfer->size != size)
     {
        transfersetsize(peer->transfer, size);
+       transfer = peer->transfer;
        freedcpeer(peer);
+       trytransferbypeer(transfer->fnet, transfer->peerid);
        return;
     }
     startdl(peer);
@@ -1617,6 +1647,13 @@ static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, ch
 
 static void cmd_error(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
+    if(peer->fetchingtthl)
+    {
+       peer->fetchingtthl = 0;
+       peer->notthl = 1;
+       requestfile(peer);
+       return;
+    }
     if((peer->transfer != NULL) && (peer->transfer->dir == TRNSD_DOWN))
     {
        transferseterror(peer->transfer, TRNSE_NOTFOUND);
@@ -1750,15 +1787,10 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
     } else if(fd >= 0) {
        if((buf2 = icsmbstowcs(args, DCCHARSET, NULL)) != NULL)
            transfersetpath(peer->transfer, buf2);
+       peer->transfer->flags.b.minislot = 1;
     }
     if(fd < 0)
     {
-       if(slotsleft() < 1)
-       {
-           qstr(sk, "$MaxedOut|");
-           freedcpeer(peer);
-           return;
-       }
        if((node = resdcpath(args, DCCHARSET, '\\')) == NULL)
        {
            qstrf(sk, "$Error File not in share|");
@@ -1786,6 +1818,14 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
        freedcpeer(peer);
        return;
     }
+    if(sb.st_size < 65536)
+       peer->transfer->flags.b.minislot = 1;
+    if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
+       close(fd);
+       qstr(sk, "$MaxedOut|");
+       freedcpeer(peer);
+       return;
+    }
     if((offset != 0) && (lseek(fd, offset, SEEK_SET) < 0))
     {
        close(fd);
@@ -1889,14 +1929,10 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
     } else if(fd >= 0) {
        if((buf2 = icsmbstowcs(args, charset, NULL)) != NULL)
            transfersetpath(peer->transfer, buf2);
+       peer->transfer->flags.b.minislot = 1;
     }
     if(fd < 0)
     {
-       if(slotsleft() < 1)
-       {
-           qstr(sk, "$MaxedOut|");
-           return;
-       }
        if((node = resdcpath(p, charset, '\\')) == NULL)
        {
            qstr(sk, "$Error File not in cache|");
@@ -1921,6 +1957,13 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
        qstr(sk, "$Error|");
        return;
     }
+    if(sb.st_size < 65536)
+       peer->transfer->flags.b.minislot = 1;
+    if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
+       close(fd);
+       qstr(sk, "$MaxedOut|");
+       return;
+    }
     if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
     {
        close(fd);
@@ -1973,14 +2016,10 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
     } else if(fd >= 0) {
        if((wbuf = icsmbstowcs(argv[1], "UTF-8", NULL)) != NULL)
            transfersetpath(peer->transfer, wbuf);
+       peer->transfer->flags.b.minislot = 1;
     }
     if(fd < 0)
     {
-       if(slotsleft() < 1)
-       {
-           qstr(sk, "$MaxedOut|");
-           goto out;
-       }
        if(!strncmp(argv[1], "TTH/", 4))
        {
            if((node = findbytth(argv[1] + 4)) == NULL)
@@ -2020,6 +2059,12 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
            qstr(sk, "$Error|");
            goto out;
        }
+       if(sb.st_size < 65536)
+           peer->transfer->flags.b.minislot = 1;
+       if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
+           qstr(sk, "$MaxedOut|");
+           goto out;
+       }
        if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
        {
            qstr(sk, "$Error Offset out of range|");
@@ -2083,6 +2128,8 @@ static void handletthl(struct dcpeer *peer)
     }
     if(peer->curread >= peer->totalsize)
     {
+       if(peer->timeout == NULL)
+           peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
        peer->state = PEER_CMD;
        synctigertree(&peer->tth);
        restigertree(&peer->tth, buf);
@@ -2121,9 +2168,12 @@ static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *
            freedcpeer(peer);
            goto out;
        }
+       if(peer->timeout != NULL)
+           canceltimer(peer->timeout);
        peer->state = PEER_TTHL;
        peer->totalsize = numbytes;
        peer->curread = 0;
+       peer->fetchingtthl = 0;
        inittigertree(&peer->tth);
        handletthl(peer);
     } else if(!strcmp(argv[0], "file")) {
@@ -2406,10 +2456,10 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
        if(minsize != 0)
        {
            sizebuf2(sstr, sstrdata + 32, 1);
-           snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%i?1?", minsize);
+           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%i?1?", minsize);
        } else if(maxsize != -1) {
            sizebuf2(sstr, sstrdata + 32, 1);
-           snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%i?1?", maxsize);
+           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%i?1?", maxsize);
        } else {
            bufcat(sstr, "F?F?0?1?", 8);
        }
@@ -2494,6 +2544,7 @@ struct command hubcmds[] =
     {"$SR", cc(cmd_sr)},
     {"$UserCommand", cc(cmd_usercommand)},
     {"$GetPass", cc(cmd_getpass)},
+    {"$LogedIn", cc(cmd_logedin)}, /* sic */
     {NULL, NULL}
 };
 
@@ -2590,10 +2641,13 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
                {
                    freedcpeer(peer);
                } else {
+                   if(peer->timeout == NULL)
+                       peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
                    peer->state = PEER_CMD;
                    endcompress(peer);
                    transfersetstate(transfer, TRNS_HS);
                    socksettos(peer->sk, confgetint("fnet", "fnptos"));
+                   transfer->flags.b.minislot = 0;
                    peer->sk->writecb = NULL;
                }
            }
@@ -2670,6 +2724,7 @@ static void udpread(struct socket *sk, void *data)
     char *buf, *p, *p2, *hashbuf;
     size_t buflen, hashlen;
     char *nick, *filename, *hubname;
+    struct sockaddr_in hubaddr;
     int size, slots;
     struct fnetnode *fn, *myfn;
     struct dchub *hub;
@@ -2729,6 +2784,27 @@ static void udpread(struct socket *sk, void *data)
            return;
        }
        *p2 = 0;
+       p = p2 + 2;
+       if((p2 = strchr(p, ':')) == NULL)
+       {
+           free(buf);
+           return;
+       }
+       *(p2++) = 0;
+       hubaddr.sin_family = AF_INET;
+       if(!inet_aton(p, &hubaddr.sin_addr))
+       {
+           free(buf);
+           return;
+       }
+       p = p2;
+       if((p2 = strchr(p, ')')) == NULL)
+       {
+           free(buf);
+           return;
+       }
+       *p2 = 0;
+       hubaddr.sin_port = htons(atoi(p));
        if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
        {
            free(buf);
@@ -2768,6 +2844,17 @@ static void udpread(struct socket *sk, void *data)
                }
            }
        }
+       if(myfn == NULL)
+       {
+           for(fn = fnetnodes; fn != NULL; fn = fn->next)
+           {
+               if((fn->fnet == &dcnet) && (fn->sk != NULL) && addreq(fn->sk->remote, (struct sockaddr *)&hubaddr))
+               {
+                   myfn = fn;
+                   break;
+               }
+           }
+       }
        sr = newsrchres(&dcnet, wfile, wnick);
        if(sr->peernick != NULL)
            free(sr->peernick);
@@ -2845,10 +2932,20 @@ static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
 static struct dchub *newdchub(struct fnetnode *fn)
 {
     struct dchub *new;
+    wchar_t *emu;
     
     new = smalloc(sizeof(*new));
     memset(new, 0, sizeof(*new));
     fn->data = new;
+    if(confgetint("dc", "dcppemu"))
+       new->dcppemu = 1;
+    if((emu = wpfind(fn->args, L"dcppemu")) != NULL)
+    {
+       if(*emu == L'y')
+           new->dcppemu = 1;
+       if(*emu == L'n')
+           new->dcppemu = 0;
+    }
     if(hubsetnick(fn, fn->mynick))
        fnetsetnick(fn, L"DoldaConnectUser-IN");
     /* IN as in Invalid Nick */
@@ -2864,6 +2961,8 @@ static struct dcpeer *newdcpeer(struct socket *sk)
     new->transfer = NULL;
     getsock(sk);
     new->sk = sk;
+    if(confgetint("dc", "dcppemu"))
+       new->dcppemu = 1;
     new->next = peers;
     new->prev = NULL;
     if(peers != NULL)
@@ -2893,6 +2992,8 @@ static void freedcpeer(struct dcpeer *peer)
            resettransfer(peer->transfer);
        transferdetach(peer->transfer);
     }
+    if(peer->timeout != NULL)
+       canceltimer(peer->timeout);
     if(peer->sk->data == peer)
        peer->sk->data = NULL;
     peer->sk->readcb = NULL;
@@ -3032,20 +3133,24 @@ static void peererror(struct socket *sk, int err, struct dcpeer *peer)
 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
 {
     struct dcpeer *peer;
+    struct dchub *hub;
     
     if(err != 0)
     {
        putfnetnode(fn);
        return;
     }
+    hub = fn->data;
     peer = newdcpeer(sk);
     peer->fn = fn;
     peer->accepted = 0;
+    peer->dcppemu = hub->dcppemu;
     sk->readcb = (void (*)(struct socket *, void *))peerread;
     sk->errcb = (void (*)(struct socket *, int, void *))peererror;
     sk->data = peer;
     socksettos(sk, confgetint("fnet", "fnptos"));
     putsock(sk);
+    peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
     sendmynick(peer);
     sendpeerlock(peer);
 }
@@ -3060,6 +3165,7 @@ static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
     newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
     newsk->data = peer;
     socksettos(newsk, confgetint("fnet", "fnptos"));
+    peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
 }
 
 static void updatehmlist(void)
@@ -3241,7 +3347,10 @@ static void updatexmllist(void)
     for(i = 0; i < sizeof(cidbuf) - 1; i++)
        cidbuf[i] = (rand() % ('Z' - 'A' + 1)) + 'A';
     cidbuf[i] = 0;
-    fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, DCIDFULL);
+    if(confgetint("dc", "dcppemu"))
+       fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"DC++ 0.674\">\r\n", cidbuf);
+    else
+       fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, "DoldaConnect" VERSION);
     
     node = shareroot->child;
     lev = 0;
@@ -3286,11 +3395,10 @@ static void updatexmllist(void)
        }
     }
     
-#ifdef DCPP_MASQUERADE
-    fprintf(fs, "</FileListing>");
-#else
-    fprintf(fs, "</FileListing>\r\n");
-#endif
+    if(confgetint("dc", "dcppemu"))
+       fprintf(fs, "</FileListing>");
+    else
+       fprintf(fs, "</FileListing>\r\n");
     fclose(fs);
 }
 
@@ -3419,6 +3527,9 @@ static int run(void)
        nextpeer = peer->next;
        if((qcmd = ulqcmd(&peer->queue)) != NULL)
        {
+           if(peer->timeout != NULL)
+               canceltimer(peer->timeout);
+           peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
            if(*qcmd->string == '$')
                dispatchcommand(qcmd, peercmds, peer->sk, peer);
            freeqcmd(qcmd);
@@ -3528,6 +3639,7 @@ static struct configvar myvars[] =
     {CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
     {CONF_VAR_INT, "udpport", {.num = 0}},
     {CONF_VAR_INT, "tcpport", {.num = 0}},
+    {CONF_VAR_BOOL, "dcppemu", {.num = 0}},
     {CONF_VAR_END}
 };