Don't print the path from Unix sockets as abstract peers seem to not follow the speci...
[doldaconnect.git] / lib / uilib.c
CommitLineData
d3372da9 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
20/*
21 * Note: This code is still ugly, since I copied it almost verbatim
22 * from the daemon's parser. It would need serious cleanups, but at
23 * least it works for now.
24 */
25
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <string.h>
33#include <iconv.h>
34#include <wchar.h>
35#include <wctype.h>
36#include <errno.h>
37#include <stdarg.h>
38#include <sys/socket.h>
39#include <netinet/in.h>
40#include <arpa/inet.h>
12383d48 41#include <sys/un.h>
d3372da9 42#include <fcntl.h>
43#include <netdb.h>
44#include <sys/poll.h>
12383d48 45#include <pwd.h>
d3372da9 46#ifdef HAVE_RESOLVER
47#include <arpa/nameser.h>
48#include <resolv.h>
49#endif
50
51#include <doldaconnect/uilib.h>
31c700d1 52#include <utils.h>
d3372da9 53
4d487bad 54#define DOLCON_SRV_NAME "_dolcon._tcp"
55
d3372da9 56#define RESP_END -1
57#define RESP_DSC 0
58#define RESP_STR 1
59#define RESP_INT 2
60#define RESP_FLOAT 3
61
62struct respclass
63{
64 struct respclass *next;
65 int code;
66 int nwords;
67 int *wordt;
68};
69
70struct command
71{
72 struct command *next;
73 wchar_t *name;
74 struct respclass *classes;
75};
76
77struct qcmd
78{
79 struct qcmd *next;
80 struct command *cmd;
81 int tag;
82 char *buf;
83 size_t buflen;
84 int (*callback)(struct dc_response *resp);
85 void *data;
86};
87
88void dc_uimisc_disconnected(void);
89
90/* The first command must be the nameless connect command and second
91 * the notification command. */
92static struct command *commands = NULL;
93static struct qcmd *queue = NULL, *queueend = NULL;
94static struct dc_response *respqueue = NULL, *respqueueend = NULL;
95static int state = -1;
96static int fd = -1;
97static iconv_t ichandle;
98static int resetreader = 1;
99static char *dchostname = NULL;
100static struct addrinfo *hostlist = NULL, *curhost = NULL;
d3372da9 101
102static struct dc_response *makeresp(void)
103{
104 struct dc_response *new;
105
106 new = smalloc(sizeof(*new));
107 new->next = NULL;
108 new->prev = NULL;
109 new->code = 0;
110 new->cmdname = NULL;
111 new->rlines = NULL;
112 new->linessize = 0;
113 new->numlines = 0;
114 new->curline = 0;
115 new->data = NULL;
116 return(new);
117}
118
119static void freeqcmd(struct qcmd *qcmd)
120{
121 if(qcmd->buf != NULL)
122 free(qcmd->buf);
123 free(qcmd);
124}
125
126static void unlinkqueue(void)
127{
128 struct qcmd *qcmd;
129
130 if((qcmd = queue) == NULL)
131 return;
132 queue = qcmd->next;
133 if(queue == NULL)
134 queueend = NULL;
135 freeqcmd(qcmd);
136}
137
138static struct qcmd *makeqcmd(wchar_t *name)
139{
140 struct qcmd *new;
141 struct command *cmd;
142 static int tag = 0;
143
144 if(name == NULL)
145 {
146 cmd = commands;
147 } else {
148 for(cmd = commands; cmd != NULL; cmd = cmd->next)
149 {
150 if((cmd->name != NULL) && !wcscmp(cmd->name, name))
151 break;
152 }
153 if(cmd == NULL)
154 return(NULL);
155 }
156 new = smalloc(sizeof(*new));
157 new->tag = tag++;
158 new->cmd = cmd;
159 new->buf = NULL;
160 new->buflen = 0;
161 new->callback = NULL;
162 new->next = NULL;
163 new->data = NULL;
164 if(queueend == NULL)
165 {
166 queue = queueend = new;
167 } else {
168 queueend->next = new;
169 queueend = new;
170 }
171 return(new);
172}
173
174static wchar_t *quoteword(wchar_t *word)
175{
176 wchar_t *wp, *buf, *bp;
177 int dq, numbs, numc;
178
179 dq = 0;
180 numbs = 0;
181 numc = 0;
182 if(*word == L'\0')
183 {
184 dq = 1;
185 } else {
186 for(wp = word; *wp != L'\0'; wp++)
187 {
188 if(!dq && iswspace(*wp))
189 dq = 1;
190 if((*wp == L'\\') || (*wp == L'\"'))
191 numbs++;
192 numc++;
193 }
194 }
195 if(!dq && !numbs)
196 return(NULL);
197 bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
198 if(dq)
199 *(bp++) = L'\"';
200 for(wp = word; *wp != L'\0'; wp++)
201 {
202 if((*wp == L'\\') || (*wp == L'\"'))
203 *(bp++) = L'\\';
204 *(bp++) = *wp;
205 }
206 if(dq)
207 *(bp++) = L'\"';
208 *(bp++) = L'\0';
209 return(buf);
210}
211
212static struct command *makecmd(wchar_t *name)
213{
214 struct command *new;
215
216 new = smalloc(sizeof(*new));
217 new->name = name;
218 new->classes = NULL;
219 new->next = commands;
220 commands = new;
221 return(new);
222}
223
224static struct respclass *addresp(struct command *cmd, int code, ...)
225{
226 struct respclass *new;
227 int i;
228 int resps[128];
229 va_list args;
230
231 va_start(args, code);
232 i = 0;
233 while((resps[i++] = va_arg(args, int)) != RESP_END);
234 i--;
235 va_end(args);
236 new = smalloc(sizeof(*new));
237 new->code = code;
238 new->nwords = i;
239 if(i > 0)
240 {
241 new->wordt = smalloc(sizeof(int) * i);
242 memcpy(new->wordt, resps, sizeof(int) * i);
243 } else {
244 new->wordt = NULL;
245 }
246 new->next = cmd->classes;
247 cmd->classes = new;
248 return(new);
249}
250
251#include "initcmds.h"
252
253int dc_init(void)
254{
255 if((ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
256 return(-1);
257 initcmds();
258 return(0);
259}
260
261void dc_cleanup(void)
262{
263 iconv_close(ichandle);
264}
265
266void dc_disconnect(void)
267{
268 struct dc_response *resp;
269
270 state = -1;
271 if(fd >= 0)
272 close(fd);
273 fd = -1;
274 while(queue != NULL)
275 unlinkqueue();
276 while((resp = dc_getresp()) != NULL)
277 dc_freeresp(resp);
278 dc_uimisc_disconnected();
279 if(dchostname != NULL)
280 free(dchostname);
281 dchostname = NULL;
282}
283
284void dc_freeresp(struct dc_response *resp)
285{
286 int i, o;
287
288 for(i = 0; i < resp->numlines; i++)
289 {
290 for(o = 0; o < resp->rlines[i].argc; o++)
291 free(resp->rlines[i].argv[o]);
292 free(resp->rlines[i].argv);
293 }
294 free(resp->rlines);
295 free(resp);
296}
297
298struct dc_response *dc_getresp(void)
299{
300 struct dc_response *ret;
301
302 if((ret = respqueue) == NULL)
303 return(NULL);
304 respqueue = ret->next;
305 if(respqueue == NULL)
306 respqueueend = NULL;
307 else
308 respqueue->prev = NULL;
309 return(ret);
310}
311
312struct dc_response *dc_gettaggedresp(int tag)
313{
314 struct dc_response *resp;
315
316 for(resp = respqueue; resp != NULL; resp = resp->next)
317 {
318 if(resp->tag == tag)
319 {
320 if(resp->prev != NULL)
321 resp->prev->next = resp->next;
322 if(resp->next != NULL)
323 resp->next->prev = resp->prev;
324 if(resp == respqueue)
325 respqueue = resp->next;
326 if(resp == respqueueend)
327 respqueueend = resp->prev;
328 return(resp);
329 }
330 }
331 return(NULL);
332}
333
334struct dc_response *dc_gettaggedrespsync(int tag)
335{
336 struct pollfd pfd;
337 struct dc_response *resp;
338
339 while((resp = dc_gettaggedresp(tag)) == NULL)
340 {
341 pfd.fd = fd;
342 pfd.events = POLLIN;
343 if(dc_wantwrite())
344 pfd.events |= POLLOUT;
345 if(poll(&pfd, 1, -1) < 0)
346 return(NULL);
347 if((pfd.revents & POLLIN) && dc_handleread())
348 return(NULL);
349 if((pfd.revents & POLLOUT) && dc_handlewrite())
350 return(NULL);
351 }
352 return(resp);
353}
354
355int dc_wantwrite(void)
356{
357 switch(state)
358 {
359 case 1:
360 if((queue != NULL) && (queue->buflen > 0))
361 return(1);
362 break;
363 }
364 return(0);
365}
366
367int dc_getstate(void)
368{
369 return(state);
370}
371
372int dc_queuecmd(int (*callback)(struct dc_response *), void *data, ...)
373{
374 struct qcmd *qcmd;
375 int num, freepart;
376 va_list al;
377 char *final;
378 wchar_t **toks;
379 wchar_t *buf;
380 wchar_t *part, *tpart;
381 size_t bufsize, bufdata;
382
383 buf = NULL;
384 bufsize = bufdata = 0;
385 num = 0;
386 va_start(al, data);
387 while((part = va_arg(al, wchar_t *)) != NULL)
388 {
389 if(!wcscmp(part, L"%%a"))
390 {
391 for(toks = va_arg(al, wchar_t **); *toks != NULL; toks++)
392 {
393 part = *toks;
394 freepart = 0;
395 if((tpart = quoteword(part)) != NULL)
396 {
397 freepart = 1;
398 part = tpart;
399 }
400 addtobuf(buf, L' ');
401 bufcat(buf, part, wcslen(part));
402 num++;
403 if(freepart)
404 free(part);
405 }
406 } else {
407 if(*part == L'%')
408 {
409 /* This demands that all arguments that are passed to the
410 * function are of equal length, that of an int. I know
411 * that GCC does that on IA32 platforms, but I do not know
412 * which other platforms and compilers that it applies
413 * to. If this breaks your platform, please mail me about
414 * it.
415 */
416 part = vswprintf2(tpart = (part + 1), al);
417 for(; *tpart != L'\0'; tpart++)
418 {
419 if(*tpart == L'%')
420 {
421 if(tpart[1] == L'%')
422 tpart++;
423 else
424 va_arg(al, int);
425 }
426 }
427 freepart = 1;
428 } else {
429 freepart = 0;
430 }
431 if((tpart = quoteword(part)) != NULL)
432 {
433 if(freepart)
434 free(part);
435 part = tpart;
436 freepart = 1;
437 }
438 if(num > 0)
439 addtobuf(buf, L' ');
440 if(num == 0)
441 {
442 if((qcmd = makeqcmd(part)) == NULL)
443 {
444 if(freepart)
445 free(part);
446 if(buf != NULL)
447 free(buf);
448 return(-1);
449 } else {
450 qcmd->callback = callback;
451 qcmd->data = data;
452 }
453 }
454 bufcat(buf, part, wcslen(part));
455 num++;
456 if(freepart)
457 free(part);
458 }
459 }
460 bufcat(buf, L"\r\n\0", 3);
461 if((final = icwcstombs(buf, "utf-8")) == NULL)
462 {
463 free(buf);
464 return(-1);
465 }
466 va_end(al);
467 free(buf);
468 qcmd->buf = final;
469 qcmd->buflen = strlen(final);
470 return(qcmd->tag);
471}
472
473int dc_handleread(void)
474{
475 int ret, done;
476 char *p1, *p2;
477 size_t len;
26d72b0d 478 socklen_t optlen;
d3372da9 479 int errnobak;
480 /* Ewww... this really is soo ugly. I need to clean this up some day. */
481 static int pstate = 0;
482 static char inbuf[128];
483 static size_t inbufdata = 0;
484 static wchar_t *cbuf = NULL;
485 static size_t cbufsize = 0, cbufdata = 0;
486 static wchar_t *pptr = NULL;
487 static wchar_t **argv = NULL;
26d72b0d 488 static int argc = 0;
489 static size_t args = 0;
d3372da9 490 static wchar_t *cw = NULL;
491 static size_t cwsize = 0, cwdata = 0;
492 static struct dc_response *curresp = NULL;
493 static int cont = 0;
494 static int unlink = 0;
495
496 switch(state)
497 {
498 case -1:
499 return(-1);
500 case 0:
26d72b0d 501 optlen = sizeof(ret);
502 getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
d3372da9 503 if(ret)
504 {
505 int newfd;
12383d48 506
d3372da9 507 for(curhost = curhost->ai_next; curhost != NULL; curhost = curhost->ai_next)
508 {
509 if((newfd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
510 {
511 errnobak = errno;
512 dc_disconnect();
513 errno = errnobak;
514 return(-1);
515 }
516 dup2(newfd, fd);
517 close(newfd);
518 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
12383d48 519 if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
d3372da9 520 {
521 if(errno == EINPROGRESS)
522 return(0);
523 } else {
524 break;
525 }
526 }
527 if(curhost == NULL)
528 {
529 dc_disconnect();
530 errno = ret;
531 return(-1);
532 }
533 }
12383d48 534 if(curhost->ai_canonname != NULL)
535 dchostname = sstrdup(curhost->ai_canonname);
d3372da9 536 state = 1;
537 resetreader = 1;
538 break;
539 case 1:
540 if(resetreader)
541 {
542 inbufdata = 0;
543 cbufdata = 0;
544 pptr = NULL;
545 pstate = 0;
546 resetreader = 0;
547 cont = 0;
548 if(curresp != NULL)
549 dc_freeresp(curresp);
550 curresp = NULL;
551 }
552 if(inbufdata == 128)
553 inbufdata = 0;
554 ret = read(fd, inbuf + inbufdata, 128 - inbufdata);
555 if(ret < 0)
556 {
557 if((errno == EAGAIN) || (errno == EINTR))
558 return(0);
559 errnobak = errno;
560 dc_disconnect();
561 errno = errnobak;
562 return(-1);
563 } else if(ret == 0) {
564 dc_disconnect();
565 errno = 0;
566 return(-1);
567 }
568 inbufdata += ret;
569 done = 0;
570 while(!done)
571 {
572 if(cbufsize == cbufdata)
573 {
574 if(pptr != NULL)
575 len = pptr - cbuf;
576 if((cbuf = realloc(cbuf, sizeof(wchar_t) * (cbufsize += 256))) == NULL)
577 {
578 dc_disconnect();
579 errno = ENOMEM;
580 return(-1);
581 }
582 if(pptr != NULL)
583 pptr = cbuf + len;
584 }
585 p1 = inbuf;
586 p2 = (char *)(cbuf + cbufdata);
587 len = sizeof(wchar_t) * (cbufsize - cbufdata);
588 ret = iconv(ichandle, &p1, &inbufdata, &p2, &len);
589 memmove(inbuf, p1, inbufdata);
590 cbufdata = cbufsize - (len / sizeof(wchar_t));
591 if(ret < 0)
592 {
593 switch(errno)
594 {
595 case EILSEQ:
596 /* XXX Is this really OK? */
597 inbufdata = 0;
598 done = 1;
599 break;
600 case EINVAL:
601 done = 1;
602 break;
603 case E2BIG:
604 break;
605 default:
606 errnobak = errno;
607 dc_disconnect();
608 errno = errnobak;
609 return(-1);
610 }
611 } else {
612 done = 1;
613 }
614 }
615 if(pptr == NULL)
616 pptr = cbuf;
617 done = 0;
618 while(!done && (pptr - cbuf < cbufdata))
619 {
620 switch(pstate)
621 {
622 case 0:
623 if(iswspace(*pptr))
624 {
625 if(*pptr == L'\r')
626 {
627 if(pptr == cbuf + cbufdata - 1)
628 {
629 done = 1;
630 break;
631 }
632 if(*(++pptr) == L'\n')
633 {
634 if(curresp == NULL)
635 {
636 curresp = makeresp();
637 if((argc > 0) && ((curresp->code = wcstol(argv[0], NULL, 10)) >= 600))
638 {
639 curresp->cmdname = L".notify";
640 curresp->internal = commands->next;
641 curresp->tag = -1;
642 unlink = 0;
643 } else {
644 if((curresp->cmdname = queue->cmd->name) == NULL)
645 curresp->cmdname = L".connect";
646 curresp->data = queue->data;
647 curresp->tag = queue->tag;
648 curresp->internal = (void *)(queue->cmd);
649 unlink = 1;
650 }
651 }
652 sizebuf(&curresp->rlines, &curresp->linessize, curresp->numlines + 1, sizeof(*(curresp->rlines)), 1);
653 curresp->rlines[curresp->numlines].argc = argc;
654 curresp->rlines[curresp->numlines].argv = argv;
655 argv = NULL;
656 argc = args = 0;
657 curresp->numlines++;
658 if(!cont)
659 {
660 if((curresp->code >= 600) || (queue == NULL) || (queue->callback == NULL))
661 ret = 0;
662 else
663 ret = queue->callback(curresp);
664 if(ret == 0)
665 {
666 if(respqueue == NULL)
667 {
668 respqueue = respqueueend = curresp;
669 } else {
670 curresp->next = NULL;
671 curresp->prev = respqueueend;
672 respqueueend->next = curresp;
673 respqueueend = curresp;
674 }
675 } else if(ret == 1) {
676 dc_freeresp(curresp);
677 }
678 curresp = NULL;
679 if(unlink)
680 unlinkqueue();
681 }
682 wmemmove(cbuf, pptr, cbufdata -= (pptr - cbuf));
683 pptr = cbuf;
684 } else {
685 pptr++;
686 }
687 } else {
688 pptr++;
689 }
690 } else {
691 pstate = 1;
692 cwdata = 0;
693 }
694 break;
695 case 1:
696 if(iswspace(*pptr) || ((argc == 0) && (*pptr == L'-')))
697 {
698 if(argc == 0)
699 {
700 if(*pptr == L'-')
701 {
702 cont = 1;
703 pptr++;
704 } else {
705 cont = 0;
706 }
707 }
708 addtobuf(cw, L'\0');
709 sizebuf(&argv, &args, argc + 1, sizeof(*argv), 1);
710 argv[argc++] = cw;
711 cw = NULL;
712 cwsize = cwdata = 0;
713 pstate = 0;
714 } else if(*pptr == L'\"') {
715 pstate = 2;
716 pptr++;
717 } else if(*pptr == L'\\') {
718 if(pptr == cbuf + cbufdata - 1)
719 {
720 done = 1;
721 break;
722 }
723 addtobuf(cw, *(++pptr));
724 pptr++;
725 } else {
726 addtobuf(cw, *(pptr++));
727 }
728 break;
729 case 2:
730 if(*pptr == L'\"')
731 {
732 pstate = 1;
733 } else if(*pptr == L'\\') {
734 addtobuf(cw, *(++pptr));
735 } else {
736 addtobuf(cw, *pptr);
737 }
738 pptr++;
739 break;
740 }
741 }
742 break;
743 }
744 return(0);
745}
746
747int dc_handlewrite(void)
748{
749 int ret;
750 int errnobak;
751
752 switch(state)
753 {
754 case 1:
755 if(queue->buflen > 0)
756 {
757 ret = send(fd, queue->buf, queue->buflen, MSG_NOSIGNAL | MSG_DONTWAIT);
758 if(ret < 0)
759 {
760 if((errno == EAGAIN) || (errno == EINTR))
761 return(0);
762 errnobak = errno;
763 dc_disconnect();
764 errno = errnobak;
765 return(-1);
766 }
767 if(ret > 0)
768 memmove(queue->buf, queue->buf + ret, queue->buflen -= ret);
769 }
770 break;
771 }
772 return(0);
773}
774
775#ifdef HAVE_RESOLVER
0c58c2a0 776/*
777 * It kind of sucks that libresolv doesn't have any DNS parsing
778 * routines. We'll have to do it manually.
779 */
d3372da9 780static char *readname(unsigned char *msg, unsigned char *eom, unsigned char **p)
781{
782 char *name, *tname;
783 unsigned char *tp;
784 size_t namesize, namedata, len;
785
786 name = NULL;
787 namesize = namedata = 0;
788 while(1)
789 {
790 len = *((*p)++);
791 if(len == 0)
792 {
793 addtobuf(name, 0);
794 return(name);
795 } else if(len == 0xc0) {
796 tp = msg + *((*p)++);
797 if((tname = readname(msg, eom, &tp)) == NULL)
798 {
799 if(name != NULL)
800 free(name);
801 return(NULL);
802 }
803 bufcat(name, tname, strlen(tname));
804 addtobuf(name, 0);
805 free(tname);
806 return(name);
807 } else if(*p + len >= eom) {
808 if(name != NULL)
809 free(name);
810 return(NULL);
811 }
812 bufcat(name, *p, len);
813 *p += len;
814 addtobuf(name, '.');
815 }
816}
817
818static int skipname(unsigned char *msg, unsigned char *eom, unsigned char **p)
819{
820 size_t len;
821
822 while(1)
823 {
824 len = *((*p)++);
825 if(len == 0)
826 {
827 return(0);
828 } else if(len == 0xc0) {
829 (*p)++;
830 return(0);
831 } else if(*p + len >= eom) {
832 return(-1);
833 }
834 *p += len;
835 }
836}
837
838static int getsrvrr(char *name, char **host, int *port)
839{
840 int i;
841 char *name2, *rrname;
842 unsigned char *eom, *p;
843 unsigned char buf[1024];
844 int flags, num, class, type;
845 size_t len;
846 int ret;
847
848 if(!(_res.options & RES_INIT))
849 {
850 if(res_init() < 0)
851 return(-1);
852 }
853 /* res_querydomain doesn't work for some reason */
4d487bad 854 if(name[strlen(name) - 1] == '.')
855 name2 = sprintf2("%s.%s", DOLCON_SRV_NAME, name);
856 else
857 name2 = sprintf2("%s.%s.", DOLCON_SRV_NAME, name);
d3372da9 858 ret = res_query(name2, C_IN, T_SRV, buf, sizeof(buf));
859 if(ret < 0)
860 {
861 free(name2);
862 return(-1);
863 }
864 eom = buf + ret;
0c58c2a0 865 /*
866 * Assume transaction ID is correct.
867 *
868 * Flags check: FA0F masks in request/response flag, opcode,
869 * truncated flag and status code, and ignores authoritativeness,
870 * recursion flags and DNSSEC and reserved bits.
871 */
d3372da9 872 flags = (buf[2] << 8) + buf[3];
873 if((flags & 0xfa0f) != 0x8000)
874 {
875 free(name2);
876 return(-1);
877 }
0c58c2a0 878 /* Skip the query entries */
d3372da9 879 num = (buf[4] << 8) + buf[5];
880 p = buf + 12;
881 for(i = 0; i < num; i++)
882 {
883 if(skipname(buf, eom, &p))
884 {
885 free(name2);
886 return(-1);
887 }
0c58c2a0 888 p += 4; /* Type and class */
d3372da9 889 }
0c58c2a0 890 /* Parse answers */
d3372da9 891 num = (buf[6] << 8) + buf[7];
892 for(i = 0; i < num; i++)
893 {
894 if((rrname = readname(buf, eom, &p)) == NULL)
895 {
896 free(name2);
897 return(-1);
898 }
899 type = *(p++) << 8;
900 type += *(p++);
901 class = *(p++) << 8;
902 class += *(p++);
0c58c2a0 903 p += 4; /* TTL */
d3372da9 904 len = *(p++) << 8;
905 len += *(p++);
906 if((class == C_IN) && (type == T_SRV) && !strcmp(rrname, name2))
907 {
908 free(rrname);
909 free(name2);
910 /* Noone will want to have alternative DC servers, so
911 * don't care about priority and weigth */
912 p += 4;
913 if(port == NULL)
914 {
915 p += 2;
916 } else {
917 *port = *(p++) << 8;
918 *port += *(p++);
919 }
920 if(host != NULL)
921 {
922 if((rrname = readname(buf, eom, &p)) == NULL)
923 return(-1);
924 *host = rrname;
925 }
926 return(0);
927 }
928 p += len;
929 free(rrname);
930 }
931 free(name2);
932 return(-1);
933}
934#else
935static int getsrvrr(char *name, char **host, int *port)
936{
c4c34936 937 errno = EOPNOTSUPP;
d3372da9 938 return(-1);
939}
940#endif
941
12383d48 942static struct addrinfo *gaicat(struct addrinfo *l1, struct addrinfo *l2)
d3372da9 943{
12383d48 944 struct addrinfo *p;
945
946 if(l1 == NULL)
947 return(l2);
948 for(p = l1; p->ai_next != NULL; p = p->ai_next);
949 p->ai_next = l2;
950 return(l1);
951}
952
953/* This isn't actually correct, in any sense of the word. It only
954 * works on systems whose getaddrinfo implementation saves the
955 * sockaddr in the same malloc block as the struct addrinfo. Those
956 * systems include at least FreeBSD and glibc-based systems, though,
957 * so it should not be any immediate threat, and it allows me to not
958 * implement a getaddrinfo wrapper. It can always be changed, should
959 * the need arise. */
960static struct addrinfo *unixgai(int type, char *path)
961{
962 void *buf;
963 struct addrinfo *ai;
964 struct sockaddr_un *un;
965
966 buf = smalloc(sizeof(*ai) + sizeof(*un));
967 memset(buf, 0, sizeof(*ai) + sizeof(*un));
968 ai = (struct addrinfo *)buf;
969 un = (struct sockaddr_un *)(buf + sizeof(*ai));
970 ai->ai_flags = 0;
971 ai->ai_family = AF_UNIX;
972 ai->ai_socktype = type;
973 ai->ai_protocol = 0;
974 ai->ai_addrlen = sizeof(*un);
975 ai->ai_addr = (struct sockaddr *)un;
976 ai->ai_canonname = NULL;
977 ai->ai_next = NULL;
978 un->sun_family = PF_UNIX;
979 strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
980 return(ai);
981}
982
983static struct addrinfo *resolvtcp(char *name, int port)
984{
985 struct addrinfo hint, *ret;
986 char tmp[32];
d3372da9 987
d3372da9 988 memset(&hint, 0, sizeof(hint));
989 hint.ai_socktype = SOCK_STREAM;
12383d48 990 hint.ai_flags = AI_NUMERICSERV | AI_CANONNAME;
991 snprintf(tmp, sizeof(tmp), "%i", port);
992 if(!getaddrinfo(name, tmp, &hint, &ret))
993 return(ret);
994 return(NULL);
995}
996
997static struct addrinfo *resolvsrv(char *name)
998{
999 struct addrinfo *ret;
1000 char *realname;
1001 int port;
1002
1003 if(getsrvrr(name, &realname, &port))
1004 return(NULL);
1005 ret = resolvtcp(realname, port);
1006 free(realname);
1007 return(ret);
1008}
1009
1010static struct addrinfo *resolvhost(char *host)
1011{
1012 char *p, *hp;
1013 struct addrinfo *ret;
1014 int port;
1015
1016 if(strchr(host, '/'))
1017 return(unixgai(SOCK_STREAM, host));
1018 if((strchr(host, ':') == NULL) && ((ret = resolvsrv(host)) != NULL))
1019 return(ret);
1020 ret = NULL;
1021 if((*host == '[') && ((p = strchr(host, ']')) != NULL))
d3372da9 1022 {
12383d48 1023 hp = memcpy(smalloc(p - host), host + 1, (p - host) - 1);
1024 hp[(p - host) - 1] = 0;
1025 if(strchr(hp, ':') != NULL) {
1026 port = 0;
1027 if(*(++p) == ':')
1028 port = atoi(p + 1);
1029 if(port == 0)
1030 port = 1500;
1031 ret = resolvtcp(hp, port);
d3372da9 1032 }
12383d48 1033 free(hp);
1034 }
1035 if(ret != NULL)
1036 return(ret);
1037 hp = sstrdup(host);
1038 port = 0;
1039 if((p = strrchr(hp, ':')) != NULL) {
1040 *(p++) = 0;
1041 port = atoi(p);
1042 }
1043 if(port == 0)
1044 port = 1500;
1045 ret = resolvtcp(hp, port);
1046 free(hp);
1047 if(ret != NULL)
1048 return(ret);
1049 return(NULL);
1050}
1051
1052static struct addrinfo *defaulthost(void)
1053{
1054 struct addrinfo *ret;
1055 struct passwd *pwd;
1056 char *tmp;
1057 char dn[1024];
1058
1059 if(((tmp = getenv("DCSERVER")) != NULL) && *tmp)
1060 return(resolvhost(tmp));
1061 ret = NULL;
1062 if((getuid() != 0) && ((pwd = getpwuid(getuid())) != NULL))
1063 {
1064 tmp = sprintf2("/tmp/doldacond-%s", pwd->pw_name);
1065 ret = gaicat(ret, unixgai(SOCK_STREAM, tmp));
1066 free(tmp);
d3372da9 1067 }
12383d48 1068 ret = gaicat(ret, unixgai(SOCK_STREAM, "/var/run/doldacond.sock"));
1069 ret = gaicat(ret, resolvtcp("localhost", 1500));
1070 if(!getdomainname(dn, sizeof(dn)) && *dn && strcmp(dn, "(none)"))
1071 ret = gaicat(ret, resolvsrv(dn));
1072 return(ret);
1073}
1074
1075int dc_connect(char *host)
1076{
1077 struct qcmd *qcmd;
1078 int errnobak;
1079
1080 if(fd >= 0)
1081 dc_disconnect();
1082 state = -1;
d3372da9 1083 if(hostlist != NULL)
1084 freeaddrinfo(hostlist);
12383d48 1085 if(!host || !*host)
1086 hostlist = defaulthost();
1087 else
1088 hostlist = resolvhost(host);
1089 if(hostlist == NULL)
d3372da9 1090 return(-1);
d3372da9 1091 for(curhost = hostlist; curhost != NULL; curhost = curhost->ai_next)
1092 {
1093 if((fd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
1094 {
1095 errnobak = errno;
d3372da9 1096 errno = errnobak;
1097 return(-1);
1098 }
1099 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
12383d48 1100 if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
d3372da9 1101 {
1102 if(errno == EINPROGRESS)
1103 {
1104 state = 0;
1105 break;
1106 }
1107 close(fd);
1108 fd = -1;
1109 } else {
12383d48 1110 if(curhost->ai_canonname != NULL)
1111 dchostname = sstrdup(curhost->ai_canonname);
d3372da9 1112 state = 1;
1113 break;
1114 }
1115 }
1116 qcmd = makeqcmd(NULL);
1117 resetreader = 1;
d3372da9 1118 return(fd);
1119}
1120
1121struct dc_intresp *dc_interpret(struct dc_response *resp)
1122{
1123 int i;
1124 struct dc_intresp *iresp;
1125 struct command *cmd;
1126 struct respclass *cls;
1127 int code;
26d72b0d 1128 size_t args;
d3372da9 1129
1130 if((resp->numlines == 0) || (resp->rlines[0].argc == 0) || (resp->curline >= resp->numlines))
1131 return(NULL);
1132 code = wcstol(resp->rlines[0].argv[0], NULL, 10);
1133 cmd = (struct command *)(resp->internal);
1134 for(cls = cmd->classes; cls != NULL; cls = cls->next)
1135 {
1136 if(cls->code == code)
1137 break;
1138 }
1139 if(cls == NULL)
1140 return(NULL);
1141 if(cls->nwords >= resp->rlines[resp->curline].argc)
1142 return(NULL);
1143 iresp = smalloc(sizeof(*iresp));
1144 iresp->code = code;
1145 iresp->argv = NULL;
1146 iresp->argc = 0;
1147 args = 0;
1148 for(i = 0; i < cls->nwords; i++)
1149 {
1150 switch(cls->wordt[i])
1151 {
1152 case RESP_DSC:
1153 break;
1154 case RESP_STR:
1155 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1156 iresp->argv[iresp->argc].val.str = swcsdup(resp->rlines[resp->curline].argv[i + 1]);
1157 iresp->argv[iresp->argc].type = cls->wordt[i];
1158 iresp->argc++;
1159 break;
1160 case RESP_INT:
1161 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1162 iresp->argv[iresp->argc].val.num = wcstol(resp->rlines[resp->curline].argv[i + 1], NULL, 0);
1163 iresp->argv[iresp->argc].type = cls->wordt[i];
1164 iresp->argc++;
1165 break;
1166 case RESP_FLOAT:
1167 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1168 iresp->argv[iresp->argc].val.flnum = wcstod(resp->rlines[resp->curline].argv[i + 1], NULL);
1169 iresp->argv[iresp->argc].type = cls->wordt[i];
1170 iresp->argc++;
1171 break;
1172 }
1173 }
1174 resp->curline++;
1175 return(iresp);
1176}
1177
1178void dc_freeires(struct dc_intresp *ires)
1179{
1180 int i;
1181
1182 for(i = 0; i < ires->argc; i++)
1183 {
1184 if(ires->argv[i].type == RESP_STR)
1185 free(ires->argv[i].val.str);
1186 }
1187 free(ires->argv);
1188 free(ires);
1189}
1190
1191const char *dc_gethostname(void)
1192{
1193 return(dchostname);
1194}