Fix dc_queuecmd portability (and changed syntax!).
[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
106 static 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
123 static void freeqcmd(struct qcmd *qcmd)
124 {
125     if(qcmd->buf != NULL)
126         free(qcmd->buf);
127     free(qcmd);
128 }
129
130 static 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
142 static 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
178 static 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
216 static 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
228 static 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
257 int 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
265 void dc_cleanup(void)
266 {
267     iconv_close(ichandle);
268 }
269
270 void 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();
283     if(servinfo.hostname != NULL)
284         free(servinfo.hostname);
285     memset(&servinfo, 0, sizeof(servinfo));
286 }
287
288 void 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
302 struct 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
316 struct 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
338 struct 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
359 int 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
371 int dc_getstate(void)
372 {
373     return(state);
374 }
375
376 int dc_queuecmd(int (*callback)(struct dc_response *), void *data, ...)
377 {
378     struct qcmd *qcmd;
379     int num, freepart;
380     va_list al;
381     char *final, *sarg;
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     {
393         if(!wcscmp(part, L"%a"))
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             {
413                 tpart = part + 1;
414                 if(!wcscmp(tpart, L"i"))
415                 {
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)
422                     {
423                         if(buf != NULL)
424                             free(buf);
425                         return(-1);
426                     }
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);
442                 }
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
488 int dc_handleread(void)
489 {
490     int ret, done;
491     char *p1, *p2;
492     size_t len;
493     socklen_t optlen;
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;
503     static int argc = 0;
504     static size_t args = 0;
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:
516         optlen = sizeof(ret);
517         getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
518         if(ret)
519         {
520             int newfd;
521
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);
534                 if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
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         }
549         if(curhost->ai_canonname != NULL)
550             servinfo.hostname = sstrdup(curhost->ai_canonname);
551         servinfo.family = curhost->ai_family;
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
763 static 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
782 int dc_handlewrite(void)
783 {
784     int ret;
785     int errnobak;
786     struct msghdr msg;
787     struct iovec bufvec;
788     
789     switch(state)
790     {
791     case 1:
792         if(queue->buflen > 0)
793         {
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);
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
823 /*
824  * It kind of sucks that libresolv doesn't have any DNS parsing
825  * routines. We'll have to do it manually.
826  */
827 static 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
865 static 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
885 static 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 */
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);
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;
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      */
919     flags = (buf[2] << 8) + buf[3];
920     if((flags & 0xfa0f) != 0x8000)
921     {
922         free(name2);
923         return(-1);
924     }
925     /* Skip the query entries */
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         }
935         p += 4; /* Type and class */
936     }
937     /* Parse answers */
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++);
950         p += 4; /* TTL */
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
982 static int getsrvrr(char *name, char **host, int *port)
983 {
984     errno = EOPNOTSUPP;
985     return(-1);
986 }
987 #endif
988
989 static struct addrinfo *gaicat(struct addrinfo *l1, struct addrinfo *l2)
990 {
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. */
1007 static 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
1030 static struct addrinfo *resolvtcp(char *name, int port)
1031 {
1032     struct addrinfo hint, *ret;
1033     char tmp[32];
1034     
1035     memset(&hint, 0, sizeof(hint));
1036     hint.ai_socktype = SOCK_STREAM;
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
1044 static 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
1057 static 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))
1069     {
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);
1079         }
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
1099 static 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);
1114     }
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
1122 int dc_connect(char *host)
1123 {
1124     struct qcmd *qcmd;
1125     int errnobak;
1126     
1127     if(fd >= 0)
1128         dc_disconnect();
1129     state = -1;
1130     if(hostlist != NULL)
1131         freeaddrinfo(hostlist);
1132     if(!host || !*host)
1133         hostlist = defaulthost();
1134     else
1135         hostlist = resolvhost(host);
1136     if(hostlist == NULL)
1137         return(-1);
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;
1143             errno = errnobak;
1144             return(-1);
1145         }
1146         fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
1147         if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
1148         {
1149             if(errno == EINPROGRESS)
1150             {
1151                 state = 0;
1152                 break;
1153             }
1154             close(fd);
1155             fd = -1;
1156         } else {
1157             if(curhost->ai_canonname != NULL)
1158                 servinfo.hostname = sstrdup(curhost->ai_canonname);
1159             servinfo.family = curhost->ai_family;
1160             state = 1;
1161             break;
1162         }
1163     }
1164     qcmd = makeqcmd(NULL);
1165     resetreader = 1;
1166     return(fd);
1167 }
1168
1169 struct 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;
1176     size_t args;
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
1226 void 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
1239 int 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
1257 const char *dc_gethostname(void)
1258 {
1259     return(servinfo.hostname);
1260 }