1290dd95e0ea4ab63d15a4ad3fa1293982903a64
[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 <sys/un.h>
42 #include <fcntl.h>
43 #include <netdb.h>
44 #include <sys/poll.h>
45 #include <pwd.h>
46 #ifdef HAVE_RESOLVER
47 #include <arpa/nameser.h>
48 #include <resolv.h>
49 #endif
50
51 #include <doldaconnect/uilib.h>
52 #include <utils.h>
53
54 #define DOLCON_SRV_NAME "_dolcon._tcp"
55
56 #define RESP_END -1
57 #define RESP_DSC 0
58 #define RESP_STR 1
59 #define RESP_INT 2
60 #define RESP_FLOAT 3
61 #define RESP_LNUM 4
62
63 struct respclass
64 {
65     struct respclass *next;
66     int code;
67     int nwords;
68     int *wordt;
69 };
70
71 struct command
72 {
73     struct command *next;
74     wchar_t *name;
75     struct respclass *classes;
76 };
77
78 struct qcmd
79 {
80     struct qcmd *next;
81     struct command *cmd;
82     int tag;
83     char *buf;
84     size_t buflen;
85     int (*callback)(struct dc_response *resp);
86     void *data;
87 };
88
89 void dc_uimisc_disconnected(void);
90
91 /* The first command must be the nameless connect command and second
92  * the notification command. */
93 static struct command *commands = NULL;
94 static struct qcmd *queue = NULL, *queueend = NULL;
95 static struct dc_response *respqueue = NULL, *respqueueend = NULL;
96 static int state = -1;
97 static int fd = -1;
98 static iconv_t ichandle;
99 static int resetreader = 1;
100 static struct addrinfo *hostlist = NULL, *curhost = NULL;
101 struct {
102     char *hostname;
103     int family;
104     int sentcreds;
105 } servinfo;
106 char *dc_srv_local;
107
108 static void message(int bits, char *format, ...)
109 {
110     static int hb = -1;
111     char *v;
112     va_list args;
113     
114     if(hb == -1)
115     {
116         hb = 0;
117         if((v = getenv("LIBDCUI_MSG")) != NULL)
118             hb = strtol(v, NULL, 0) & 65535;
119     }
120     if(hb & bits)
121     {
122         va_start(args, format);
123         vfprintf(stderr, format, args);
124         va_end(args);
125     }
126 }
127
128 static char *formataddress(struct sockaddr *arg, socklen_t arglen)
129 {
130     struct sockaddr_in *ipv4;
131 #ifdef HAVE_IPV6
132     struct sockaddr_in6 *ipv6;
133 #endif
134     static char *ret = NULL;
135     char buf[1024];
136     
137     if(ret != NULL)
138         free(ret);
139     ret = NULL;
140     switch(arg->sa_family)
141     {
142     case AF_UNIX:
143         ret = sprintf2("Unix socket (%s)", ((struct sockaddr_un *)arg)->sun_path);
144         break;
145     case AF_INET:
146         ipv4 = (struct sockaddr_in *)arg;
147         if(inet_ntop(AF_INET, &ipv4->sin_addr, buf, sizeof(buf)) == NULL)
148             return(NULL);
149         ret = sprintf2("%s:%i", buf, (int)ntohs(ipv4->sin_port));
150         break;
151 #ifdef HAVE_IPV6
152     case AF_INET6:
153         ipv6 = (struct sockaddr_in6 *)arg;
154         if(inet_ntop(AF_INET6, &ipv6->sin6_addr, buf, sizeof(buf)) == NULL)
155             return(NULL);
156         ret = sprintf2("[%s]:%i", buf, (int)ntohs(ipv6->sin6_port));
157         break;
158 #endif
159     default:
160         errno = EPFNOSUPPORT;
161         break;
162     }
163     return(ret);
164 }
165 static struct dc_response *makeresp(void)
166 {
167     struct dc_response *new;
168     
169     new = smalloc(sizeof(*new));
170     new->next = NULL;
171     new->prev = NULL;
172     new->code = 0;
173     new->cmdname = NULL;
174     new->rlines = NULL;
175     new->linessize = 0;
176     new->numlines = 0;
177     new->curline = 0;
178     new->data = NULL;
179     return(new);
180 }
181
182 static void freeqcmd(struct qcmd *qcmd)
183 {
184     if(qcmd->buf != NULL)
185         free(qcmd->buf);
186     free(qcmd);
187 }
188
189 static void unlinkqueue(void)
190 {
191     struct qcmd *qcmd;
192     
193     if((qcmd = queue) == NULL)
194         return;
195     queue = qcmd->next;
196     if(queue == NULL)
197         queueend = NULL;
198     freeqcmd(qcmd);
199 }
200
201 static struct qcmd *makeqcmd(wchar_t *name)
202 {
203     struct qcmd *new;
204     struct command *cmd;
205     static int tag = 0;
206     
207     if(name == NULL)
208     {
209         cmd = commands;
210     } else {
211         for(cmd = commands; cmd != NULL; cmd = cmd->next)
212         {
213             if((cmd->name != NULL) && !wcscmp(cmd->name, name))
214                 break;
215         }
216         if(cmd == NULL) {
217             errno = ENOSYS; /* Bleh */
218             return(NULL);
219         }
220     }
221     new = smalloc(sizeof(*new));
222     new->tag = tag++;
223     new->cmd = cmd;
224     new->buf = NULL;
225     new->buflen = 0;
226     new->callback = NULL;
227     new->next = NULL;
228     new->data = NULL;
229     if(queueend == NULL)
230     {
231         queue = queueend = new;
232     } else {
233         queueend->next = new;
234         queueend = new;
235     }
236     return(new);
237 }
238
239 static wchar_t *quoteword(wchar_t *word)
240 {
241     wchar_t *wp, *buf, *bp;
242     int dq, numbs, numc;
243     
244     dq = 0;
245     numbs = 0;
246     numc = 0;
247     if(*word == L'\0')
248     {
249         dq = 1;
250     } else {
251         for(wp = word; *wp != L'\0'; wp++)
252         {
253             if(!dq && iswspace(*wp))
254                 dq = 1;
255             if((*wp == L'\\') || (*wp == L'\"'))
256                 numbs++;
257             numc++;
258         }
259     }
260     if(!dq && !numbs)
261         return(NULL);
262     bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
263     if(dq)
264         *(bp++) = L'\"';
265     for(wp = word; *wp != L'\0'; wp++)
266     {
267         if((*wp == L'\\') || (*wp == L'\"'))
268             *(bp++) = L'\\';
269         *(bp++) = *wp;
270     }
271     if(dq)
272         *(bp++) = L'\"';
273     *(bp++) = L'\0';
274     return(buf);
275 }
276
277 static struct command *makecmd(wchar_t *name)
278 {
279     struct command *new;
280     
281     new = smalloc(sizeof(*new));
282     new->name = name;
283     new->classes = NULL;
284     new->next = commands;
285     commands = new;
286     return(new);
287 }
288
289 static struct respclass *addresp(struct command *cmd, int code, ...)
290 {
291     struct respclass *new;
292     int i;
293     int resps[128];
294     va_list args;
295         
296     va_start(args, code);
297     i = 0;
298     while((resps[i++] = va_arg(args, int)) != RESP_END);
299     i--;
300     va_end(args);
301     new = smalloc(sizeof(*new));
302     new->code = code;
303     new->nwords = i;
304     if(i > 0)
305     {
306         new->wordt = smalloc(sizeof(int) * i);
307         memcpy(new->wordt, resps, sizeof(int) * i);
308     } else {
309         new->wordt = NULL;
310     }
311     new->next = cmd->classes;
312     cmd->classes = new;
313     return(new);
314 }
315
316 #include "initcmds.h"
317
318 int dc_init(void)
319 {
320     if((ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
321         return(-1);
322     dc_srv_local = sstrdup("");
323     initcmds();
324     return(0);
325 }
326
327 void dc_cleanup(void)
328 {
329     iconv_close(ichandle);
330 }
331
332 void dc_disconnect(void)
333 {
334     struct dc_response *resp;
335     
336     state = -1;
337     if(fd >= 0)
338         close(fd);
339     fd = -1;
340     while(queue != NULL)
341         unlinkqueue();
342     while((resp = dc_getresp()) != NULL)
343         dc_freeresp(resp);
344     dc_uimisc_disconnected();
345     if(servinfo.hostname != NULL)
346         free(servinfo.hostname);
347     memset(&servinfo, 0, sizeof(servinfo));
348 }
349
350 void dc_freeresp(struct dc_response *resp)
351 {
352     int i, o;
353     
354     for(i = 0; i < resp->numlines; i++)
355     {
356         for(o = 0; o < resp->rlines[i].argc; o++)
357             free(resp->rlines[i].argv[o]);
358         free(resp->rlines[i].argv);
359     }
360     free(resp->rlines);
361     free(resp);
362 }
363
364 struct dc_response *dc_getresp(void)
365 {
366     struct dc_response *ret;
367     
368     if((ret = respqueue) == NULL)
369         return(NULL);
370     respqueue = ret->next;
371     if(respqueue == NULL)
372         respqueueend = NULL;
373     else
374         respqueue->prev = NULL;
375     return(ret);
376 }
377
378 struct dc_response *dc_gettaggedresp(int tag)
379 {
380     struct dc_response *resp;
381     
382     for(resp = respqueue; resp != NULL; resp = resp->next)
383     {
384         if(resp->tag == tag)
385         {
386             if(resp->prev != NULL)
387                 resp->prev->next = resp->next;
388             if(resp->next != NULL)
389                 resp->next->prev = resp->prev;
390             if(resp == respqueue)
391                 respqueue = resp->next;
392             if(resp == respqueueend)
393                 respqueueend = resp->prev;
394             return(resp);
395         }
396     }
397     return(NULL);
398 }
399
400 struct dc_response *dc_gettaggedrespsync(int tag)
401 {
402     struct pollfd pfd;
403     struct dc_response *resp;
404     
405     while((resp = dc_gettaggedresp(tag)) == NULL)
406     {
407         pfd.fd = fd;
408         pfd.events = POLLIN;
409         if(dc_wantwrite())
410             pfd.events |= POLLOUT;
411         if(poll(&pfd, 1, -1) < 0)
412             return(NULL);
413         if((pfd.revents & POLLIN) && dc_handleread())
414             return(NULL);
415         if((pfd.revents & POLLOUT) && dc_handlewrite())
416             return(NULL);
417     }
418     return(resp);
419 }
420
421 int dc_wantwrite(void)
422 {
423     switch(state)
424     {
425     case 1:
426         if((queue != NULL) && (queue->buflen > 0))
427             return(1);
428         break;
429     }
430     return(0);
431 }
432
433 int dc_getstate(void)
434 {
435     return(state);
436 }
437
438 int dc_queuecmd(int (*callback)(struct dc_response *), void *data, ...)
439 {
440     struct qcmd *qcmd;
441     int num, freepart;
442     va_list al;
443     char *final, *sarg;
444     wchar_t **toks;
445     wchar_t *buf;
446     wchar_t *part, *tpart;
447     size_t bufsize, bufdata;
448     
449     buf = NULL;
450     bufsize = bufdata = 0;
451     num = 0;
452     va_start(al, data);
453     while((part = va_arg(al, wchar_t *)) != NULL)
454     {
455         if(!wcscmp(part, L"%a"))
456         {
457             for(toks = va_arg(al, wchar_t **); *toks != NULL; toks++)
458             {
459                 part = *toks;
460                 freepart = 0;
461                 if((tpart = quoteword(part)) != NULL)
462                 {
463                     freepart = 1;
464                     part = tpart;
465                 }
466                 addtobuf(buf, L' ');
467                 bufcat(buf, part, wcslen(part));
468                 num++;
469                 if(freepart)
470                     free(part);
471             }
472         } else {
473             if(*part == L'%')
474             {
475                 tpart = part + 1;
476                 if(!wcscmp(tpart, L"i"))
477                 {
478                     freepart = 1;
479                     part = swprintf2(L"%i", va_arg(al, int));
480                 } else if(!wcscmp(tpart, L"li")) {
481                     freepart = 1;
482                     part = swprintf2(L"%ji", (intmax_t)va_arg(al, dc_lnum_t));
483                 } else if(!wcscmp(tpart, L"s")) {
484                     freepart = 1;
485                     part = icmbstowcs(sarg = va_arg(al, char *), NULL);
486                     if(part == NULL)
487                     {
488                         if(buf != NULL)
489                             free(buf);
490                         return(-1);
491                     }
492                 } else if(!wcscmp(tpart, L"ls")) {
493                     freepart = 0;
494                     part = va_arg(al, wchar_t *);
495                 } else if(!wcscmp(tpart, L"ll")) {
496                     freepart = 1;
497                     part = swprintf2(L"%lli", va_arg(al, long long));
498                 } else if(!wcscmp(tpart, L"f")) {
499                     freepart = 1;
500                     part = swprintf2(L"%f", va_arg(al, double));
501                 } else if(!wcscmp(tpart, L"x")) {
502                     freepart = 1;
503                     part = swprintf2(L"%x", va_arg(al, int));
504                 } else {
505                     if(buf != NULL)
506                         free(buf);
507                     errno = EINVAL;
508                     return(-1);
509                 }
510             } else {
511                 freepart = 0;
512             }
513             if((tpart = quoteword(part)) != NULL)
514             {
515                 if(freepart)
516                     free(part);
517                 part = tpart;
518                 freepart = 1;
519             }
520             if(num > 0)
521                 addtobuf(buf, L' ');
522             if(num == 0)
523             {
524                 if((qcmd = makeqcmd(part)) == NULL)
525                 {
526                     if(freepart)
527                         free(part);
528                     if(buf != NULL)
529                         free(buf);
530                     return(-1);
531                 } else {
532                     qcmd->callback = callback;
533                     qcmd->data = data;
534                 }
535             }
536             bufcat(buf, part, wcslen(part));
537             num++;
538             if(freepart)
539                 free(part);
540         }
541     }
542     bufcat(buf, L"\r\n\0", 3);
543     if((final = icwcstombs(buf, "utf-8")) == NULL)
544     {
545         free(buf);
546         return(-1);
547     }
548     va_end(al);
549     free(buf);
550     qcmd->buf = final;
551     qcmd->buflen = strlen(final);
552     return(qcmd->tag);
553 }
554
555 int dc_handleread(void)
556 {
557     int ret, done;
558     char *p1, *p2;
559     size_t len;
560     socklen_t optlen;
561     int errnobak;
562     /* Ewww... this really is soo ugly. I need to clean this up some day. */
563     static int pstate = 0;
564     static char inbuf[128];
565     static size_t inbufdata = 0;
566     static wchar_t *cbuf = NULL;
567     static size_t cbufsize = 0, cbufdata = 0;
568     static wchar_t *pptr = NULL;
569     static wchar_t **argv = NULL;
570     static int argc = 0;
571     static size_t args = 0;
572     static wchar_t *cw = NULL;
573     static size_t cwsize = 0, cwdata = 0;
574     static struct dc_response *curresp = NULL;
575     static int cont = 0;
576     static int unlink = 0;
577     
578     switch(state)
579     {
580     case -1:
581         return(-1);
582     case 0:
583         optlen = sizeof(ret);
584         getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
585         if(ret)
586         {
587             int newfd;
588             
589             message(2, "could not connect to %s: %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen), strerror(ret));
590             for(curhost = curhost->ai_next; curhost != NULL; curhost = curhost->ai_next)
591             {
592                 if((newfd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
593                 {
594                     errnobak = errno;
595                     dc_disconnect();
596                     errno = errnobak;
597                     return(-1);
598                 }
599                 dup2(newfd, fd);
600                 close(newfd);
601                 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
602                 message(4, "connecting to %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen));
603                 if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
604                 {
605                     if(errno == EINPROGRESS)
606                         return(0);
607                     message(2, "could not connect to %s: %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen), strerror(ret));
608                 } else {
609                     break;
610                 }
611             }
612             if(curhost == NULL)
613             {
614                 dc_disconnect();
615                 errno = ret;
616                 return(-1);
617             }
618         }
619         if(curhost->ai_canonname != NULL)
620             servinfo.hostname = sstrdup(curhost->ai_canonname);
621         servinfo.family = curhost->ai_family;
622         state = 1;
623         resetreader = 1;
624         break;
625     case 1:
626         if(resetreader)
627         {
628             inbufdata = 0;
629             cbufdata = 0;
630             pptr = NULL;
631             pstate = 0;
632             resetreader = 0;
633             cont = 0;
634             if(curresp != NULL)
635                 dc_freeresp(curresp);
636             curresp = NULL;
637         }
638         if(inbufdata == 128)
639             inbufdata = 0;
640         ret = read(fd, inbuf + inbufdata, 128 - inbufdata);
641         if(ret < 0)
642         {
643             if((errno == EAGAIN) || (errno == EINTR))
644                 return(0);
645             errnobak = errno;
646             dc_disconnect();
647             errno = errnobak;
648             return(-1);
649         } else if(ret == 0) {
650             dc_disconnect();
651             errno = 0;
652             return(-1);
653         }
654         inbufdata += ret;
655         done = 0;
656         while(!done)
657         {
658             if(cbufsize == cbufdata)
659             {
660                 if(pptr != NULL)
661                     len = pptr - cbuf;
662                 if((cbuf = realloc(cbuf, sizeof(wchar_t) * (cbufsize += 256))) == NULL)
663                 {
664                     dc_disconnect();
665                     errno = ENOMEM;
666                     return(-1);
667                 }
668                 if(pptr != NULL)
669                     pptr = cbuf + len;
670             }
671             p1 = inbuf;
672             p2 = (char *)(cbuf + cbufdata);
673             len = sizeof(wchar_t) * (cbufsize - cbufdata);
674             ret = iconv(ichandle, &p1, &inbufdata, &p2, &len);
675             memmove(inbuf, p1, inbufdata);
676             cbufdata = cbufsize - (len / sizeof(wchar_t));
677             if(ret < 0)
678             {
679                 switch(errno)
680                 {
681                 case EILSEQ:
682                     /* XXX Is this really OK? */
683                     inbufdata = 0;
684                     done = 1;
685                     break;
686                 case EINVAL:
687                     done = 1;
688                     break;
689                 case E2BIG:
690                     break;
691                 default:
692                     errnobak = errno;
693                     dc_disconnect();
694                     errno = errnobak;
695                     return(-1);
696                 }
697             } else {
698                 done = 1;
699             }
700         }
701         if(pptr == NULL)
702             pptr = cbuf;
703         done = 0;
704         while(!done && (pptr - cbuf < cbufdata))
705         {
706             switch(pstate)
707             {
708             case 0:
709                 if(iswspace(*pptr))
710                 {
711                     if(*pptr == L'\r')
712                     {
713                         if(pptr == cbuf + cbufdata - 1)
714                         {
715                             done = 1;
716                             break;
717                         }
718                         if(*(++pptr) == L'\n')
719                         {
720                             if(curresp == NULL)
721                             {
722                                 curresp = makeresp();
723                                 if((argc > 0) && ((curresp->code = wcstol(argv[0], NULL, 10)) >= 600))
724                                 {
725                                     curresp->cmdname = L".notify";
726                                     curresp->internal = commands->next;
727                                     curresp->tag = -1;
728                                     unlink = 0;
729                                 } else {
730                                     if((curresp->cmdname = queue->cmd->name) == NULL)
731                                         curresp->cmdname = L".connect";
732                                     curresp->data = queue->data;
733                                     curresp->tag = queue->tag;
734                                     curresp->internal = (void *)(queue->cmd);
735                                     unlink = 1;
736                                 }
737                             }
738                             sizebuf(&curresp->rlines, &curresp->linessize, curresp->numlines + 1, sizeof(*(curresp->rlines)), 1);
739                             curresp->rlines[curresp->numlines].argc = argc;
740                             curresp->rlines[curresp->numlines].argv = argv;
741                             argv = NULL;
742                             argc = args = 0;
743                             curresp->numlines++;
744                             if(!cont)
745                             {
746                                 if((curresp->code >= 600) || (queue == NULL) || (queue->callback == NULL))
747                                     ret = 0;
748                                 else
749                                     ret = queue->callback(curresp);
750                                 if(ret == 0)
751                                 {
752                                     if(respqueue == NULL)
753                                     {
754                                         respqueue = respqueueend = curresp;
755                                     } else {
756                                         curresp->next = NULL;
757                                         curresp->prev = respqueueend;
758                                         respqueueend->next = curresp;
759                                         respqueueend = curresp;
760                                     }
761                                 } else if(ret == 1) {
762                                     dc_freeresp(curresp);
763                                 }
764                                 curresp = NULL;
765                                 if(unlink)
766                                     unlinkqueue();
767                             }
768                             wmemmove(cbuf, pptr, cbufdata -= (pptr - cbuf));
769                             pptr = cbuf;
770                         } else {
771                             pptr++;
772                         }
773                     } else {
774                         pptr++;
775                     }
776                 } else {
777                     pstate = 1;
778                     cwdata = 0;
779                 }
780                 break;
781             case 1:
782                 if(iswspace(*pptr) || ((argc == 0) && (*pptr == L'-')))
783                 {
784                     if(argc == 0)
785                     {
786                         if(*pptr == L'-')
787                         {
788                             cont = 1;
789                             pptr++;
790                         } else {
791                             cont = 0;
792                         }
793                     }
794                     addtobuf(cw, L'\0');
795                     sizebuf(&argv, &args, argc + 1, sizeof(*argv), 1);
796                     argv[argc++] = cw;
797                     cw = NULL;
798                     cwsize = cwdata = 0;
799                     pstate = 0;
800                 } else if(*pptr == L'\"') {
801                     pstate = 2;
802                     pptr++;
803                 } else if(*pptr == L'\\') {
804                     if(pptr == cbuf + cbufdata - 1)
805                     {
806                         done = 1;
807                         break;
808                     }
809                     addtobuf(cw, *(++pptr));
810                     pptr++;
811                 } else {
812                     addtobuf(cw, *(pptr++));
813                 }
814                 break;
815             case 2:
816                 if(*pptr == L'\"')
817                 {
818                     pstate = 1;
819                 } else if(*pptr == L'\\') {
820                     addtobuf(cw, *(++pptr));
821                 } else {
822                     addtobuf(cw, *pptr);
823                 }
824                 pptr++;
825                 break;
826             }
827         }
828         break;
829     }
830     return(0);
831 }
832
833 #if UNIX_AUTH_STYLE == 1
834 static void mkcreds(struct msghdr *msg)
835 {
836     struct ucred *ucred;
837     static char buf[CMSG_SPACE(sizeof(*ucred))];
838     struct cmsghdr *cmsg;
839     
840     msg->msg_control = buf;
841     msg->msg_controllen = sizeof(buf);
842     cmsg = CMSG_FIRSTHDR(msg);
843     cmsg->cmsg_level = SOL_SOCKET;
844     cmsg->cmsg_type = SCM_CREDENTIALS;
845     cmsg->cmsg_len = CMSG_LEN(sizeof(*ucred));
846     ucred = (struct ucred *)CMSG_DATA(cmsg);
847     ucred->pid = getpid();
848     ucred->uid = getuid();
849     ucred->gid = getgid();
850     msg->msg_controllen = cmsg->cmsg_len;
851 }
852 #endif
853
854 int dc_handlewrite(void)
855 {
856     int ret;
857     int errnobak;
858     struct msghdr msg;
859     struct iovec bufvec;
860     
861     switch(state)
862     {
863     case 1:
864         if(queue->buflen > 0)
865         {
866             memset(&msg, 0, sizeof(msg));
867             msg.msg_iov = &bufvec;
868             msg.msg_iovlen = 1;
869             bufvec.iov_base = queue->buf;
870             bufvec.iov_len = queue->buflen;
871 #if UNIX_AUTH_STYLE == 1
872             if((servinfo.family == PF_UNIX) && !servinfo.sentcreds)
873             {
874                 mkcreds(&msg);
875                 servinfo.sentcreds = 1;
876             }
877 #endif
878             ret = sendmsg(fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT);
879             if(ret < 0)
880             {
881                 if((errno == EAGAIN) || (errno == EINTR))
882                     return(0);
883                 errnobak = errno;
884                 dc_disconnect();
885                 errno = errnobak;
886                 return(-1);
887             }
888             if(ret > 0)
889                 memmove(queue->buf, queue->buf + ret, queue->buflen -= ret);
890         }
891         break;
892     }
893     return(0);
894 }
895
896 #ifdef HAVE_RESOLVER
897 /*
898  * It kind of sucks that libresolv doesn't have any DNS parsing
899  * routines. We'll have to do it manually.
900  */
901 static char *readname(unsigned char *msg, unsigned char *eom, unsigned char **p)
902 {
903     char *name, *tname;
904     unsigned char *tp;
905     size_t namesize, namedata, len;
906     
907     name = NULL;
908     namesize = namedata = 0;
909     while(1)
910     {
911         len = *((*p)++);
912         if(len == 0)
913         {
914             addtobuf(name, 0);
915             return(name);
916         } else if(len == 0xc0) {
917             tp = msg + *((*p)++);
918             if((tname = readname(msg, eom, &tp)) == NULL)
919             {
920                 if(name != NULL)
921                     free(name);
922                 return(NULL);
923             }
924             bufcat(name, tname, strlen(tname));
925             addtobuf(name, 0);
926             free(tname);
927             return(name);
928         } else if(*p + len >= eom) {
929             if(name != NULL)
930                 free(name);
931             return(NULL);
932         }
933         bufcat(name, *p, len);
934         *p += len;
935         addtobuf(name, '.');
936     }
937 }
938
939 static int skipname(unsigned char *msg, unsigned char *eom, unsigned char **p)
940 {
941     size_t len;
942     
943     while(1)
944     {
945         len = *((*p)++);
946         if(len == 0)
947         {
948             return(0);
949         } else if(len == 0xc0) {
950             (*p)++;
951             return(0);
952         } else if(*p + len >= eom) {
953             return(-1);
954         }
955         *p += len;
956     }
957 }
958
959 static int getsrvrr(char *name, char **host, int *port)
960 {
961     int i;
962     char *name2, *rrname;
963     unsigned char *eom, *p;
964     unsigned char buf[1024];
965     int flags, num, class, type;
966     size_t len;
967     int ret;
968     
969     if(!(_res.options & RES_INIT))
970     {
971         if(res_init() < 0)
972             return(-1);
973     }
974     /* res_querydomain doesn't work for some reason */
975     if(name[strlen(name) - 1] == '.')
976         name2 = sprintf2("%s.%s", DOLCON_SRV_NAME, name);
977     else
978         name2 = sprintf2("%s.%s.", DOLCON_SRV_NAME, name);
979     ret = res_query(name2, C_IN, T_SRV, buf, sizeof(buf));
980     if(ret < 0)
981     {
982         free(name2);
983         return(-1);
984     }
985     eom = buf + ret;
986     /*
987      * Assume transaction ID is correct.
988      *
989      * Flags check: FA0F masks in request/response flag, opcode,
990      * truncated flag and status code, and ignores authoritativeness,
991      * recursion flags and DNSSEC and reserved bits.
992      */
993     flags = (buf[2] << 8) + buf[3];
994     if((flags & 0xfa0f) != 0x8000)
995     {
996         free(name2);
997         return(-1);
998     }
999     /* Skip the query entries */
1000     num = (buf[4] << 8) + buf[5];
1001     p = buf + 12;
1002     for(i = 0; i < num; i++)
1003     {
1004         if(skipname(buf, eom, &p))
1005         {
1006             free(name2);
1007             return(-1);
1008         }
1009         p += 4; /* Type and class */
1010     }
1011     /* Parse answers */
1012     num = (buf[6] << 8) + buf[7];
1013     for(i = 0; i < num; i++)
1014     {
1015         if((rrname = readname(buf, eom, &p)) == NULL)
1016         {
1017             free(name2);
1018             return(-1);
1019         }
1020         type = *(p++) << 8;
1021         type += *(p++);
1022         class = *(p++) << 8;
1023         class += *(p++);
1024         p += 4; /* TTL */
1025         len = *(p++) << 8;
1026         len += *(p++);
1027         if((class == C_IN) && (type == T_SRV) && !strcmp(rrname, name2))
1028         {
1029             free(rrname);
1030             free(name2);
1031             /* Noone will want to have alternative DC servers, so
1032              * don't care about priority and weigth */
1033             p += 4;
1034             if(port == NULL)
1035             {
1036                 p += 2;
1037             } else {
1038                 *port = *(p++) << 8;
1039                 *port += *(p++);
1040             }
1041             if(host != NULL)
1042             {
1043                 if((rrname = readname(buf, eom, &p)) == NULL)
1044                     return(-1);
1045                 *host = rrname;
1046             }
1047             return(0);
1048         }
1049         p += len;
1050         free(rrname);
1051     }
1052     free(name2);
1053     return(-1);
1054 }
1055 #else
1056 static int getsrvrr(char *name, char **host, int *port)
1057 {
1058     errno = EOPNOTSUPP;
1059     return(-1);
1060 }
1061 #endif
1062
1063 static struct addrinfo *gaicat(struct addrinfo *l1, struct addrinfo *l2)
1064 {
1065     struct addrinfo *p;
1066     
1067     if(l1 == NULL)
1068         return(l2);
1069     for(p = l1; p->ai_next != NULL; p = p->ai_next);
1070     p->ai_next = l2;
1071     return(l1);
1072 }
1073
1074 /* This isn't actually correct, in any sense of the word. It only
1075  * works on systems whose getaddrinfo implementation saves the
1076  * sockaddr in the same malloc block as the struct addrinfo. Those
1077  * systems include at least FreeBSD and glibc-based systems, though,
1078  * so it should not be any immediate threat, and it allows me to not
1079  * implement a getaddrinfo wrapper. It can always be changed, should
1080  * the need arise. */
1081 static struct addrinfo *unixgai(int type, char *path)
1082 {
1083     void *buf;
1084     struct addrinfo *ai;
1085     struct sockaddr_un *un;
1086     
1087     buf = smalloc(sizeof(*ai) + sizeof(*un));
1088     memset(buf, 0, sizeof(*ai) + sizeof(*un));
1089     ai = (struct addrinfo *)buf;
1090     un = (struct sockaddr_un *)(buf + sizeof(*ai));
1091     ai->ai_flags = 0;
1092     ai->ai_family = AF_UNIX;
1093     ai->ai_socktype = type;
1094     ai->ai_protocol = 0;
1095     ai->ai_addrlen = sizeof(*un);
1096     ai->ai_addr = (struct sockaddr *)un;
1097     ai->ai_canonname = NULL;
1098     ai->ai_next = NULL;
1099     un->sun_family = PF_UNIX;
1100     strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
1101     return(ai);
1102 }
1103
1104 static struct addrinfo *resolvtcp(char *name, int port)
1105 {
1106     struct addrinfo hint, *ret;
1107     char tmp[32];
1108     
1109     memset(&hint, 0, sizeof(hint));
1110     hint.ai_socktype = SOCK_STREAM;
1111     hint.ai_flags = AI_NUMERICSERV | AI_CANONNAME;
1112     snprintf(tmp, sizeof(tmp), "%i", port);
1113     if(!getaddrinfo(name, tmp, &hint, &ret))
1114         return(ret);
1115     return(NULL);
1116 }
1117
1118 static struct addrinfo *resolvsrv(char *name)
1119 {
1120     struct addrinfo *ret;
1121     char *realname;
1122     int port;
1123     
1124     if(getsrvrr(name, &realname, &port))
1125         return(NULL);
1126     message(4, "SRV RR resolved: %s -> %s\n", name, realname);
1127     ret = resolvtcp(realname, port);
1128     free(realname);
1129     return(ret);
1130 }
1131
1132 static struct addrinfo *resolvhost(char *host)
1133 {
1134     char *p, *hp;
1135     struct addrinfo *ret;
1136     int port;
1137     
1138     if(strchr(host, '/'))
1139         return(unixgai(SOCK_STREAM, host));
1140     if((strchr(host, ':') == NULL) && ((ret = resolvsrv(host)) != NULL))
1141         return(ret);
1142     ret = NULL;
1143     if((*host == '[') && ((p = strchr(host, ']')) != NULL))
1144     {
1145         hp = memcpy(smalloc(p - host), host + 1, (p - host) - 1);
1146         hp[(p - host) - 1] = 0;
1147         if(strchr(hp, ':') != NULL) {
1148             port = 0;
1149             if(*(++p) == ':')
1150                 port = atoi(p + 1);
1151             if(port == 0)
1152                 port = 1500;
1153             ret = resolvtcp(hp, port);
1154         }
1155         free(hp);
1156     }
1157     if(ret != NULL)
1158         return(ret);
1159     hp = sstrdup(host);
1160     port = 0;
1161     if((p = strrchr(hp, ':')) != NULL) {
1162         *(p++) = 0;
1163         port = atoi(p);
1164     }
1165     if(port == 0)
1166         port = 1500;
1167     ret = resolvtcp(hp, port);
1168     free(hp);
1169     if(ret != NULL)
1170         return(ret);
1171     return(NULL);
1172 }
1173
1174 static struct addrinfo *getlocalai(void)
1175 {
1176     struct addrinfo *ret;
1177     struct passwd *pwd;
1178     char *tmp;
1179
1180     ret = NULL;
1181     if((getuid() != 0) && ((pwd = getpwuid(getuid())) != NULL))
1182     {
1183         tmp = sprintf2("/tmp/doldacond-%s", pwd->pw_name);
1184         ret = unixgai(SOCK_STREAM, tmp);
1185         free(tmp);
1186     }
1187     ret = gaicat(ret, unixgai(SOCK_STREAM, "/var/run/doldacond.sock"));
1188     return(ret);
1189 }
1190
1191 static struct addrinfo *defaulthost(void)
1192 {
1193     struct addrinfo *ret;
1194     char *tmp;
1195     char dn[1024];
1196     
1197     if(((tmp = getenv("DCSERVER")) != NULL) && *tmp) {
1198         message(4, "using DCSERVER: %s\n", tmp);
1199         return(resolvhost(tmp));
1200     }
1201     ret = getlocalai();
1202     ret = gaicat(ret, resolvtcp("localhost", 1500));
1203     if(!getdomainname(dn, sizeof(dn)) && *dn && strcmp(dn, "(none)"))
1204         ret = gaicat(ret, resolvsrv(dn));
1205     return(ret);
1206 }
1207
1208 static int dc_connectai(struct addrinfo *hosts, struct qcmd **cnctcmd)
1209 {
1210     struct qcmd *qcmd;
1211     int errnobak;
1212     
1213     if(fd >= 0)
1214         dc_disconnect();
1215     state = -1;
1216     if(hostlist != NULL)
1217         freeaddrinfo(hostlist);
1218     hostlist = hosts;
1219     for(curhost = hostlist; curhost != NULL; curhost = curhost->ai_next)
1220     {
1221         if((fd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
1222         {
1223             errnobak = errno;
1224             freeaddrinfo(hostlist);
1225             hostlist = NULL;
1226             errno = errnobak;
1227             return(-1);
1228         }
1229         fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
1230         message(4, "connecting to %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen));
1231         if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
1232         {
1233             if(errno == EINPROGRESS)
1234             {
1235                 state = 0;
1236                 break;
1237             }
1238             message(2, "could not connect to %s: %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen), strerror(errno));
1239             close(fd);
1240             fd = -1;
1241         } else {
1242             if(curhost->ai_canonname != NULL)
1243                 servinfo.hostname = sstrdup(curhost->ai_canonname);
1244             servinfo.family = curhost->ai_family;
1245             state = 1;
1246             break;
1247         }
1248     }
1249     if(state != -1)
1250     {
1251         qcmd = makeqcmd(NULL);
1252         if(cnctcmd != NULL)
1253             *cnctcmd = qcmd;
1254         resetreader = 1;
1255     } else {
1256         free(hostlist);
1257         hostlist = NULL;
1258     }
1259     return(fd);
1260 }
1261
1262 static int dc_connect2(char *host, struct qcmd **cnctcmd)
1263 {
1264     struct addrinfo *ai;
1265     struct qcmd *qcmd;
1266     int ret;
1267     
1268     if(host == dc_srv_local) {
1269         message(4, "connect start: Unix\n");
1270         ai = getlocalai();
1271     } else if(!host || !*host) {
1272         message(4, "connect start: default\n");
1273         ai = defaulthost();
1274     } else {
1275         message(4, "connect start: host %s\n", host);
1276         ai = resolvhost(host);
1277     }
1278     if(ai == NULL)
1279         return(-1);
1280     ret = dc_connectai(ai, &qcmd);
1281     if((ret >= 0) && (cnctcmd != NULL))
1282         *cnctcmd = qcmd;
1283     return(ret);
1284 }
1285
1286 int dc_connect(char *host)
1287 {
1288     return(dc_connect2(host, NULL));
1289 }
1290
1291 int dc_connectsync(char *host, struct dc_response **respbuf)
1292 {
1293     int ret;
1294     struct qcmd *cc;
1295     struct dc_response *resp;
1296     
1297     if((ret = dc_connect2(host, &cc)) < 0)
1298         return(-1);
1299     resp = dc_gettaggedrespsync(cc->tag);
1300     if(resp == NULL) {
1301         dc_disconnect();
1302         return(-1);
1303     }
1304     if(respbuf == NULL)
1305         dc_freeresp(resp);
1306     else
1307         *respbuf = resp;
1308     return(ret);
1309 }
1310
1311 int dc_connectsync2(char *host, int rev)
1312 {
1313     int ret;
1314     struct dc_response *resp;
1315     
1316     if((ret = dc_connectsync(host, &resp)) < 0)
1317         return(-1);
1318     if(dc_checkprotocol(resp, rev))
1319     {
1320         dc_freeresp(resp);
1321         dc_disconnect();
1322         errno = EPROTONOSUPPORT;
1323         return(-1);
1324     }
1325     dc_freeresp(resp);
1326     return(ret);
1327 }
1328
1329 struct dc_intresp *dc_interpret(struct dc_response *resp)
1330 {
1331     int i;
1332     struct dc_intresp *iresp;
1333     struct command *cmd;
1334     struct respclass *cls;
1335     int code;
1336     size_t args;
1337     
1338     if((resp->numlines == 0) || (resp->rlines[0].argc == 0) || (resp->curline >= resp->numlines))
1339         return(NULL);
1340     code = wcstol(resp->rlines[0].argv[0], NULL, 10);
1341     cmd = (struct command *)(resp->internal);
1342     for(cls = cmd->classes; cls != NULL; cls = cls->next)
1343     {
1344         if(cls->code == code)
1345             break;
1346     }
1347     if(cls == NULL)
1348         return(NULL);
1349     if(cls->nwords >= resp->rlines[resp->curline].argc)
1350         return(NULL);
1351     iresp = smalloc(sizeof(*iresp));
1352     iresp->code = code;
1353     iresp->argv = NULL;
1354     iresp->argc = 0;
1355     args = 0;
1356     for(i = 0; i < cls->nwords; i++)
1357     {
1358         switch(cls->wordt[i])
1359         {
1360         case RESP_DSC:
1361             break;
1362         case RESP_STR:
1363             sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1364             iresp->argv[iresp->argc].val.str = swcsdup(resp->rlines[resp->curline].argv[i + 1]);
1365             iresp->argv[iresp->argc].type = cls->wordt[i];
1366             iresp->argc++;
1367             break;
1368         case RESP_INT:
1369             sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1370             iresp->argv[iresp->argc].val.num = wcstol(resp->rlines[resp->curline].argv[i + 1], NULL, 0);
1371             iresp->argv[iresp->argc].type = cls->wordt[i];
1372             iresp->argc++;
1373             break;
1374         case RESP_FLOAT:
1375             sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1376             iresp->argv[iresp->argc].val.flnum = wcstod(resp->rlines[resp->curline].argv[i + 1], NULL);
1377             iresp->argv[iresp->argc].type = cls->wordt[i];
1378             iresp->argc++;
1379             break;
1380         case RESP_LNUM:
1381             sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
1382             iresp->argv[iresp->argc].val.lnum = wcstoll(resp->rlines[resp->curline].argv[i + 1], NULL, 0);
1383             iresp->argv[iresp->argc].type = cls->wordt[i];
1384             iresp->argc++;
1385             break;
1386         }
1387     }
1388     resp->curline++;
1389     return(iresp);
1390 }
1391
1392 void dc_freeires(struct dc_intresp *ires)
1393 {
1394     int i;
1395     
1396     for(i = 0; i < ires->argc; i++)
1397     {
1398         if(ires->argv[i].type == RESP_STR)
1399             free(ires->argv[i].val.str);
1400     }
1401     free(ires->argv);
1402     free(ires);
1403 }
1404
1405 int dc_checkprotocol(struct dc_response *resp, int revision)
1406 {
1407     struct dc_intresp *ires;
1408     int low, high;
1409     
1410     if(resp->code != 201)
1411         return(-1);
1412     resp->curline = 0;
1413     if((ires = dc_interpret(resp)) == NULL)
1414         return(-1);
1415     low = ires->argv[0].val.num;
1416     high = ires->argv[1].val.num;
1417     dc_freeires(ires);
1418     if((revision < low) || (revision > high))
1419         return(-1);
1420     return(0);
1421 }
1422
1423 const char *dc_gethostname(void)
1424 {
1425     return(servinfo.hostname);
1426 }
1427
1428 int dc_getfd(void)
1429 {
1430     return(fd);
1431 }