First should-be-working version.
[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;
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         if((p3 - buffer) + (p2 - p) + 1 > buflen) {
101             *errnop = ENOMEM;
102             *h_errnop = NETDB_INTERNAL;
103             return(NSS_STATUS_UNAVAIL);
104         }
105         memcpy(p3, p, (p2 - p) + 1);
106         retbuf->aliaslist[an] = p3;
107         p3 += (p2 - p) + 1;
108         p = p2 + 1;
109         if(++an == 16) {
110             *errnop = ENOMEM;
111             *h_errnop = NETDB_INTERNAL;
112             return(NSS_STATUS_UNAVAIL);
113         }
114     }
115     if(an == 0) {
116         *h_errnop = TRY_AGAIN; /* Is this correct? */
117         return(NSS_STATUS_NOTFOUND);
118     }
119     retbuf->aliaslist[an] = NULL;
120     
121     memcpy(retbuf->retaddr, addr, len);
122     retbuf->addrlist[0] = retbuf->retaddr;
123     retbuf->addrlist[1] = NULL;
124     result->h_name = retbuf->aliaslist[0];
125     result->h_aliases = retbuf->aliaslist;
126     result->h_addr_list = retbuf->addrlist;
127     result->h_addrtype = af;
128     result->h_length = len;
129     
130     *h_errnop = NETDB_SUCCESS;
131     return(NSS_STATUS_SUCCESS);
132 }