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