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