2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
39 #include "sysevents.h"
44 #define ADC_PROTOCOL 0
45 #define ADC_IDENTIFY 1
52 int minargs, needsender;
53 void (*func)(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv);
69 size_t inbufdata, inbufsize;
72 size_t cbdata, cbsize;
76 struct wcspair *hubinf;
77 struct qcmdqueue queue;
81 /* I've never understood why both of these are necessary, but... */
82 static wchar_t *privid, *cid;
84 static wchar_t **parseadc(wchar_t *cmdline)
87 size_t retsize, retdata;
93 retsize = retdata = 0;
96 if((p2 = wcschr(p, L' ')) != NULL)
99 for(ep = p; len > 0; ep++, len--) {
103 } else if(ep[1] == L'n') {
105 } else if(ep[1] == L'\\') {
108 memmove(ep, ep + 2, (len -= 2) * sizeof(*ep));
112 memmove(ep, ep + 1, --len * sizeof(*ep));
117 addtobuf(ret, swcsdup(p));
124 static void sendeoc(struct socket *sk)
126 sockqueue(sk, "\n", 1);
129 static void sendadc1(struct socket *sk, int sep, wchar_t *arg)
133 size_t bufsize, bufdata;
136 bufsize = bufdata = 0;
139 for(; *arg != L'\0'; arg++) {
141 bufcat(buf, L"\\s", 2);
142 else if(*arg == L'\n')
143 bufcat(buf, L"\\n", 2);
144 else if(*arg == L'\\')
145 bufcat(buf, L"\\\\", 2);
149 addtobuf(buf, L'\0');
150 mbsbuf = icwcstombs(buf, "utf-8");
151 sockqueue(sk, mbsbuf, strlen(mbsbuf));
155 static void sendadc(struct socket *sk, int sep, ...)
162 for(i = 0; (arg = va_arg(args, wchar_t *)) != NULL; i++) {
166 sendadc1(sk, (i == 0)?sep:1, arg);
171 static struct qcmd *newqcmd(struct qcmdqueue *queue, wchar_t **args)
175 new = smalloc(sizeof(*new));
181 queue->l->next = new;
187 static struct qcmd *ulqcmd(struct qcmdqueue *queue)
191 if((ret = queue->f) == NULL)
193 if((queue->f = ret->next) == NULL)
199 static void freeqcmd(struct qcmd *qcmd)
201 freeparr(qcmd->args);
205 #define ADC_CMDFN(name) static void name(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv)
207 #define UNUSED __attribute__ ((unused))
212 struct adchub *hub UNUSED = fn->data; \
213 struct socket *sk UNUSED = hub->sk;
220 for(i = 1; i < argc; i++) {
221 if(wcslen(argv[i]) < 3)
223 for(o = 0, f = 0; hub->sup[o]; o++) {
224 if(!wcscmp(argv[i] + 2, hub->sup[o])) {
229 if(!wcsncmp(argv[i], L"AD", 2)) {
232 hub->sup = srealloc(hub->sup, sizeof(*(hub->sup)) * (o + 1));
233 hub->sup[o] = swcsdup(argv[i] + 2);
234 } else if(!wcsncmp(argv[i], L"RM", 2)) {
238 memmove(hub->sup[o], hub->sup[o + 1], parrlen(hub->sup) - o);
249 hub->sid = swcsdup(argv[1]);
250 if(hub->state == ADC_PROTOCOL) {
251 hub->state = ADC_IDENTIFY;
264 static struct command hubcmds[] = {
265 {L"SUP", 1, 0, cmd_sup},
266 {L"SID", 2, 0, cmd_sid},
267 {L"INF", 0, 0, cmd_inf},
271 static void dispatch(struct qcmd *qcmd, struct fnetnode *fn)
275 wchar_t *cmdnm, *sender, **argv;
277 if((argc = parrlen(argv = qcmd->args)) < 1)
281 if(wcslen(cmdnm) < 2)
284 if((*cmdnm == L'B') || (*cmdnm == L'F')) {
289 } else if((*cmdnm == L'D') || (*cmdnm == L'E')) {
296 for(cmd = hubcmds; cmd->func != NULL; cmd++) {
297 if(!wcscmp(cmd->name, qcmd->args[0] + 1)) {
298 if(argc < cmd->minargs)
300 cmd->func(fn, cmdnm, sender, argc, qcmd->args);
304 flog(LOG_DEBUG, "unknown adc command: %ls", qcmd->args[0]);
307 static void hubread(struct socket *sk, struct fnetnode *fn)
311 char *newbuf, *p1, *p2;
313 size_t datalen, cblen;
316 if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
318 if(hub->inbufdata > 1024)
320 bufcat(hub->inbuf, newbuf, datalen);
322 /* Memory eating protection */
323 if(hub->cbdata > 65536)
325 while(hub->inbufdata > 0) {
326 if(hub->cbdata == hub->cbsize)
327 sizebuf2(hub->cb, hub->cbdata + datalen, 1);
329 p2 = (char *)(hub->cb + hub->cbdata);
330 cblen = sizeof(*(hub->cb)) * (hub->cbsize - hub->cbdata);
331 ret = iconv(hub->ich, &p1, &hub->inbufdata, &p2, &cblen);
332 memmove(hub->inbuf, p1, hub->inbufdata);
333 if(((p2 - ((char *)hub->cb)) % sizeof(*hub->cb)) != 0) {
334 flog(LOG_CRIT, "Spotted a dismembered wchar_t!");
337 hub->cbdata = hub->cbsize - (cblen / sizeof(*(hub->cb)));
339 if(errno == EILSEQ) {
340 flog(LOG_DEBUG, "adc fnetnode %i sent illegal utf-8 sequence", fn->id);
343 } else if(errno == EINVAL) {
345 } else if(errno == E2BIG) {
348 flog(LOG_WARNING, "bug? iconv returned unexpected error: %s", strerror(errno));
353 while((p = wmemchr(hub->cb, L'\n', hub->cbdata)) != NULL) {
355 newqcmd(&hub->queue, parseadc(hub->cb));
356 memmove(hub->cb, p, (hub->cbdata -= (p - hub->cb)) * sizeof(*(hub->cb)));
360 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
365 static void hubconnect(struct fnetnode *fn, struct socket *sk)
369 sk->readcb = (void (*)(struct socket *, void *))hubread;
370 sk->errcb = (void (*)(struct socket *, int, void *))huberr;
373 hub = smalloc(sizeof(*hub));
374 memset(hub, 0, sizeof(*hub));
375 getsock(hub->sk = sk);
376 if((hub->ich = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1) {
377 flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
382 sendadc(sk, 0, L"HSUP", L"ADBASE", eoc, NULL);
385 static void hubdestroy(struct fnetnode *fn)
390 iconv_close(hub->ich);
391 if(hub->inbuf != NULL)
398 static void hubkill(struct fnetnode *fn)
406 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
411 static int hubreqconn(struct fnetpeer *peer)
416 static struct fnet adcnet_store = {
417 .connect = hubconnect,
418 .destroy = hubdestroy,
420 .setnick = hubsetnick,
421 .reqconn = hubreqconn,
425 static struct fnet *adcnet = &adcnet_store;
430 struct fnetnode *fn, *nextfn;
435 for(fn = fnetnodes; fn != NULL; fn = nextfn) {
437 if(fn->fnet != adcnet)
439 if((hub = fn->data) == NULL)
441 if((qcmd = ulqcmd(&hub->queue)) != NULL) {
442 if((hub->sk != NULL) && (hub->sk->state == SOCK_EST))
452 static void preinit(int hup)
459 static int init(int hup)
462 char idbuf[24], *id32;
468 if((privid = fetchvar("adc.pid", NULL)) == NULL) {
469 for(i = 0; i < sizeof(idbuf); i++)
470 idbuf[i] = rand() % 256;
471 id32 = base32encode(idbuf, sizeof(idbuf));
473 privid = icmbstowcs(id32, "us-ascii");
475 storevar("adc.pid", privid, sizeof(*privid) * (wcslen(privid) + 1));
478 id32 = base32decode(icswcstombs(privid, "us-ascii", NULL), NULL);
480 dotiger(&th, id32, 24);
483 restiger(&th, idbuf);
484 id32 = base32encode(idbuf, sizeof(idbuf));
486 cid = icmbstowcs(id32, "us-ascii");
492 static void terminate(void)
497 static struct configvar myvars[] = {
498 /** Specifies a specific UDP port to use for ADC search
499 * results. If left unspecified, a port is allocated
500 * dynamically. Useful for NAT routers (see also the
501 * net.visibleipv4 address for those cases). */
502 {CONF_VAR_INT, "udpport", {.num = 0}},
503 /** Specifies a specific TCP port to use for ADC peer
504 * connections. If left unspecified, a port is allocated
505 * dynamically. Useful for NAT routers (see also the
506 * net.visibleipv4 address for those cases). */
507 {CONF_VAR_INT, "tcpport", {.num = 0}},
511 static struct module me = {
518 .terminate = terminate,