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