Nicify SRV name generation.
[doldaconnect.git] / lib / uilib.c
CommitLineData
d3372da9 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>
31c700d1 50#include <utils.h>
d3372da9 51
4d487bad 52#define DOLCON_SRV_NAME "_dolcon._tcp"
53
d3372da9 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
60struct respclass
61{
62 struct respclass *next;
63 int code;
64 int nwords;
65 int *wordt;
66};
67
68struct command
69{
70 struct command *next;
71 wchar_t *name;
72 struct respclass *classes;
73};
74
75struct 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
86void dc_uimisc_disconnected(void);
87
88/* The first command must be the nameless connect command and second
89 * the notification command. */
90static struct command *commands = NULL;
91static struct qcmd *queue = NULL, *queueend = NULL;
92static struct dc_response *respqueue = NULL, *respqueueend = NULL;
93static int state = -1;
94static int fd = -1;
95static iconv_t ichandle;
96static int resetreader = 1;
97static char *dchostname = NULL;
98static struct addrinfo *hostlist = NULL, *curhost = NULL;
99static int servport;
100
101static 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
118static void freeqcmd(struct qcmd *qcmd)
119{
120 if(qcmd->buf != NULL)
121 free(qcmd->buf);
122 free(qcmd);
123}
124
125static 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
137static 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
173static 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
211static 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
223static 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
252int 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
260void dc_cleanup(void)
261{
262 iconv_close(ichandle);
263}
264
265void 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
283void 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
297struct 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
311struct 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
333struct 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
354int 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
366int dc_getstate(void)
367{
368 return(state);
369}
370
371int 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
472int dc_handleread(void)
473{
474 int ret, done;
475 char *p1, *p2;
476 size_t len;
26d72b0d 477 socklen_t optlen;
d3372da9 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;
26d72b0d 487 static int argc = 0;
488 static size_t args = 0;
d3372da9 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:
26d72b0d 500 optlen = sizeof(ret);
501 getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
d3372da9 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
760int 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
789static char *readname(unsigned char *msg, unsigned char *eom, unsigned char **p)
790{
791 char *name, *tname;
792 unsigned char *tp;
793 size_t namesize, namedata, len;
794
795 name = NULL;
796 namesize = namedata = 0;
797 while(1)
798 {
799 len = *((*p)++);
800 if(len == 0)
801 {
802 addtobuf(name, 0);
803 return(name);
804 } else if(len == 0xc0) {
805 tp = msg + *((*p)++);
806 if((tname = readname(msg, eom, &tp)) == NULL)
807 {
808 if(name != NULL)
809 free(name);
810 return(NULL);
811 }
812 bufcat(name, tname, strlen(tname));
813 addtobuf(name, 0);
814 free(tname);
815 return(name);
816 } else if(*p + len >= eom) {
817 if(name != NULL)
818 free(name);
819 return(NULL);
820 }
821 bufcat(name, *p, len);
822 *p += len;
823 addtobuf(name, '.');
824 }
825}
826
827static int skipname(unsigned char *msg, unsigned char *eom, unsigned char **p)
828{
829 size_t len;
830
831 while(1)
832 {
833 len = *((*p)++);
834 if(len == 0)
835 {
836 return(0);
837 } else if(len == 0xc0) {
838 (*p)++;
839 return(0);
840 } else if(*p + len >= eom) {
841 return(-1);
842 }
843 *p += len;
844 }
845}
846
847static int getsrvrr(char *name, char **host, int *port)
848{
849 int i;
850 char *name2, *rrname;
851 unsigned char *eom, *p;
852 unsigned char buf[1024];
853 int flags, num, class, type;
854 size_t len;
855 int ret;
856
857 if(!(_res.options & RES_INIT))
858 {
859 if(res_init() < 0)
860 return(-1);
861 }
862 /* res_querydomain doesn't work for some reason */
4d487bad 863 if(name[strlen(name) - 1] == '.')
864 name2 = sprintf2("%s.%s", DOLCON_SRV_NAME, name);
865 else
866 name2 = sprintf2("%s.%s.", DOLCON_SRV_NAME, name);
d3372da9 867 ret = res_query(name2, C_IN, T_SRV, buf, sizeof(buf));
868 if(ret < 0)
869 {
870 free(name2);
871 return(-1);
872 }
873 eom = buf + ret;
874 flags = (buf[2] << 8) + buf[3];
875 if((flags & 0xfa0f) != 0x8000)
876 {
877 free(name2);
878 return(-1);
879 }
880 num = (buf[4] << 8) + buf[5];
881 p = buf + 12;
882 for(i = 0; i < num; i++)
883 {
884 if(skipname(buf, eom, &p))
885 {
886 free(name2);
887 return(-1);
888 }
889 p += 4;
890 }
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;
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
935static int getsrvrr(char *name, char **host, int *port)
936{
c4c34936 937 errno = EOPNOTSUPP;
d3372da9 938 return(-1);
939}
940#endif
941
942int dc_connect(char *host, int port)
943{
944 struct addrinfo hint;
945 struct sockaddr_storage addr;
946 struct sockaddr_in *ipv4;
947#ifdef HAVE_IPV6
948 struct sockaddr_in6 *ipv6;
949#endif
950 struct qcmd *qcmd;
951 char *newhost;
952 int getsrv, freehost;
953 int errnobak;
954
955 if(fd >= 0)
956 dc_disconnect();
957 state = -1;
958 freehost = 0;
959 if(port < 0)
960 {
961 port = 1500;
962 getsrv = 1;
963 } else {
964 getsrv = 0;
965 }
966 memset(&hint, 0, sizeof(hint));
967 hint.ai_socktype = SOCK_STREAM;
968 if(getsrv)
969 {
970 if(!getsrvrr(host, &newhost, &port))
971 {
972 host = newhost;
973 freehost = 1;
974 }
975 }
976 servport = port;
977 if(hostlist != NULL)
978 freeaddrinfo(hostlist);
979 if(getaddrinfo(host, NULL, &hint, &hostlist))
980 {
981 errno = ENONET;
982 if(freehost)
983 free(host);
984 return(-1);
985 }
986 for(curhost = hostlist; curhost != NULL; curhost = curhost->ai_next)
987 {
988 if((fd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
989 {
990 errnobak = errno;
991 if(freehost)
992 free(host);
993 errno = errnobak;
994 return(-1);
995 }
996 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
997 memcpy(&addr, curhost->ai_addr, curhost->ai_addrlen);
998 if(addr.ss_family == AF_INET)
999 {
1000 ipv4 = (struct sockaddr_in *)&addr;
1001 ipv4->sin_port = htons(port);
1002 }
1003#ifdef HAVE_IPV6
1004 if(addr.ss_family == AF_INET6)
1005 {
1006 ipv6 = (struct sockaddr_in6 *)&addr;
1007 ipv6->sin6_port = htons(port);
1008 }
1009#endif
1010 if(connect(fd, (struct sockaddr *)&addr, curhost->ai_addrlen))
1011 {
1012 if(errno == EINPROGRESS)
1013 {
1014 state = 0;
1015 break;
1016 }
1017 close(fd);
1018 fd = -1;
1019 } else {
1020 state = 1;
1021 break;
1022 }
1023 }
1024 qcmd = makeqcmd(NULL);
1025 resetreader = 1;
1026 if(dchostname != NULL)
1027 free(dchostname);
1028 dchostname = sstrdup(host);
1029 if(freehost)
1030 free(host);
1031 return(fd);
1032}
1033
1034struct dc_intresp *dc_interpret(struct dc_response *resp)
1035{
1036 int i;
1037 struct dc_intresp *iresp;
1038 struct command *cmd;
1039 struct respclass *cls;
1040 int code;
26d72b0d 1041 size_t args;
d3372da9 1042
1043 if((resp->numlines == 0) || (resp->rlines[0].argc == 0) || (resp->curline >= resp->numlines))
1044 return(NULL);
1045 code = wcstol(resp->rlines[0].argv[0], NULL, 10);
1046 cmd = (struct command *)(resp->internal);
1047 for(cls = cmd->classes; cls != NULL; cls = cls->next)
1048 {
1049 if(cls->code == code)
1050 break;
1051 }
1052 if(cls == NULL)
1053 return(NULL);
1054 if(cls->nwords >= resp->rlines[resp->curline].argc)
1055 return(NULL);
1056 iresp = smalloc(sizeof(*iresp));
1057 iresp->code = code;
1058 iresp->argv = NULL;
1059 iresp->argc = 0;
1060 args = 0;
1061 for(i = 0; i < cls->nwords; i++)
1062 {
1063 switch(cls->wordt[i])
1064 {
1065 case RESP_DSC:
1066 break;
1067 case RESP_STR:
1068 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1069 iresp->argv[iresp->argc].val.str = swcsdup(resp->rlines[resp->curline].argv[i + 1]);
1070 iresp->argv[iresp->argc].type = cls->wordt[i];
1071 iresp->argc++;
1072 break;
1073 case RESP_INT:
1074 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1075 iresp->argv[iresp->argc].val.num = wcstol(resp->rlines[resp->curline].argv[i + 1], NULL, 0);
1076 iresp->argv[iresp->argc].type = cls->wordt[i];
1077 iresp->argc++;
1078 break;
1079 case RESP_FLOAT:
1080 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1081 iresp->argv[iresp->argc].val.flnum = wcstod(resp->rlines[resp->curline].argv[i + 1], NULL);
1082 iresp->argv[iresp->argc].type = cls->wordt[i];
1083 iresp->argc++;
1084 break;
1085 }
1086 }
1087 resp->curline++;
1088 return(iresp);
1089}
1090
1091void dc_freeires(struct dc_intresp *ires)
1092{
1093 int i;
1094
1095 for(i = 0; i < ires->argc; i++)
1096 {
1097 if(ires->argv[i].type == RESP_STR)
1098 free(ires->argv[i].val.str);
1099 }
1100 free(ires->argv);
1101 free(ires);
1102}
1103
1104const char *dc_gethostname(void)
1105{
1106 return(dchostname);
1107}