Improve srs info.
[doldaconnect.git] / daemon / fnet-adc.c
CommitLineData
58e83d6a 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4 *
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.
9 *
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.
14 *
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
18*/
19#include <stdlib.h>
20#include <stdio.h>
21#include <unistd.h>
22#include <stdarg.h>
23#include <string.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <wctype.h>
27#include <iconv.h>
28#include <errno.h>
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33#include "filenet.h"
34#include "log.h"
35#include "module.h"
36#include "utils.h"
37#include "client.h"
38#include "transfer.h"
39#include "sysevents.h"
40#include "net.h"
41#include "tiger.h"
42
43/* Protocol states */
44#define ADC_PROTOCOL 0
45#define ADC_IDENTIFY 1
46#define ADC_VERIFY 2
47#define ADC_NORMAL 3
48#define ADC_DATA 4
49
50struct command {
51 wchar_t *name;
52 int minargs, needsender;
53 void (*func)(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv);
54};
55
56struct qcmd {
57 struct qcmd *next;
58 wchar_t **args;
59};
60
61struct adchub {
62 char *inbuf;
63 size_t inbufdata, inbufsize;
64 wchar_t *sid;
65 wchar_t *cb;
66 size_t cbdata, cbsize;
67 wchar_t **sup;
68 iconv_t ich;
69 int state;
70 struct qcmd *queue;
71};
72
73static wchar_t *eoc;
74/* I've never understood why both of these are necessary, but... */
75static wchar_t *privid, *cid;
76
77static wchar_t **parseadc(wchar_t *cmdline)
78{
79 wchar_t **ret;
80 size_t retsize, retdata;
81 wchar_t *p, *p2, *ep;
82 wchar_t r;
83 ssize_t len;
84
85 ret = NULL;
86 retsize = retdata = 0;
87 p = cmdline;
88 do {
89 if((p2 = wcschr(p, L' ')) != NULL)
90 *(p2++) = L'\0';
91 len = wcslen(p);
92 for(ep = p; len > 0; ep++, len--) {
93 if(*ep == L'\\') {
94 if(ep[1] == L's') {
95 r = L' ';
96 } else if(ep[1] == L'n') {
97 r = L'\n';
98 } else if(ep[1] == L'\\') {
99 r = L'\\';
100 } else {
101 memmove(ep, ep + 2, (len -= 2) * sizeof(*ep));
102 ep--;
103 continue;
104 }
105 memmove(ep, ep + 1, --len * sizeof(*ep));
106 *ep = r;
107 }
108 }
109 *ep = L'\0';
110 addtobuf(ret, swcsdup(p));
111 p = p2;
112 } while(p2 != NULL);
113 addtobuf(ret, NULL);
114 return(ret);
115}
116
117static void sendeoc(struct socket *sk)
118{
119 sockqueue(sk, "\n", 1);
120}
121
122static void sendadc1(struct socket *sk, int sep, wchar_t *arg)
123{
124 char *mbsbuf;
125 wchar_t *buf;
126 size_t bufsize, bufdata;
127
128 buf = NULL;
129 bufsize = bufdata = 0;
130 if(sep)
131 addtobuf(buf, L' ');
132 for(; *arg != L'\0'; arg++) {
133 if(*arg == L' ')
134 bufcat(buf, L"\\s", 2);
135 else if(*arg == L'\n')
136 bufcat(buf, L"\\n", 2);
137 else if(*arg == L'\\')
138 bufcat(buf, L"\\\\", 2);
139 else
140 addtobuf(buf, *arg);
141 }
142 addtobuf(buf, L'\0');
143 mbsbuf = icwcstombs(buf, "utf-8");
144 sockqueue(sk, mbsbuf, strlen(mbsbuf));
145 free(mbsbuf);
146}
147
148static void sendadc(struct socket *sk, int sep, ...)
149{
150 va_list args;
151 wchar_t *arg;
152 int i;
153
154 va_start(args, sep);
155 for(i = 0; (arg = va_arg(args, wchar_t *)) != NULL; i++) {
156 if(arg == eoc)
157 sendeoc(sk);
158 else
159 sendadc1(sk, (i == 0)?sep:1, arg);
160 }
161 va_end(args);
162}
163
164static struct qcmd *newqcmd(struct qcmd **queue, wchar_t **args)
165{
166 struct qcmd *new;
167
168 while(*queue != NULL)
169 queue = &((*queue)->next);
170 new = smalloc(sizeof(*new));
171 new->next = NULL;
172 new->args = args;
173 *queue = new;
174 return(new);
175}
176
177static struct qcmd *ulqcmd(struct qcmd **queue)
178{
179 struct qcmd *ret;
180
181 if((ret = *queue) == NULL)
182 return(NULL);
183 *queue = ret->next;
184 return(ret);
185}
186
187static void freeqcmd(struct qcmd *qcmd)
188{
189 freeparr(qcmd->args);
190 free(qcmd);
191}
192
193#define ADC_CMDFN(name) static void name(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv)
194#define ADC_CMDCOM struct socket *sk = fn->sk; struct adchub *hub = fn->data;
195
196ADC_CMDFN(cmd_sup)
197{
198 ADC_CMDCOM;
199 int i, o, f;
200
201 for(i = 1; i < argc; i++) {
202 if(wcslen(argv[i]) < 3)
203 continue;
204 for(o = 0, f = 0; hub->sup[o]; o++) {
205 if(!wcscmp(argv[i] + 2, hub->sup[o])) {
206 f = 1;
207 break;
208 }
209 }
210 if(!wcsncmp(argv[i], L"AD", 2)) {
211 if(f)
212 continue;
213 hub->sup = srealloc(hub->sup, sizeof(*(hub->sup)) * (o + 1));
214 hub->sup[o] = swcsdup(argv[i] + 2);
215 } else if(!wcsncmp(argv[i], L"RM", 2)) {
216 if(!f)
217 continue;
218 memmove(hub->sup[o], hub->sup[o + 1], parrlen(hub->sup) - o);
219 }
220 }
221}
222
223ADC_CMDFN(cmd_sid)
224{
225 ADC_CMDCOM;
226
227 if(hub->sid != NULL)
228 free(hub->sid);
229 hub->sid = swcsdup(argv[1]);
230 if(hub->state == ADC_PROTOCOL) {
231 hub->state = ADC_IDENTIFY;
232 }
233}
234
235static struct command hubcmds[] = {
236 {L"SUP", 1, 0, cmd_sup},
237 {L"SID", 2, 0, cmd_sid},
238 {NULL, 0, 0, NULL}
239};
240
241static void dispatch(struct qcmd *qcmd, struct fnetnode *fn)
242{
243 struct command *cmd;
244 int argc;
245 wchar_t *cmdnm, *sender, **argv;
246
247 if((argc = parrlen(argv = qcmd->args)) < 1)
248 return;
249 cmdnm = *(argv++);
250 argc--;
251 if(wcslen(cmdnm) < 2)
252 return;
253 sender = NULL;
254 if((*cmdnm == L'B') || (*cmdnm == L'F')) {
255 if(argc < 1)
256 return;
257 sender = *(argv++);
258 argc--;
259 } else if((*cmdnm == L'D') || (*cmdnm == L'E')) {
260 if(argc < 2)
261 return;
262 sender = *argv;
263 argv += 2;
264 argc -= 2;
265 }
266 for(cmd = hubcmds; cmd->func != NULL; cmd++) {
267 if(!wcscmp(cmd->name, qcmd->args[0] + 1)) {
268 if(argc < cmd->minargs)
269 return;
270 cmd->func(fn, cmdnm, sender, argc, qcmd->args);
271 return;
272 }
273 }
274 flog(LOG_DEBUG, "unknown adc command: %ls", qcmd->args[0]);
275}
276
277static void hubread(struct socket *sk, struct fnetnode *fn)
278{
279 int ret;
280 struct adchub *hub;
281 char *newbuf, *p1, *p2;
282 wchar_t *p;
283 size_t datalen, cblen;
284
285 hub = fn->data;
286 if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
287 return;
288 if(hub->inbufdata > 1024)
289 hub->inbufdata = 0;
290 bufcat(hub->inbuf, newbuf, datalen);
291 free(newbuf);
292 /* Memory eating protection */
293 if(hub->cbdata > 65536)
294 hub->cbdata = 0;
295 while(hub->inbufdata > 0) {
296 if(hub->cbdata == hub->cbsize)
297 sizebuf2(hub->cb, hub->cbdata + datalen, 1);
298 p1 = hub->inbuf;
299 p2 = (char *)(hub->cb + hub->cbdata);
300 cblen = sizeof(*(hub->cb)) * (hub->cbsize - hub->cbdata);
301 ret = iconv(hub->ich, &p1, &hub->inbufdata, &p2, &cblen);
302 memmove(hub->inbuf, p1, hub->inbufdata);
303 if(((p2 - ((char *)hub->cb)) % sizeof(*hub->cb)) != 0) {
304 flog(LOG_CRIT, "Spotted a dismembered wchar_t!");
305 abort();
306 }
307 hub->cbdata = hub->cbsize - (cblen / sizeof(*(hub->cb)));
308 if(ret < 0) {
309 if(errno == EILSEQ) {
310 flog(LOG_DEBUG, "adc fnetnode %i sent illegal utf-8 sequence", fn->id);
311 killfnetnode(fn);
312 return;
313 } else if(errno == EINVAL) {
314 break;
315 } else if(errno == E2BIG) {
316 /* continue; */
317 } else {
318 flog(LOG_WARNING, "bug? iconv returned unexpected error: %s", strerror(errno));
319 return;
320 }
321 }
322 }
323 while((p = wmemchr(hub->cb, L'\n', hub->cbdata)) != NULL) {
324 *(p++) = L'\0';
325 newqcmd(&hub->queue, parseadc(hub->cb));
326 memmove(hub->cb, p, (hub->cbdata -= (p - hub->cb)) * sizeof(*(hub->cb)));
327 }
328}
329
330static void huberr(struct socket *sk, int err, struct fnetnode *fn)
331{
332 killfnetnode(fn);
333}
334
335static void hubconnect(struct fnetnode *fn)
336{
337 struct adchub *hub;
338
339 fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
340 fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
341 fn->sk->data = fn;
342 getfnetnode(fn);
343
344 hub = smalloc(sizeof(*hub));
345 memset(hub, 0, sizeof(*hub));
346 if((hub->ich = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1) {
347 flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
348 killfnetnode(fn);
349 return;
350 }
351 fn->data = hub;
352 sendadc(fn->sk, 0, L"HSUP", L"ADBASE", eoc, NULL);
353}
354
355static void hubdestroy(struct fnetnode *fn)
356{
357 struct adchub *hub;
358
359 if((hub = fn->data) == NULL)
360 return;
361 iconv_close(hub->ich);
362 if(hub->inbuf != NULL)
363 free(hub->inbuf);
364 if(hub->sup != NULL)
365 freeparr(hub->sup);
366 free(hub);
367}
368
369static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
370{
371 return(0);
372}
373
374static int hubreqconn(struct fnetpeer *peer)
375{
376 return(0);
377}
378
379static struct fnet adcnet = {
380 .connect = hubconnect,
381 .destroy = hubdestroy,
382 .setnick = hubsetnick,
383 .reqconn = hubreqconn,
384 .name = L"adc"
385};
386
387static int run(void)
388{
389 int ret;
390 struct fnetnode *fn, *nextfn;
391 struct adchub *hub;
392 struct qcmd *qcmd;
393
394 ret = 0;
395 for(fn = fnetnodes; fn != NULL; fn = nextfn) {
396 nextfn = fn->next;
397 if(fn->fnet != &adcnet)
398 continue;
399 if((hub = fn->data) == NULL)
400 continue;
401 if((qcmd = ulqcmd(&hub->queue)) != NULL) {
402 if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
403 dispatch(qcmd, fn);
404 freeqcmd(qcmd);
405 ret = 1;
406 break;
407 }
408 }
409 return(ret);
410}
411
412static void preinit(int hup)
413{
414 if(hup)
415 return;
416 regfnet(&adcnet);
417}
418
419static int init(int hup)
420{
421 int i;
422 char idbuf[24], *id32;
423 struct tigerhash th;
424
425 if(!hup) {
426 eoc = swcsdup(L"");
427
428 if((privid = fetchvar("adc.pid", NULL)) == NULL) {
429 for(i = 0; i < sizeof(idbuf); i++)
430 idbuf[i] = rand() % 256;
431 id32 = base32encode(idbuf, sizeof(idbuf));
432 id32[39] = 0;
433 privid = icmbstowcs(id32, "us-ascii");
434 free(id32);
435 storevar("adc.pid", privid, sizeof(*privid) * (wcslen(privid) + 1));
436 }
437
438 id32 = base32decode(icswcstombs(privid, "us-ascii", NULL), NULL);
439 inittiger(&th);
440 dotiger(&th, id32, 24);
441 synctiger(&th);
442 free(id32);
443 restiger(&th, idbuf);
444 id32 = base32encode(idbuf, sizeof(idbuf));
445 id32[39] = 0;
446 cid = icmbstowcs(id32, "us-ascii");
447 free(id32);
448 }
449 return(0);
450}
451
452static void terminate(void)
453{
454
455}
456
457static struct configvar myvars[] = {
458 {CONF_VAR_INT, "udpport", {.num = 0}},
459 {CONF_VAR_INT, "tcpport", {.num = 0}},
460 {CONF_VAR_END}
461};
462
463static struct module me = {
464 .conf = {
465 .vars = myvars
466 },
467 .preinit = preinit,
468 .init = init,
469 .run = run,
470 .terminate = terminate,
471 .name = "adc"
472};
473
474MODULE(me)