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