Typo.
[doldaconnect.git] / lib / uilib.c
... / ...
CommitLineData
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
58struct respclass
59{
60 struct respclass *next;
61 int code;
62 int nwords;
63 int *wordt;
64};
65
66struct command
67{
68 struct command *next;
69 wchar_t *name;
70 struct respclass *classes;
71};
72
73struct 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
84void dc_uimisc_disconnected(void);
85
86/* The first command must be the nameless connect command and second
87 * the notification command. */
88static struct command *commands = NULL;
89static struct qcmd *queue = NULL, *queueend = NULL;
90static struct dc_response *respqueue = NULL, *respqueueend = NULL;
91static int state = -1;
92static int fd = -1;
93static iconv_t ichandle;
94static int resetreader = 1;
95static char *dchostname = NULL;
96static struct addrinfo *hostlist = NULL, *curhost = NULL;
97static int servport;
98
99static 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
116static void freeqcmd(struct qcmd *qcmd)
117{
118 if(qcmd->buf != NULL)
119 free(qcmd->buf);
120 free(qcmd);
121}
122
123static 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
135static 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
171static 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
209static 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
221static 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
250int 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
258void dc_cleanup(void)
259{
260 iconv_close(ichandle);
261}
262
263void 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
281void 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
295struct 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
309struct 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
331struct 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
352int 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
364int dc_getstate(void)
365{
366 return(state);
367}
368
369int 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
470int 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
758int 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
787static 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
825static 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
845static 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
938static int getsrvrr(char *name, char **host, int *port)
939{
940 errno = EOPNOTSUPP;
941 return(-1);
942}
943#endif
944
945int 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
1037struct 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
1094void 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
1107const char *dc_gethostname(void)
1108{
1109 return(dchostname);
1110}