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