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