/*
* 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
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
-#include <malloc.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <alloca.h>
#include <wctype.h>
#include <time.h>
#include <errno.h>
char *charset;
char *nativename;
char *nativenick;
+ char **supports;
};
struct dcexppeer
static char *xmlbz2listname = NULL;
static struct timer *listwritetimer = NULL;
-static int peerconnect(struct socket *sk, int err, struct fnetnode *fn);
-static int peerread(struct socket *sk, struct dcpeer *peer);
-static int peererror(struct socket *sk, int err, struct dcpeer *peer);
+static void peerconnect(struct socket *sk, int err, struct fnetnode *fn);
static void freedcpeer(struct dcpeer *peer);
-static void transread(struct dcpeer *peer);
-static void transerr(struct dcpeer *peer);
-static int transwrite(struct socket *sk, struct dcpeer *peer);
+static void transread(struct socket *sk, struct dcpeer *peer);
+static void transerr(struct socket *sk, int err, struct dcpeer *peer);
+static void transwrite(struct socket *sk, struct dcpeer *peer);
static void updatehmlist(void);
static void updatexmllist(void);
static void updatexmlbz2list(void);
return(1);
}
+/*
+ * Uncomment when used!
+
+static int hubsupports(struct dchub *hub, char *cap)
+{
+ char **p;
+
+ if(hub->supports == NULL)
+ return(0);
+ for(p = hub->supports; *p != NULL; p++)
+ {
+ if(!strcasecmp(*p, cap))
+ return(1);
+ }
+ return(0);
+}
+*/
+
static int supports(struct dcpeer *peer, char *cap)
{
char **p;
freedcpeer(peer);
return;
}
- qstrf(peer->sk, "$UGetBlock %i %i %s|", peer->transfer->curpos, peer->transfer->size - peer->transfer->curpos, buf);
+ qstrf(peer->sk, "$UGetBlock %zi %zi %s|", peer->transfer->curpos, peer->transfer->size - peer->transfer->curpos, buf);
} else {
/* Use DCCHARSET for $Get paths until further researched... */
if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
freedcpeer(peer);
return;
}
- qstrf(peer->sk, "$Get %s$%i|", buf, peer->transfer->curpos + 1);
+ qstrf(peer->sk, "$Get %s$%zi|", buf, peer->transfer->curpos + 1);
}
}
if(node->f.b.hastth)
{
buf2 = base32encode(node->hashtth, 24);
- qstrf(dsk, "%s%s\005%i%sTTH:%.39s%s", prefix, buf, node->size, infix, buf2, postfix);
+ qstrf(dsk, "%s%s\005%zi%sTTH:%.39s%s", prefix, buf, node->size, infix, buf2, postfix);
free(buf2);
} else {
- qstrf(dsk, "%s%s\005%i%s%s%s", prefix, buf, node->size, infix, hub->nativename, postfix);
+ qstrf(dsk, "%s%s\005%zi%s%s%s", prefix, buf, node->size, infix, hub->nativename, postfix);
}
free(buf);
}
addr.sin_port = htons(atoi(p));
if(!inet_aton(args, &addr.sin_addr))
return;
- newsk = netcsconn((struct sockaddr *)&addr, sizeof(addr), (int (*)(struct socket *, int, void *))peerconnect, fn);
+ newsk = netcsconn((struct sockaddr *)&addr, sizeof(addr), (void (*)(struct socket *, int, void *))peerconnect, fn);
getfnetnode(fn);
hubhandleaction(sk, fn, cmd, args);
}
hubhandleaction(sk, fn, cmd, args);
}
+static void cmd_hubsupports(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
+{
+ struct dchub *hub;
+ int i;
+ char *p, *p2;
+ char **arr;
+ size_t arrsize, arrdata;
+
+ hub = fn->data;
+ if(hub->supports != NULL)
+ {
+ for(i = 0; hub->supports[i] != NULL; i++)
+ free(hub->supports[i]);
+ free(hub->supports);
+ }
+ arr = NULL;
+ arrsize = arrdata = 0;
+ p = args;
+ do
+ {
+ if((p2 = strchr(p, ' ')) != NULL)
+ *(p2++) = 0;
+ if(*p == 0)
+ continue;
+ addtobuf(arr, sstrdup(p));
+ } while((p = p2) != NULL);
+ addtobuf(arr, NULL);
+ hub->supports = arr;
+}
+
static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
{
struct dcexppeer *expect;
canceltimer(peer->timeout);
peer->state = PEER_TRNS;
transferstartdl(peer->transfer, peer->sk);
+ peer->sk->readcb = (void (*)(struct socket *, void *))transread;
+ peer->sk->errcb = (void (*)(struct socket *, int, void *))transerr;
}
static void startul(struct dcpeer *peer)
canceltimer(peer->timeout);
peer->state = PEER_TRNS;
transferstartul(peer->transfer, peer->sk);
- CBREG(peer->sk, socket_write, (int (*)(struct socket *, void *))transwrite, NULL, peer);
+ peer->sk->writecb = (void (*)(struct socket *, void *))transwrite;
}
static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
lesk = wrapsock(fd);
transferprepul(peer->transfer, sb.st_size, offset, -1, lesk);
putsock(lesk);
- qstrf(sk, "$FileLength %i|", peer->transfer->size);
+ qstrf(sk, "$FileLength %zi|", peer->transfer->size);
}
static void cmd_send(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
goto out;
}
startdl(peer);
- transread(peer);
+ if(peer->inbufdata > 0)
+ {
+ sockpushdata(sk, peer->inbuf, peer->inbufdata);
+ peer->inbufdata = 0;
+ transread(sk, peer);
+ }
} else {
/* We certainly didn't request this...*/
freedcpeer(peer);
return;
}
startdl(peer);
- transread(peer);
+ if(peer->inbufdata > 0)
+ {
+ sockpushdata(sk, peer->inbuf, peer->inbufdata);
+ peer->inbufdata = 0;
+ transread(sk, peer);
+ }
}
/*
{"$UserCommand", cc(cmd_usercommand)},
{"$GetPass", cc(cmd_getpass)},
{"$LogedIn", cc(cmd_logedin)}, /* sic */
+ {"$Supports", cc(cmd_hubsupports)},
{NULL, NULL}
};
static void dctransdetach(struct transfer *transfer, struct dcpeer *peer)
{
- CBUNREG(transfer, trans_filterout, trresumecb, peer);
+ CBUNREG(transfer, trans_filterout, peer);
if(peer->freeing)
return;
peer->transfer = NULL;
transfersetstate(transfer, TRNS_HS);
socksettos(peer->sk, confgetint("fnet", "fnptos"));
transfer->flags.b.minislot = 0;
- CBUNREG(peer->sk, socket_write, transwrite, peer);
+ peer->sk->writecb = NULL;
}
}
}
peer->sk->ignread = 0;
}
-static void transread(struct dcpeer *peer)
+static void transread(struct socket *sk, struct dcpeer *peer)
{
- size_t num;
+ void *buf;
+ size_t bufsize;
struct transfer *transfer;
+ if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
+ return;
if(peer->transfer == NULL)
{
+ free(buf);
freedcpeer(peer);
return;
}
- if(peer->inbufsize > peer->transfer->size - peer->transfer->curpos)
- num = peer->transfer->size - peer->transfer->curpos;
- else
- num = peer->inbufsize;
- transferputdata(peer->transfer, peer->inbuf, num);
- memmove(peer->inbuf, peer->inbuf + num, peer->inbufsize -= num);
+ transferputdata(peer->transfer, buf, bufsize);
+ free(buf);
if(peer->transfer->curpos >= peer->transfer->size)
{
transfer = peer->transfer;
return;
}
if(transferdatasize(peer->transfer) > 65535)
- peer->sk->ignread = 1;
+ sk->ignread = 1;
}
-static void transerr(struct dcpeer *peer)
+static void transerr(struct socket *sk, int err, struct dcpeer *peer)
{
struct transfer *transfer;
if((transfer = peer->transfer) == NULL)
+ {
+ freedcpeer(peer);
return;
+ }
transferdetach(transfer);
transferendofdata(transfer);
}
-static int transwrite(struct socket *sk, struct dcpeer *peer)
+static void transwrite(struct socket *sk, struct dcpeer *peer)
{
if((peer->state != PEER_TRNS) && (peer->state != PEER_SYNC))
- return(1);
+ return;
if(peer->transfer == NULL)
{
freedcpeer(peer);
- return(1);
+ return;
}
dctransgotdata(peer->transfer, peer);
- return(0);
}
-static int udpread(struct socket *sk, void *data)
+static void udpread(struct socket *sk, void *data)
{
char *buf, *p, *p2, *hashbuf;
size_t buflen, hashlen;
struct hash *hash;
if((buf = sockgetinbuf(sk, &buflen)) == NULL)
- return(0);
+ return;
buf = srealloc(buf, buflen + 1);
buf[buflen] = 0;
if(!strncmp(buf, "$SR ", 4))
if((p2 = strchr(p, ' ')) == NULL)
{
free(buf);
- return(0);
+ return;
}
*p2 = 0;
p = p2 + 1;
if((p2 = strchr(p, 5)) == NULL)
{
free(buf);
- return(0);
+ return;
}
*p2 = 0;
p = p2 + 1;
if((p2 = strchr(p, ' ')) == NULL)
{
free(buf);
- return(0);
+ return;
}
*p2 = 0;
size = atoi(p);
if((p2 = strchr(p, '/')) == NULL)
{
free(buf);
- return(0);
+ return;
}
*p2 = 0;
slots = atoi(p);
if((p2 = strchr(p, 5)) == NULL)
{
free(buf);
- return(0);
+ return;
}
p = p2 + 1;
hubname = p;
if((p2 = strstr(p, " (")) == NULL)
{
free(buf);
- return(0);
+ return;
}
*p2 = 0;
p = p2 + 2;
if((p2 = strchr(p, ':')) == NULL)
{
free(buf);
- return(0);
+ return;
}
*(p2++) = 0;
hubaddr.sin_family = AF_INET;
if(!inet_aton(p, &hubaddr.sin_addr))
{
free(buf);
- return(0);
+ return;
}
p = p2;
if((p2 = strchr(p, ')')) == NULL)
{
free(buf);
- return(0);
+ return;
}
*p2 = 0;
hubaddr.sin_port = htons(atoi(p));
if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
{
free(buf);
- return(0);
+ return;
}
myfn = NULL;
hash = NULL;
if((wnick = icmbstowcs(nick, (hub == NULL)?DCCHARSET:(hub->charset))) == NULL)
{
free(buf);
- return(0);
+ return;
}
sr = newsrchres(&dcnet, wfile, wnick);
if(sr->peernick != NULL)
freesrchres(sr);
}
free(buf);
- return(0);
}
static void hubread(struct socket *sk, struct fnetnode *fn)
}
if(peer->timeout != NULL)
canceltimer(peer->timeout);
- /* XXX: Unregister transwrite from peer->sk->socket_write? */
- CBUNREG(peer->sk, socket_read, peerread, peer);
- CBUNREG(peer->sk, socket_err, peererror, peer);
+ if(peer->sk->data == peer)
+ peer->sk->data = NULL;
+ peer->sk->readcb = NULL;
+ peer->sk->writecb = NULL;
+ peer->sk->errcb = NULL;
putsock(peer->sk);
endcompress(peer);
if(peer->supports != NULL)
static void hubconnect(struct fnetnode *fn)
{
- CBREG(fn->sk, socket_read, (int (*)(struct socket *, void *))hubread, (void (*)(void *))putfnetnode, getfnetnode(fn));
- CBREG(fn->sk, socket_err, (int (*)(struct socket *, int, void *))huberr, (void (*)(void *))putfnetnode, getfnetnode(fn));
+ fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
+ fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
+ getfnetnode(fn);
fn->data = newdchub(fn);
+ fn->sk->data = fn;
return;
}
static void hubdestroy(struct fnetnode *fn)
{
+ int i;
struct dchub *hub;
struct qcommand *qcmd;
hub = (struct dchub *)fn->data;
- if(fn->sk != NULL)
+ if((fn->sk != NULL) && (fn->sk->data == fn))
{
- CBUNREG(fn->sk, socket_read, hubread, fn);
- CBUNREG(fn->sk, socket_err, huberr, fn);
+ fn->sk->data = NULL;
+ fn->sk->readcb = NULL;
+ fn->sk->errcb = NULL;
+ putfnetnode(fn);
}
if(hub == NULL)
return;
while((qcmd = ulqcmd(&hub->queue)) != NULL)
freeqcmd(qcmd);
+ if(hub->supports != NULL)
+ {
+ for(i = 0; hub->supports[i] != NULL; i++)
+ free(hub->supports[i]);
+ free(hub->supports);
+ }
if(hub->nativename != NULL)
free(hub->nativename);
if(hub->nativenick != NULL)
.filebasename = dcbasename
};
-static void cmdread(struct dcpeer *peer)
-{
- char *p;
- struct command *cmd;
-
- p = peer->inbuf;
- while((peer->inbufdata > 0) && (p = memchr(peer->inbuf, '|', peer->inbufdata)) != NULL)
- {
- *(p++) = 0;
- newqcmd(&peer->queue, peer->inbuf);
- for(cmd = peercmds; cmd->handler != NULL; cmd++)
- {
- if(!memcmp(peer->inbuf, cmd->name, strlen(cmd->name)) && ((peer->inbuf[strlen(cmd->name)] == ' ') || (peer->inbuf[strlen(cmd->name)] == '|')))
- break;
- }
- memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
- if(cmd->stop)
- {
- peer->state = PEER_STOP;
- break;
- }
- }
-}
-
-static int peerread(struct socket *sk, struct dcpeer *peer)
+static void peerread(struct socket *sk, struct dcpeer *peer)
{
- char *newbuf;
+ char *newbuf, *p;
size_t datalen;
+ struct command *cmd;
if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
- return(0);
+ return;
sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
memcpy(peer->inbuf + peer->inbufdata, newbuf, datalen);
free(newbuf);
peer->inbufdata += datalen;
if(peer->state == PEER_CMD)
{
- cmdread(peer);
- } else if(peer->state == PEER_TRNS) {
- transread(peer);
+ p = peer->inbuf;
+ while((peer->inbufdata > 0) && (p = memchr(peer->inbuf, '|', peer->inbufdata)) != NULL)
+ {
+ *(p++) = 0;
+ newqcmd(&peer->queue, peer->inbuf);
+ for(cmd = peercmds; cmd->handler != NULL; cmd++)
+ {
+ if(!memcmp(peer->inbuf, cmd->name, strlen(cmd->name)) && ((peer->inbuf[strlen(cmd->name)] == ' ') || (peer->inbuf[strlen(cmd->name)] == '|')))
+ break;
+ }
+ memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
+ if(cmd->stop)
+ {
+ peer->state = PEER_STOP;
+ break;
+ }
+ }
} else if(peer->state == PEER_TTHL) {
handletthl(peer);
}
- return(0);
}
-static int peererror(struct socket *sk, int err, struct dcpeer *peer)
+static void peererror(struct socket *sk, int err, struct dcpeer *peer)
{
- if(peer->state == PEER_TRNS)
- transerr(peer);
freedcpeer(peer);
- return(0);
}
-static int peerconnect(struct socket *sk, int err, struct fnetnode *fn)
+static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
{
struct dcpeer *peer;
struct dchub *hub;
{
putfnetnode(fn);
putsock(sk);
- return(1);
+ return;
}
hub = fn->data;
peer = newdcpeer(sk);
peer->fn = fn;
peer->accepted = 0;
peer->dcppemu = hub->dcppemu;
- CBREG(sk, socket_read, (int (*)(struct socket *, void *))peerread, NULL, peer);
- CBREG(sk, socket_err, (int (*)(struct socket *, int, void *))peererror, NULL, peer);
+ 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);
- return(1);
}
-static int peeraccept(struct socket *sk, struct socket *newsk, void *data)
+static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
{
struct dcpeer *peer;
peer = newdcpeer(newsk);
peer->accepted = 1;
- CBREG(sk, socket_read, (int (*)(struct socket *, void *))peerread, NULL, peer);
- CBREG(sk, socket_err, (int (*)(struct socket *, int, void *))peererror, NULL, peer);
+ newsk->readcb = (void (*)(struct socket *, void *))peerread;
+ 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);
- return(0);
}
static void updatehmlist(void)
if(node->f.b.type == FILE_REG)
{
addtobuf(buf, '|');
- sprintf(numbuf, "%i", node->size);
+ sprintf(numbuf, "%zi", node->size);
bufcat(buf, numbuf, strlen(numbuf));
}
addtobuf(buf, 13);
lev++;
continue;
} else {
- fprintf(fs, "<File Name=\"%s\" Size=\"%i\"", namebuf, node->size);
+ fprintf(fs, "<File Name=\"%s\" Size=\"%zi\"", namebuf, node->size);
if(node->f.b.hastth)
{
hashbuf = base32encode(node->hashtth, 24);
if(!now)
{
if(listwritetimer == NULL)
- listwritetimer = timercallback(ntime() + 300, listtimercb, NULL);
+ listwritetimer = timercallback(ntime() + confgetint("cli", "hashwritedelay"), listtimercb, NULL);
return;
}
if(listwritetimer != NULL)
return(0);
}
+static char *quotestr(char *str)
+{
+ unsigned char *buf;
+ unsigned char *p;
+ size_t bufsize, bufdata;
+ wchar_t *wbuf;
+ static char *enc = NULL;
+ size_t encsize, encdata;
+
+ buf = NULL;
+ bufsize = bufdata = 0;
+ for(p = (unsigned char *)str; *p; p++)
+ {
+ if(*p == '\b')
+ bufcat(buf, "\\b", 2);
+ else if(*p == '\t')
+ bufcat(buf, "\\t", 2);
+ else if(*p == '\n')
+ bufcat(buf, "\\n", 2);
+ else if(*p == '\r')
+ bufcat(buf, "\\r", 2);
+ else if(*p == '\\')
+ bufcat(buf, "\\\\", 2);
+ else if(*p >= 32)
+ addtobuf(buf, *p);
+ else
+ bprintf(buf, "\\x%02x", *p);
+ }
+ addtobuf(buf, 0);
+ if(enc != NULL)
+ free(enc);
+ enc = NULL;
+ if((wbuf = icmbstowcs((char *)buf, DCCHARSET)) != NULL)
+ {
+ enc = icwcstombs(wbuf, NULL);
+ free(wbuf);
+ }
+ if(enc == NULL)
+ {
+ encsize = encdata = 0;
+ for(p = buf; *p; p++) {
+ if(*p < 128)
+ addtobuf(enc, *p);
+ else
+ bprintf(buf, "\\x%x", *p);
+ }
+ }
+ free(buf);
+ return(enc);
+}
+
+static void logunimpl(char *list, char *cmd, char *args)
+{
+ FILE *log;
+
+ if((log = fopen("/tmp/dc-unimpl", "a")) == NULL)
+ {
+ flog(LOG_WARNING, "could not open unimpl log: %s", strerror(errno));
+ return;
+ }
+ fputs(list, log);
+ fputc('\t', log);
+ fputs(quotestr(cmd), log);
+ if(args != NULL)
+ {
+ fputc('\t', log);
+ fputs(quotestr(args), log);
+ }
+ fputc('\n', log);
+ fclose(log);
+}
+
static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, struct socket *sk, void *data)
{
char *p;
break;
}
if(cmd->handler != NULL)
+ {
cmd->handler(sk, data, qcmd->string, p);
-/*
- else
- flog(LOG_DEBUG, "Unimplemented DC command: %s \"%s\"", qcmd->string, p?p:"noargs");
-*/
+ } else if(confgetint("dc", "logunimpl")) {
+ if(cmdlist == hubcmds)
+ logunimpl("hub", qcmd->string, p);
+ else if(cmdlist == peercmds)
+ logunimpl("peer", qcmd->string, p);
+ else
+ logunimpl("other?!", qcmd->string, p);
+ }
}
static int run(void)
flog(LOG_WARNING, "could not create new DC UDP socket, reverting to old: %s", strerror(errno));
return(0);
}
- CBREG(newsock, socket_read, udpread, NULL, NULL);
+ newsock->readcb = udpread;
if(udpsock != NULL)
putsock(udpsock);
udpsock = newsock;
flog(LOG_CRIT, "could not create DC UDP socket: %s", strerror(errno));
return(1);
}
- CBREG(udpsock, socket_read, udpread, NULL, NULL);
+ udpsock->readcb = udpread;
addr.sin_port = htons(confgetint("dc", "tcpport"));
if((tcpsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
static struct configvar myvars[] =
{
+ /** Specifies the share description reported to other DC users. */
{CONF_VAR_STRING, "desc", {.str = L""}},
+ /** Specifies the speed reported to other DC users. Normal values
+ * are 28.8Kbps, 33.6Kbps, 56Kbps, Satellite, ISDN, DSL, Cable,
+ * LAN(T1) or LAN(T3)*/
{CONF_VAR_STRING, "speedstring", {.str = L"LAN(T1)"}},
+ /** The e-mail address to report to other DC users. */
{CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
+ /** Specifies a specific UDP port to use for DC 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 DC 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}},
+ /** If set to true, doldacond will do its best to emulate DC++
+ * (currently v0.674). This should be left off if at all possible,
+ * since turning it on will violate the rules of most hubs and
+ * thus give hub owners an actual reason to kick you if it is
+ * detected. It might be needed for some of the more bone-headed
+ * hub owners, though. Note that DC++ emulation can also be turned
+ * on or off for individual hubs, overriding this setting. */
{CONF_VAR_BOOL, "dcppemu", {.num = 0}},
+ /** Use for debugging. If set to true, doldacond will log all
+ * unknown commands it receives, and their arguments, to
+ * /tmp/dc-unimpl. */
+ {CONF_VAR_BOOL, "logunimpl", {.num = 0}},
{CONF_VAR_END}
};