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