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