f8d0fc13d0fd48c59acc5890f2139b09389f2b4d
[oweals/musl.git] / src / passwd / nscd_query.c
1 #include <sys/socket.h>
2 #include <byteswap.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <errno.h>
7 #include "nscd.h"
8
9 static const struct {
10         short sun_family;
11         char sun_path[21];
12 } addr = {
13         AF_UNIX,
14         "/var/run/nscd/socket"
15 };
16
17 FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap)
18 {
19         size_t i;
20         int fd;
21         FILE *f = 0;
22         int32_t req_buf[REQ_LEN] = {
23                 NSCDVERSION,
24                 req,
25                 strlen(key)+1
26         };
27         struct msghdr msg = {
28                 .msg_iov = (struct iovec[]){
29                         {&req_buf, sizeof(req_buf)},
30                         {(char*)key, strlen(key)+1}
31                 },
32                 .msg_iovlen = 2
33         };
34
35         if (strlen(key) > INT32_MAX - 1) {
36                 return (FILE*)-1;
37         }
38
39         *swap = 0;
40 retry:
41
42         fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
43         if (fd < 0) return NULL;
44
45         if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
46                 /* If there isn't a running nscd we return -1 to indicate that
47                  * that is precisely what happened
48                  */
49                 if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) {
50                         close(fd);
51                         return (FILE *)-1;
52                 }
53                 goto error;
54         }
55
56         if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0)
57                 goto error;
58
59         if(!(f = fdopen(fd, "r"))) goto error;
60
61         if (!fread(buf, len, 1, f)) {
62                 /* If the VERSION entry mismatches nscd will disconnect. The
63                  * most likely cause is that the endianness mismatched. So, we
64                  * byteswap and try once more. (if we already swapped, just
65                  * fail out)
66                  */
67                 if (ferror(f)) goto error;
68                 if (!*swap) {
69                         fclose(f);
70                         for (i = 0; i < sizeof(req_buf)/sizeof(req_buf[0]); i++) {
71                                 req_buf[i] = bswap_32(req_buf[i]);
72                         }
73                         *swap = 1;
74                         goto retry;
75                 } else {
76                         errno = EIO;
77                         goto error;
78                 }
79         }
80
81         if (*swap) {
82                 for (i = 0; i < len/sizeof(buf[0]); i++) {
83                         buf[i] = bswap_32(buf[i]);
84                 }
85         }
86
87         /* The first entry in every nscd response is the version number. This
88          * really shouldn't happen, and is evidence of some form of malformed
89          * response.
90          */
91         if(buf[0] != NSCDVERSION) {
92                 errno = EIO;
93                 goto error;
94         }
95
96         return f;
97 error:
98         if (f) fclose(f); else close(fd);
99         return 0;
100 }