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