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>
47 #include <arpa/nameser.h>
51 #include <doldaconnect/uilib.h>
54 #define DOLCON_SRV_NAME "_dolcon._tcp"
64 struct respclass *next;
74 struct respclass *classes;
84 int (*callback)(struct dc_response *resp);
88 void dc_uimisc_disconnected(void);
90 /* The first command must be the nameless connect command and second
91 * the notification command. */
92 static struct command *commands = NULL;
93 static struct qcmd *queue = NULL, *queueend = NULL;
94 static struct dc_response *respqueue = NULL, *respqueueend = NULL;
95 static int state = -1;
97 static iconv_t ichandle;
98 static int resetreader = 1;
99 static char *dchostname = NULL;
100 static struct addrinfo *hostlist = NULL, *curhost = NULL;
102 static struct dc_response *makeresp(void)
104 struct dc_response *new;
106 new = smalloc(sizeof(*new));
119 static void freeqcmd(struct qcmd *qcmd)
121 if(qcmd->buf != NULL)
126 static void unlinkqueue(void)
130 if((qcmd = queue) == NULL)
138 static struct qcmd *makeqcmd(wchar_t *name)
148 for(cmd = commands; cmd != NULL; cmd = cmd->next)
150 if((cmd->name != NULL) && !wcscmp(cmd->name, name))
156 new = smalloc(sizeof(*new));
161 new->callback = NULL;
166 queue = queueend = new;
168 queueend->next = new;
174 static wchar_t *quoteword(wchar_t *word)
176 wchar_t *wp, *buf, *bp;
186 for(wp = word; *wp != L'\0'; wp++)
188 if(!dq && iswspace(*wp))
190 if((*wp == L'\\') || (*wp == L'\"'))
197 bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
200 for(wp = word; *wp != L'\0'; wp++)
202 if((*wp == L'\\') || (*wp == L'\"'))
212 static struct command *makecmd(wchar_t *name)
216 new = smalloc(sizeof(*new));
219 new->next = commands;
224 static struct respclass *addresp(struct command *cmd, int code, ...)
226 struct respclass *new;
231 va_start(args, code);
233 while((resps[i++] = va_arg(args, int)) != RESP_END);
236 new = smalloc(sizeof(*new));
241 new->wordt = smalloc(sizeof(int) * i);
242 memcpy(new->wordt, resps, sizeof(int) * i);
246 new->next = cmd->classes;
251 #include "initcmds.h"
255 if((ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
261 void dc_cleanup(void)
263 iconv_close(ichandle);
266 void dc_disconnect(void)
268 struct dc_response *resp;
276 while((resp = dc_getresp()) != NULL)
278 dc_uimisc_disconnected();
279 if(dchostname != NULL)
284 void dc_freeresp(struct dc_response *resp)
288 for(i = 0; i < resp->numlines; i++)
290 for(o = 0; o < resp->rlines[i].argc; o++)
291 free(resp->rlines[i].argv[o]);
292 free(resp->rlines[i].argv);
298 struct dc_response *dc_getresp(void)
300 struct dc_response *ret;
302 if((ret = respqueue) == NULL)
304 respqueue = ret->next;
305 if(respqueue == NULL)
308 respqueue->prev = NULL;
312 struct dc_response *dc_gettaggedresp(int tag)
314 struct dc_response *resp;
316 for(resp = respqueue; resp != NULL; resp = resp->next)
320 if(resp->prev != NULL)
321 resp->prev->next = resp->next;
322 if(resp->next != NULL)
323 resp->next->prev = resp->prev;
324 if(resp == respqueue)
325 respqueue = resp->next;
326 if(resp == respqueueend)
327 respqueueend = resp->prev;
334 struct dc_response *dc_gettaggedrespsync(int tag)
337 struct dc_response *resp;
339 while((resp = dc_gettaggedresp(tag)) == NULL)
344 pfd.events |= POLLOUT;
345 if(poll(&pfd, 1, -1) < 0)
347 if((pfd.revents & POLLIN) && dc_handleread())
349 if((pfd.revents & POLLOUT) && dc_handlewrite())
355 int dc_wantwrite(void)
360 if((queue != NULL) && (queue->buflen > 0))
367 int dc_getstate(void)
372 int dc_queuecmd(int (*callback)(struct dc_response *), void *data, ...)
380 wchar_t *part, *tpart;
381 size_t bufsize, bufdata;
384 bufsize = bufdata = 0;
387 while((part = va_arg(al, wchar_t *)) != NULL)
389 if(!wcscmp(part, L"%%a"))
391 for(toks = va_arg(al, wchar_t **); *toks != NULL; toks++)
395 if((tpart = quoteword(part)) != NULL)
401 bufcat(buf, part, wcslen(part));
409 /* This demands that all arguments that are passed to the
410 * function are of equal length, that of an int. I know
411 * that GCC does that on IA32 platforms, but I do not know
412 * which other platforms and compilers that it applies
413 * to. If this breaks your platform, please mail me about
416 part = vswprintf2(tpart = (part + 1), al);
417 for(; *tpart != L'\0'; tpart++)
431 if((tpart = quoteword(part)) != NULL)
442 if((qcmd = makeqcmd(part)) == NULL)
450 qcmd->callback = callback;
454 bufcat(buf, part, wcslen(part));
460 bufcat(buf, L"\r\n\0", 3);
461 if((final = icwcstombs(buf, "utf-8")) == NULL)
469 qcmd->buflen = strlen(final);
473 int dc_handleread(void)
480 /* Ewww... this really is soo ugly. I need to clean this up some day. */
481 static int pstate = 0;
482 static char inbuf[128];
483 static size_t inbufdata = 0;
484 static wchar_t *cbuf = NULL;
485 static size_t cbufsize = 0, cbufdata = 0;
486 static wchar_t *pptr = NULL;
487 static wchar_t **argv = NULL;
489 static size_t args = 0;
490 static wchar_t *cw = NULL;
491 static size_t cwsize = 0, cwdata = 0;
492 static struct dc_response *curresp = NULL;
494 static int unlink = 0;
501 optlen = sizeof(ret);
502 getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
507 for(curhost = curhost->ai_next; curhost != NULL; curhost = curhost->ai_next)
509 if((newfd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
518 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
519 if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
521 if(errno == EINPROGRESS)
534 if(curhost->ai_canonname != NULL)
535 dchostname = sstrdup(curhost->ai_canonname);
549 dc_freeresp(curresp);
554 ret = read(fd, inbuf + inbufdata, 128 - inbufdata);
557 if((errno == EAGAIN) || (errno == EINTR))
563 } else if(ret == 0) {
572 if(cbufsize == cbufdata)
576 if((cbuf = realloc(cbuf, sizeof(wchar_t) * (cbufsize += 256))) == NULL)
586 p2 = (char *)(cbuf + cbufdata);
587 len = sizeof(wchar_t) * (cbufsize - cbufdata);
588 ret = iconv(ichandle, &p1, &inbufdata, &p2, &len);
589 memmove(inbuf, p1, inbufdata);
590 cbufdata = cbufsize - (len / sizeof(wchar_t));
596 /* XXX Is this really OK? */
618 while(!done && (pptr - cbuf < cbufdata))
627 if(pptr == cbuf + cbufdata - 1)
632 if(*(++pptr) == L'\n')
636 curresp = makeresp();
637 if((argc > 0) && ((curresp->code = wcstol(argv[0], NULL, 10)) >= 600))
639 curresp->cmdname = L".notify";
640 curresp->internal = commands->next;
644 if((curresp->cmdname = queue->cmd->name) == NULL)
645 curresp->cmdname = L".connect";
646 curresp->data = queue->data;
647 curresp->tag = queue->tag;
648 curresp->internal = (void *)(queue->cmd);
652 sizebuf(&curresp->rlines, &curresp->linessize, curresp->numlines + 1, sizeof(*(curresp->rlines)), 1);
653 curresp->rlines[curresp->numlines].argc = argc;
654 curresp->rlines[curresp->numlines].argv = argv;
660 if((curresp->code >= 600) || (queue == NULL) || (queue->callback == NULL))
663 ret = queue->callback(curresp);
666 if(respqueue == NULL)
668 respqueue = respqueueend = curresp;
670 curresp->next = NULL;
671 curresp->prev = respqueueend;
672 respqueueend->next = curresp;
673 respqueueend = curresp;
675 } else if(ret == 1) {
676 dc_freeresp(curresp);
682 wmemmove(cbuf, pptr, cbufdata -= (pptr - cbuf));
696 if(iswspace(*pptr) || ((argc == 0) && (*pptr == L'-')))
709 sizebuf(&argv, &args, argc + 1, sizeof(*argv), 1);
714 } else if(*pptr == L'\"') {
717 } else if(*pptr == L'\\') {
718 if(pptr == cbuf + cbufdata - 1)
723 addtobuf(cw, *(++pptr));
726 addtobuf(cw, *(pptr++));
733 } else if(*pptr == L'\\') {
734 addtobuf(cw, *(++pptr));
747 int dc_handlewrite(void)
755 if(queue->buflen > 0)
757 ret = send(fd, queue->buf, queue->buflen, MSG_NOSIGNAL | MSG_DONTWAIT);
760 if((errno == EAGAIN) || (errno == EINTR))
768 memmove(queue->buf, queue->buf + ret, queue->buflen -= ret);
777 * It kind of sucks that libresolv doesn't have any DNS parsing
778 * routines. We'll have to do it manually.
780 static char *readname(unsigned char *msg, unsigned char *eom, unsigned char **p)
784 size_t namesize, namedata, len;
787 namesize = namedata = 0;
795 } else if(len == 0xc0) {
796 tp = msg + *((*p)++);
797 if((tname = readname(msg, eom, &tp)) == NULL)
803 bufcat(name, tname, strlen(tname));
807 } else if(*p + len >= eom) {
812 bufcat(name, *p, len);
818 static int skipname(unsigned char *msg, unsigned char *eom, unsigned char **p)
828 } else if(len == 0xc0) {
831 } else if(*p + len >= eom) {
838 static int getsrvrr(char *name, char **host, int *port)
841 char *name2, *rrname;
842 unsigned char *eom, *p;
843 unsigned char buf[1024];
844 int flags, num, class, type;
848 if(!(_res.options & RES_INIT))
853 /* res_querydomain doesn't work for some reason */
854 if(name[strlen(name) - 1] == '.')
855 name2 = sprintf2("%s.%s", DOLCON_SRV_NAME, name);
857 name2 = sprintf2("%s.%s.", DOLCON_SRV_NAME, name);
858 ret = res_query(name2, C_IN, T_SRV, buf, sizeof(buf));
866 * Assume transaction ID is correct.
868 * Flags check: FA0F masks in request/response flag, opcode,
869 * truncated flag and status code, and ignores authoritativeness,
870 * recursion flags and DNSSEC and reserved bits.
872 flags = (buf[2] << 8) + buf[3];
873 if((flags & 0xfa0f) != 0x8000)
878 /* Skip the query entries */
879 num = (buf[4] << 8) + buf[5];
881 for(i = 0; i < num; i++)
883 if(skipname(buf, eom, &p))
888 p += 4; /* Type and class */
891 num = (buf[6] << 8) + buf[7];
892 for(i = 0; i < num; i++)
894 if((rrname = readname(buf, eom, &p)) == NULL)
906 if((class == C_IN) && (type == T_SRV) && !strcmp(rrname, name2))
910 /* Noone will want to have alternative DC servers, so
911 * don't care about priority and weigth */
922 if((rrname = readname(buf, eom, &p)) == NULL)
935 static int getsrvrr(char *name, char **host, int *port)
942 static struct addrinfo *gaicat(struct addrinfo *l1, struct addrinfo *l2)
948 for(p = l1; p->ai_next != NULL; p = p->ai_next);
953 /* This isn't actually correct, in any sense of the word. It only
954 * works on systems whose getaddrinfo implementation saves the
955 * sockaddr in the same malloc block as the struct addrinfo. Those
956 * systems include at least FreeBSD and glibc-based systems, though,
957 * so it should not be any immediate threat, and it allows me to not
958 * implement a getaddrinfo wrapper. It can always be changed, should
960 static struct addrinfo *unixgai(int type, char *path)
964 struct sockaddr_un *un;
966 buf = smalloc(sizeof(*ai) + sizeof(*un));
967 memset(buf, 0, sizeof(*ai) + sizeof(*un));
968 ai = (struct addrinfo *)buf;
969 un = (struct sockaddr_un *)(buf + sizeof(*ai));
971 ai->ai_family = AF_UNIX;
972 ai->ai_socktype = type;
974 ai->ai_addrlen = sizeof(*un);
975 ai->ai_addr = (struct sockaddr *)un;
976 ai->ai_canonname = NULL;
978 un->sun_family = PF_UNIX;
979 strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
983 static struct addrinfo *resolvtcp(char *name, int port)
985 struct addrinfo hint, *ret;
988 memset(&hint, 0, sizeof(hint));
989 hint.ai_socktype = SOCK_STREAM;
990 hint.ai_flags = AI_NUMERICSERV | AI_CANONNAME;
991 snprintf(tmp, sizeof(tmp), "%i", port);
992 if(!getaddrinfo(name, tmp, &hint, &ret))
997 static struct addrinfo *resolvsrv(char *name)
999 struct addrinfo *ret;
1003 if(getsrvrr(name, &realname, &port))
1005 ret = resolvtcp(realname, port);
1010 static struct addrinfo *resolvhost(char *host)
1013 struct addrinfo *ret;
1016 if(strchr(host, '/'))
1017 return(unixgai(SOCK_STREAM, host));
1018 if((strchr(host, ':') == NULL) && ((ret = resolvsrv(host)) != NULL))
1021 if((*host == '[') && ((p = strchr(host, ']')) != NULL))
1023 hp = memcpy(smalloc(p - host), host + 1, (p - host) - 1);
1024 hp[(p - host) - 1] = 0;
1025 if(strchr(hp, ':') != NULL) {
1031 ret = resolvtcp(hp, port);
1039 if((p = strrchr(hp, ':')) != NULL) {
1045 ret = resolvtcp(hp, port);
1052 static struct addrinfo *defaulthost(void)
1054 struct addrinfo *ret;
1059 if(((tmp = getenv("DCSERVER")) != NULL) && *tmp)
1060 return(resolvhost(tmp));
1062 if((getuid() != 0) && ((pwd = getpwuid(getuid())) != NULL))
1064 tmp = sprintf2("/tmp/doldacond-%s", pwd->pw_name);
1065 ret = gaicat(ret, unixgai(SOCK_STREAM, tmp));
1068 ret = gaicat(ret, unixgai(SOCK_STREAM, "/var/run/doldacond.sock"));
1069 ret = gaicat(ret, resolvtcp("localhost", 1500));
1070 if(!getdomainname(dn, sizeof(dn)) && *dn && strcmp(dn, "(none)"))
1071 ret = gaicat(ret, resolvsrv(dn));
1075 int dc_connect(char *host)
1083 if(hostlist != NULL)
1084 freeaddrinfo(hostlist);
1086 hostlist = defaulthost();
1088 hostlist = resolvhost(host);
1089 if(hostlist == NULL)
1091 for(curhost = hostlist; curhost != NULL; curhost = curhost->ai_next)
1093 if((fd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
1099 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
1100 if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
1102 if(errno == EINPROGRESS)
1110 if(curhost->ai_canonname != NULL)
1111 dchostname = sstrdup(curhost->ai_canonname);
1116 qcmd = makeqcmd(NULL);
1121 struct dc_intresp *dc_interpret(struct dc_response *resp)
1124 struct dc_intresp *iresp;
1125 struct command *cmd;
1126 struct respclass *cls;
1130 if((resp->numlines == 0) || (resp->rlines[0].argc == 0) || (resp->curline >= resp->numlines))
1132 code = wcstol(resp->rlines[0].argv[0], NULL, 10);
1133 cmd = (struct command *)(resp->internal);
1134 for(cls = cmd->classes; cls != NULL; cls = cls->next)
1136 if(cls->code == code)
1141 if(cls->nwords >= resp->rlines[resp->curline].argc)
1143 iresp = smalloc(sizeof(*iresp));
1148 for(i = 0; i < cls->nwords; i++)
1150 switch(cls->wordt[i])
1155 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1156 iresp->argv[iresp->argc].val.str = swcsdup(resp->rlines[resp->curline].argv[i + 1]);
1157 iresp->argv[iresp->argc].type = cls->wordt[i];
1161 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1162 iresp->argv[iresp->argc].val.num = wcstol(resp->rlines[resp->curline].argv[i + 1], NULL, 0);
1163 iresp->argv[iresp->argc].type = cls->wordt[i];
1167 sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1168 iresp->argv[iresp->argc].val.flnum = wcstod(resp->rlines[resp->curline].argv[i + 1], NULL);
1169 iresp->argv[iresp->argc].type = cls->wordt[i];
1178 void dc_freeires(struct dc_intresp *ires)
1182 for(i = 0; i < ires->argc; i++)
1184 if(ires->argv[i].type == RESP_STR)
1185 free(ires->argv[i].val.str);
1191 const char *dc_gethostname(void)