Add Linux/BSD switch for Unix auth.
[doldaconnect.git] / lib / uilib.c
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>
41 #include <sys/un.h>
42 #include <fcntl.h>
43 #include <netdb.h>
44 #include <sys/poll.h>
45 #include <pwd.h>
46 #ifdef HAVE_RESOLVER
47 #include <arpa/nameser.h>
48 #include <resolv.h>
49 #endif
50
51 #include <doldaconnect/uilib.h>
52 #include <utils.h>
53
54 #define DOLCON_SRV_NAME "_dolcon._tcp"
55
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
62 struct respclass
63 {
64     struct respclass *next;
65     int code;
66     int nwords;
67     int *wordt;
68 };
69
70 struct command
71 {
72     struct command *next;
73     wchar_t *name;
74     struct respclass *classes;
75 };
76
77 struct 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
88 void dc_uimisc_disconnected(void);
89
90 /* The first command must be the nameless connect command and second
91  * the notification command. */
92 static struct command *commands = NULL;
93 static struct qcmd *queue = NULL, *queueend = NULL;
94 static struct dc_response *respqueue = NULL, *respqueueend = NULL;
95 static int state = -1;
96 static int fd = -1;
97 static iconv_t ichandle;
98 static int resetreader = 1;
99 static struct addrinfo *hostlist = NULL, *curhost = NULL;
100 struct {
101     char *hostname;
102     int family;
103     int sentcreds;
104 } servinfo;
105 char *dc_srv_local;
106
107 static 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
124 static void freeqcmd(struct qcmd *qcmd)
125 {
126     if(qcmd->buf != NULL)
127         free(qcmd->buf);
128     free(qcmd);
129 }
130
131 static 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
143 static 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
179 static 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
217 static 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
229 static 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
258 int dc_init(void)
259 {
260     if((ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
261         return(-1);
262     dc_srv_local = sstrdup("");
263     initcmds();
264     return(0);
265 }
266
267 void dc_cleanup(void)
268 {
269     iconv_close(ichandle);
270 }
271
272 void 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();
285     if(servinfo.hostname != NULL)
286         free(servinfo.hostname);
287     memset(&servinfo, 0, sizeof(servinfo));
288 }
289
290 void 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
304 struct 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
318 struct 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
340 struct 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
361 int 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
373 int dc_getstate(void)
374 {
375     return(state);
376 }
377
378 int dc_queuecmd(int (*callback)(struct dc_response *), void *data, ...)
379 {
380     struct qcmd *qcmd;
381     int num, freepart;
382     va_list al;
383     char *final, *sarg;
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     {
395         if(!wcscmp(part, L"%a"))
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             {
415                 tpart = part + 1;
416                 if(!wcscmp(tpart, L"i"))
417                 {
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)
424                     {
425                         if(buf != NULL)
426                             free(buf);
427                         return(-1);
428                     }
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);
444                 }
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
490 int dc_handleread(void)
491 {
492     int ret, done;
493     char *p1, *p2;
494     size_t len;
495     socklen_t optlen;
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;
505     static int argc = 0;
506     static size_t args = 0;
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:
518         optlen = sizeof(ret);
519         getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
520         if(ret)
521         {
522             int newfd;
523
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);
536                 if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
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         }
551         if(curhost->ai_canonname != NULL)
552             servinfo.hostname = sstrdup(curhost->ai_canonname);
553         servinfo.family = curhost->ai_family;
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
765 #if UNIX_AUTH_STYLE == 1
766 static 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 }
784 #endif
785
786 int dc_handlewrite(void)
787 {
788     int ret;
789     int errnobak;
790     struct msghdr msg;
791     struct iovec bufvec;
792     
793     switch(state)
794     {
795     case 1:
796         if(queue->buflen > 0)
797         {
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;
803 #if UNIX_AUTH_STYLE == 1
804             if((servinfo.family == PF_UNIX) && !servinfo.sentcreds)
805             {
806                 mkcreds(&msg);
807                 servinfo.sentcreds = 1;
808             }
809 #endif
810             ret = sendmsg(fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT);
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
829 /*
830  * It kind of sucks that libresolv doesn't have any DNS parsing
831  * routines. We'll have to do it manually.
832  */
833 static 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
871 static 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
891 static 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 */
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);
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;
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      */
925     flags = (buf[2] << 8) + buf[3];
926     if((flags & 0xfa0f) != 0x8000)
927     {
928         free(name2);
929         return(-1);
930     }
931     /* Skip the query entries */
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         }
941         p += 4; /* Type and class */
942     }
943     /* Parse answers */
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++);
956         p += 4; /* TTL */
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
988 static int getsrvrr(char *name, char **host, int *port)
989 {
990     errno = EOPNOTSUPP;
991     return(-1);
992 }
993 #endif
994
995 static struct addrinfo *gaicat(struct addrinfo *l1, struct addrinfo *l2)
996 {
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. */
1013 static 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
1036 static struct addrinfo *resolvtcp(char *name, int port)
1037 {
1038     struct addrinfo hint, *ret;
1039     char tmp[32];
1040     
1041     memset(&hint, 0, sizeof(hint));
1042     hint.ai_socktype = SOCK_STREAM;
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
1050 static 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
1063 static 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))
1075     {
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);
1085         }
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
1105 static struct addrinfo *getlocalai(void)
1106 {
1107     struct addrinfo *ret;
1108     struct passwd *pwd;
1109     char *tmp;
1110
1111     ret = NULL;
1112     if((getuid() != 0) && ((pwd = getpwuid(getuid())) != NULL))
1113     {
1114         tmp = sprintf2("/tmp/doldacond-%s", pwd->pw_name);
1115         ret = unixgai(SOCK_STREAM, tmp);
1116         free(tmp);
1117     }
1118     ret = gaicat(ret, unixgai(SOCK_STREAM, "/var/run/doldacond.sock"));
1119     return(ret);
1120 }
1121
1122 static 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();
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
1137 static int dc_connectai(struct addrinfo *hosts, struct qcmd **cnctcmd)
1138 {
1139     struct qcmd *qcmd;
1140     int errnobak;
1141     
1142     if(fd >= 0)
1143         dc_disconnect();
1144     state = -1;
1145     if(hostlist != NULL)
1146         freeaddrinfo(hostlist);
1147     hostlist = hosts;
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;
1153             freeaddrinfo(hostlist);
1154             hostlist = NULL;
1155             errno = errnobak;
1156             return(-1);
1157         }
1158         fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
1159         if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
1160         {
1161             if(errno == EINPROGRESS)
1162             {
1163                 state = 0;
1164                 break;
1165             }
1166             close(fd);
1167             fd = -1;
1168         } else {
1169             if(curhost->ai_canonname != NULL)
1170                 servinfo.hostname = sstrdup(curhost->ai_canonname);
1171             servinfo.family = curhost->ai_family;
1172             state = 1;
1173             break;
1174         }
1175     }
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     }
1186     return(fd);
1187 }
1188
1189 static int dc_connect2(char *host, struct qcmd **cnctcmd)
1190 {
1191     struct addrinfo *ai;
1192     struct qcmd *qcmd;
1193     int ret;
1194     
1195     if(host == dc_srv_local)
1196         ai = getlocalai();
1197     else if(!host || !*host)
1198         ai = defaulthost();
1199     else
1200         ai = resolvhost(host);
1201     if(ai == NULL)
1202         return(-1);
1203     ret = dc_connectai(ai, &qcmd);
1204     if((ret >= 0) && (cnctcmd != NULL))
1205         *cnctcmd = qcmd;
1206     return(ret);
1207 }
1208
1209 int dc_connect(char *host)
1210 {
1211     return(dc_connect2(host, NULL));
1212 }
1213
1214 int dc_connectsync(char *host, struct dc_response **respbuf)
1215 {
1216     int ret;
1217     struct qcmd *cc;
1218     struct dc_response *resp;
1219     
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
1234 int 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();
1245         errno = EPROTONOSUPPORT;
1246         return(-1);
1247     }
1248     dc_freeresp(resp);
1249     return(ret);
1250 }
1251
1252 struct 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;
1259     size_t args;
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
1309 void 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
1322 int 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;
1333     high = ires->argv[1].val.num;
1334     dc_freeires(ires);
1335     if((revision < low) || (revision > high))
1336         return(-1);
1337     return(0);
1338 }
1339
1340 const char *dc_gethostname(void)
1341 {
1342     return(servinfo.hostname);
1343 }
1344
1345 int dc_getfd(void)
1346 {
1347     return(fd);
1348 }