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