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