Small cleanups.
[icmp-dn.git] / nss-icmp.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <netdb.h>
9 #include <arpa/inet.h>
10 #include <nss.h>
11 #include <sys/types.h>
12 #include <fcntl.h>
13
14 enum nss_status _nss_icmp_gethostbyaddr_r(const void *addr, socklen_t len, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop)
15 {
16     int ret;
17     struct retstruct {
18         char *aliaslist[16];
19         char *addrlist[2];
20         char retaddr[16];
21     } *retbuf;
22     char addrbuf[1024];
23     int an, thislen;
24     char *p, *p2, *p3;
25     u_int8_t *ap;
26     pid_t child;
27     int pfd[2];
28     int rl;
29     
30     retbuf = (struct retstruct *)buffer;
31     if((buflen < sizeof(*retbuf)) || (len > sizeof(retbuf->retaddr))) {
32         *errnop = ENOMEM;
33         *h_errnop = NETDB_INTERNAL;
34         return(NSS_STATUS_UNAVAIL);
35     }
36     
37     ap = (u_int8_t *)addr;
38     if(inet_ntop(af, addr, addrbuf, sizeof(addrbuf)) == NULL) {
39         *errnop = errno;
40         *h_errnop = NETDB_INTERNAL;
41         return(NSS_STATUS_UNAVAIL);
42     }
43     
44     if(pipe(pfd)) {
45         *errnop = errno;
46         *h_errnop = NETDB_INTERNAL;
47         return(NSS_STATUS_UNAVAIL);
48     }
49     /* I honestly don't know if it is considered OK to fork in other
50      * people's programs. We need a SUID worker, though, so there's
51      * little choice that I can see. */
52     if((child = fork()) < 0) {
53         *errnop = errno;
54         *h_errnop = NETDB_INTERNAL;
55         return(NSS_STATUS_UNAVAIL);
56     }
57     
58     if(child == 0) {
59         int i, fd;
60         
61         if((fd = open("/dev/null", O_WRONLY)) < 0)
62             exit(127);
63         close(pfd[0]);
64         dup2(pfd[1], 1);
65         dup2(fd, 2);
66         for(i = 3; i < FD_SETSIZE; i++)
67             close(i);
68         
69         execlp("idnlookup", "idnlookup", addrbuf, NULL);
70         exit(127);
71     }
72     
73     close(pfd[1]);
74     
75     rl = 0;
76     do {
77         ret = read(pfd[0], addrbuf + rl, sizeof(addrbuf) - rl);
78         if(ret < 0) {
79             *errnop = errno;
80             *h_errnop = NETDB_INTERNAL;
81             close(pfd[0]);
82             return(NSS_STATUS_UNAVAIL);
83         }
84         rl += ret;
85         if(rl >= sizeof(addrbuf) - 1) {
86             *errnop = ENOMEM;
87             *h_errnop = NETDB_INTERNAL;
88             close(pfd[0]);
89             return(NSS_STATUS_UNAVAIL);
90         }
91     } while(ret != 0);
92     addrbuf[rl] = 0;
93     close(pfd[0]);
94     
95     an = 0;
96     p = addrbuf;
97     p3 = buffer + sizeof(*retbuf);
98     while((p2 = strchr(p, '\n')) != NULL) {
99         *p2 = 0;
100         thislen = p2 - p;
101         if(thislen == 0)
102             continue;
103         if((p3 - buffer) + thislen + 1 > buflen) {
104             *errnop = ENOMEM;
105             *h_errnop = NETDB_INTERNAL;
106             return(NSS_STATUS_UNAVAIL);
107         }
108         memcpy(p3, p, thislen + 1);
109         retbuf->aliaslist[an] = p3;
110         p3 += thislen + 1;
111         p = p2 + 1;
112         if(++an == 16) {
113             *errnop = ENOMEM;
114             *h_errnop = NETDB_INTERNAL;
115             return(NSS_STATUS_UNAVAIL);
116         }
117     }
118     if(an == 0) {
119         *h_errnop = TRY_AGAIN; /* XXX: Is this correct? */
120         return(NSS_STATUS_NOTFOUND);
121     }
122     retbuf->aliaslist[an] = NULL;
123     
124     memcpy(retbuf->retaddr, addr, len);
125     retbuf->addrlist[0] = retbuf->retaddr;
126     retbuf->addrlist[1] = NULL;
127     result->h_name = retbuf->aliaslist[0];
128     result->h_aliases = retbuf->aliaslist;
129     result->h_addr_list = retbuf->addrlist;
130     result->h_addrtype = af;
131     result->h_length = len;
132     
133     *h_errnop = NETDB_SUCCESS;
134     return(NSS_STATUS_SUCCESS);
135 }