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