55ccc0a81954db0b58a7a26a203f5d98cafb9b4f
[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         *swap = 0;
36 retry:
37         memset(buf, 0, len);
38         buf[0] = NSCDVERSION;
39
40         fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
41         if (fd < 0) return NULL;
42
43         if(!(f = fdopen(fd, "r"))) {
44                 close(fd);
45                 return 0;
46         }
47
48         if (strlen(key) > INT32_MAX - 1)
49                 return f;
50
51         if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
52                 /* If there isn't a running nscd we return -1 to indicate that
53                  * that is precisely what happened
54                  */
55                 if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT)
56                         return f;
57                 goto error;
58         }
59
60         if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0)
61                 goto error;
62
63         if (!fread(buf, len, 1, f)) {
64                 /* If the VERSION entry mismatches nscd will disconnect. The
65                  * most likely cause is that the endianness mismatched. So, we
66                  * byteswap and try once more. (if we already swapped, just
67                  * fail out)
68                  */
69                 if (ferror(f)) goto error;
70                 if (!*swap) {
71                         fclose(f);
72                         for (i = 0; i < sizeof(req_buf)/sizeof(req_buf[0]); i++) {
73                                 req_buf[i] = bswap_32(req_buf[i]);
74                         }
75                         *swap = 1;
76                         goto retry;
77                 } else {
78                         errno = EIO;
79                         goto error;
80                 }
81         }
82
83         if (*swap) {
84                 for (i = 0; i < len/sizeof(buf[0]); i++) {
85                         buf[i] = bswap_32(buf[i]);
86                 }
87         }
88
89         /* The first entry in every nscd response is the version number. This
90          * really shouldn't happen, and is evidence of some form of malformed
91          * response.
92          */
93         if(buf[0] != NSCDVERSION) {
94                 errno = EIO;
95                 goto error;
96         }
97
98         return f;
99 error:
100         fclose(f);
101         return 0;
102 }