2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
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.
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.
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
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.
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
45 #include <arpa/nameser.h>
49 #include <doldaconnect/uilib.h>
52 #define DOLCON_SRV_NAME "_dolcon._tcp"
62 struct respclass *next;
72 struct respclass *classes;
82 int (*callback)(struct dc_response *resp);
86 void dc_uimisc_disconnected(void);
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;
95 static iconv_t ichandle;
96 static int resetreader = 1;
97 static char *dchostname = NULL;
98 static struct addrinfo *hostlist = NULL, *curhost = NULL;
101 static struct dc_response *makeresp(void)
103 struct dc_response *new;
105 new = smalloc(sizeof(*new));
118 static void freeqcmd(struct qcmd *qcmd)
120 if(qcmd->buf != NULL)
125 static void unlinkqueue(void)
129 if((qcmd = queue) == NULL)
137 static struct qcmd *makeqcmd(wchar_t *name)
147 for(cmd = commands; cmd != NULL; cmd = cmd->next)
149 if((cmd->name != NULL) && !wcscmp(cmd->name, name))
155 new = smalloc(sizeof(*new));
160 new->callback = NULL;
165 queue = queueend = new;
167 queueend->next = new;
173 static wchar_t *quoteword(wchar_t *word)
175 wchar_t *wp, *buf, *bp;
185 for(wp = word; *wp != L'\0'; wp++)
187 if(!dq && iswspace(*wp))
189 if((*wp == L'\\') || (*wp == L'\"'))
196 bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
199 for(wp = word; *wp != L'\0'; wp++)
201 if((*wp == L'\\') || (*wp == L'\"'))
211 static struct command *makecmd(wchar_t *name)
215 new = smalloc(sizeof(*new));
218 new->next = commands;
223 static struct respclass *addresp(struct command *cmd, int code, ...)
225 struct respclass *new;
230 va_start(args, code);
232 while((resps[i++] = va_arg(args, int)) != RESP_END);
235 new = smalloc(sizeof(*new));
240 new->wordt = smalloc(sizeof(int) * i);
241 memcpy(new->wordt, resps, sizeof(int) * i);
245 new->next = cmd->classes;
250 #include "initcmds.h"
254 if((ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
260 void dc_cleanup(void)
262 iconv_close(ichandle);
265 void dc_disconnect(void)
267 struct dc_response *resp;
275 while((resp = dc_getresp()) != NULL)
277 dc_uimisc_disconnected();
278 if(dchostname != NULL)
283 void dc_freeresp(struct dc_response *resp)
287 for(i = 0; i < resp->numlines; i++)
289 for(o = 0; o < resp->rlines[i].argc; o++)
290 free(resp->rlines[i].argv[o]);
291 free(resp->rlines[i].argv);
297 struct dc_response *dc_getresp(void)
299 struct dc_response *ret;
301 if((ret = respqueue) == NULL)
303 respqueue = ret->next;
304 if(respqueue == NULL)
307 respqueue->prev = NULL;
311 struct dc_response *dc_gettaggedresp(int tag)
313 struct dc_response *resp;
315 for(resp = respqueue; resp != NULL; resp = resp->next)
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;
333 struct dc_response *dc_gettaggedrespsync(int tag)
336 struct dc_response *resp;
338 while((resp = dc_gettaggedresp(tag)) == NULL)
343 pfd.events |= POLLOUT;
344 if(poll(&pfd, 1, -1) < 0)
346 if((pfd.revents & POLLIN) && dc_handleread())
348 if((pfd.revents & POLLOUT) && dc_handlewrite())
354 int dc_wantwrite(void)
359 if((queue != NULL) && (queue->buflen > 0))
366 int dc_getstate(void)
371 int dc_queuecmd(int (*callback)(struct dc_response *), void *data, ...)
379 wchar_t *part, *tpart;
380 size_t bufsize, bufdata;
383 bufsize = bufdata = 0;
386 while((part = va_arg(al, wchar_t *)) != NULL)
388 if(!wcscmp(part, L"%%a"))
390 for(toks = va_arg(al, wchar_t **); *toks != NULL; toks++)
394 if((tpart = quoteword(part)) != NULL)
400 bufcat(buf, part, wcslen(part));
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
415 part = vswprintf2(tpart = (part + 1), al);
416 for(; *tpart != L'\0'; tpart++)
430 if((tpart = quoteword(part)) != NULL)
441 if((qcmd = makeqcmd(part)) == NULL)
449 qcmd->callback = callback;
453 bufcat(buf, part, wcslen(part));
459 bufcat(buf, L"\r\n\0", 3);
460 if((final = icwcstombs(buf, "utf-8")) == NULL)
468 qcmd->buflen = strlen(final);
472 int dc_handleread(void)
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;
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;
493 static int unlink = 0;
500 optlen = sizeof(ret);
501 getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
505 struct sockaddr_storage addr;
506 struct sockaddr_in *ipv4;
507 struct sockaddr_in6 *ipv6;
509 for(curhost = curhost->ai_next; curhost != NULL; curhost = curhost->ai_next)
511 if((newfd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
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)
524 ipv4 = (struct sockaddr_in *)&addr;
525 ipv4->sin_port = htons(servport);
528 if(addr.ss_family == AF_INET6)
530 ipv6 = (struct sockaddr_in6 *)&addr;
531 ipv6->sin6_port = htons(servport);
534 if(connect(fd, (struct sockaddr *)&addr, curhost->ai_addrlen))
536 if(errno == EINPROGRESS)
562 dc_freeresp(curresp);
567 ret = read(fd, inbuf + inbufdata, 128 - inbufdata);
570 if((errno == EAGAIN) || (errno == EINTR))
576 } else if(ret == 0) {
585 if(cbufsize == cbufdata)
589 if((cbuf = realloc(cbuf, sizeof(wchar_t) * (cbufsize += 256))) == NULL)
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));
609 /* XXX Is this really OK? */
631 while(!done && (pptr - cbuf < cbufdata))
640 if(pptr == cbuf + cbufdata - 1)
645 if(*(++pptr) == L'\n')
649 curresp = makeresp();
650 if((argc > 0) && ((curresp->code = wcstol(argv[0], NULL, 10)) >= 600))
652 curresp->cmdname = L".notify";
653 curresp->internal = commands->next;
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);
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;
673 if((curresp->code >= 600) || (queue == NULL) || (queue->callback == NULL))
676 ret = queue->callback(curresp);
679 if(respqueue == NULL)
681 respqueue = respqueueend = curresp;
683 curresp->next = NULL;
684 curresp->prev = respqueueend;
685 respqueueend->next = curresp;
686 respqueueend = curresp;
688 } else if(ret == 1) {
689 dc_freeresp(curresp);
695 wmemmove(cbuf, pptr, cbufdata -= (pptr - cbuf));
709 if(iswspace(*pptr) || ((argc == 0) && (*pptr == L'-')))
722 sizebuf(&argv, &args, argc + 1, sizeof(*argv), 1);
727 } else if(*pptr == L'\"') {
730 } else if(*pptr == L'\\') {
731 if(pptr == cbuf + cbufdata - 1)
736 addtobuf(cw, *(++pptr));
739 addtobuf(cw, *(pptr++));
746 } else if(*pptr == L'\\') {
747 addtobuf(cw, *(++pptr));
760 int dc_handlewrite(void)
768 if(queue->buflen > 0)
770 ret = send(fd, queue->buf, queue->buflen, MSG_NOSIGNAL | MSG_DONTWAIT);
773 if((errno == EAGAIN) || (errno == EINTR))
781 memmove(queue->buf, queue->buf + ret, queue->buflen -= ret);
790 * It kind of sucks that libresolv doesn't have any DNS parsing
791 * routines. We'll have to do it manually.
793 static char *readname(unsigned char *msg, unsigned char *eom, unsigned char **p)
797 size_t namesize, namedata, len;
800 namesize = namedata = 0;
808 } else if(len == 0xc0) {
809 tp = msg + *((*p)++);
810 if((tname = readname(msg, eom, &tp)) == NULL)
816 bufcat(name, tname, strlen(tname));
820 } else if(*p + len >= eom) {
825 bufcat(name, *p, len);
831 static int skipname(unsigned char *msg, unsigned char *eom, unsigned char **p)
841 } else if(len == 0xc0) {
844 } else if(*p + len >= eom) {
851 static int getsrvrr(char *name, char **host, int *port)
854 char *name2, *rrname;
855 unsigned char *eom, *p;
856 unsigned char buf[1024];
857 int flags, num, class, type;
861 if(!(_res.options & RES_INIT))
866 /* res_querydomain doesn't work for some reason */
867 if(name[strlen(name) - 1] == '.')
868 name2 = sprintf2("%s.%s", DOLCON_SRV_NAME, name);
870 name2 = sprintf2("%s.%s.", DOLCON_SRV_NAME, name);
871 ret = res_query(name2, C_IN, T_SRV, buf, sizeof(buf));
879 * Assume transaction ID is correct.
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.
885 flags = (buf[2] << 8) + buf[3];
886 if((flags & 0xfa0f) != 0x8000)
891 /* Skip the query entries */
892 num = (buf[4] << 8) + buf[5];
894 for(i = 0; i < num; i++)
896 if(skipname(buf, eom, &p))
901 p += 4; /* Type and class */
904 num = (buf[6] << 8) + buf[7];
905 for(i = 0; i < num; i++)
907 if((rrname = readname(buf, eom, &p)) == NULL)
919 if((class == C_IN) && (type == T_SRV) && !strcmp(rrname, name2))
923 /* Noone will want to have alternative DC servers, so
924 * don't care about priority and weigth */
935 if((rrname = readname(buf, eom, &p)) == NULL)
948 static int getsrvrr(char *name, char **host, int *port)
955 int dc_connect(char *host, int port)
957 struct addrinfo hint;
958 struct sockaddr_storage addr;
959 struct sockaddr_in *ipv4;
961 struct sockaddr_in6 *ipv6;
965 int getsrv, freehost;
979 memset(&hint, 0, sizeof(hint));
980 hint.ai_socktype = SOCK_STREAM;
983 if(!getsrvrr(host, &newhost, &port))
991 freeaddrinfo(hostlist);
992 if(getaddrinfo(host, NULL, &hint, &hostlist))
999 for(curhost = hostlist; curhost != NULL; curhost = curhost->ai_next)
1001 if((fd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
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)
1013 ipv4 = (struct sockaddr_in *)&addr;
1014 ipv4->sin_port = htons(port);
1017 if(addr.ss_family == AF_INET6)
1019 ipv6 = (struct sockaddr_in6 *)&addr;
1020 ipv6->sin6_port = htons(port);
1023 if(connect(fd, (struct sockaddr *)&addr, curhost->ai_addrlen))
1025 if(errno == EINPROGRESS)
1037 qcmd = makeqcmd(NULL);
1039 if(dchostname != NULL)
1041 dchostname = sstrdup(host);
1047 struct dc_intresp *dc_interpret(struct dc_response *resp)
1050 struct dc_intresp *iresp;
1051 struct command *cmd;
1052 struct respclass *cls;
1056 if((resp->numlines == 0) || (resp->rlines[0].argc == 0) || (resp->curline >= resp->numlines))
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)
1062 if(cls->code == code)
1067 if(cls->nwords >= resp->rlines[resp->curline].argc)
1069 iresp = smalloc(sizeof(*iresp));
1074 for(i = 0; i < cls->nwords; i++)
1076 switch(cls->wordt[i])
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];
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];
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];
1104 void dc_freeires(struct dc_intresp *ires)
1108 for(i = 0; i < ires->argc; i++)
1110 if(ires->argv[i].type == RESP_STR)
1111 free(ires->argv[i].val.str);
1117 const char *dc_gethostname(void)