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