diff options
author | Josiah Worcester <josiahw@gmail.com> | 2015-02-22 20:58:10 -0600 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2015-02-23 01:02:14 -0500 |
commit | 34b423d2077a4c799d2089068d3bec91fb800256 (patch) | |
tree | 7b852fdc3d580b4d5dc12494812a5a5d11328f66 /src/passwd/nscd_query.c | |
parent | 0afef1aa24b784c86ae6121ca39e999824086c7c (diff) | |
download | musl-34b423d2077a4c799d2089068d3bec91fb800256.tar.gz musl-34b423d2077a4c799d2089068d3bec91fb800256.tar.xz musl-34b423d2077a4c799d2089068d3bec91fb800256.zip |
support alternate backends for the passwd and group dbs
when we fail to find the entry in the commonly accepted files, we query a server over a Unix domain socket on /var/run/nscd/socket. the protocol used here is compatible with glibc's nscd protocol on most systems (all that use 32-bit numbers for all the protocol fields, which appears to be everything but Alpha).
Diffstat (limited to 'src/passwd/nscd_query.c')
-rw-r--r-- | src/passwd/nscd_query.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/passwd/nscd_query.c b/src/passwd/nscd_query.c new file mode 100644 index 00000000..f8d0fc13 --- /dev/null +++ b/src/passwd/nscd_query.c @@ -0,0 +1,100 @@ +#include <sys/socket.h> +#include <byteswap.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include "nscd.h" + +static const struct { + short sun_family; + char sun_path[21]; +} addr = { + AF_UNIX, + "/var/run/nscd/socket" +}; + +FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap) +{ + size_t i; + int fd; + FILE *f = 0; + int32_t req_buf[REQ_LEN] = { + NSCDVERSION, + req, + strlen(key)+1 + }; + struct msghdr msg = { + .msg_iov = (struct iovec[]){ + {&req_buf, sizeof(req_buf)}, + {(char*)key, strlen(key)+1} + }, + .msg_iovlen = 2 + }; + + if (strlen(key) > INT32_MAX - 1) { + return (FILE*)-1; + } + + *swap = 0; +retry: + + fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) return NULL; + + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + /* If there isn't a running nscd we return -1 to indicate that + * that is precisely what happened + */ + if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) { + close(fd); + return (FILE *)-1; + } + goto error; + } + + if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0) + goto error; + + if(!(f = fdopen(fd, "r"))) goto error; + + if (!fread(buf, len, 1, f)) { + /* If the VERSION entry mismatches nscd will disconnect. The + * most likely cause is that the endianness mismatched. So, we + * byteswap and try once more. (if we already swapped, just + * fail out) + */ + if (ferror(f)) goto error; + if (!*swap) { + fclose(f); + for (i = 0; i < sizeof(req_buf)/sizeof(req_buf[0]); i++) { + req_buf[i] = bswap_32(req_buf[i]); + } + *swap = 1; + goto retry; + } else { + errno = EIO; + goto error; + } + } + + if (*swap) { + for (i = 0; i < len/sizeof(buf[0]); i++) { + buf[i] = bswap_32(buf[i]); + } + } + + /* The first entry in every nscd response is the version number. This + * really shouldn't happen, and is evidence of some form of malformed + * response. + */ + if(buf[0] != NSCDVERSION) { + errno = EIO; + goto error; + } + + return f; +error: + if (f) fclose(f); else close(fd); + return 0; +} |