Check for error return from getaddrinfo...
[icmp-dn.git] / src / idnlookup.c
1 /*
2  *  idnlookup - ICMP Domain Name lookup utility for Linux
3  *  Should be installed SUID root, even though I don't know if it's secure yet. :-)
4  *  Copyright (C) 2005 Fredrik Tolf <fredrik@dolda2000.com>
5  *  
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *  
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *  
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <netdb.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <netinet/ip.h>
29 #include <netinet/ip6.h>
30 #include <arpa/inet.h>
31 #include <sys/types.h>
32 #include <sys/poll.h>
33 #include <sys/time.h>
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "icmpdefs.h"
39
40 unsigned char buf[65536];
41
42 /* DN decompression not yet implemented, since I don't know where to
43  * begin counting the offset from -- the beginning of the ICMP
44  * payload, or from the beginning of the DN data buffer? */
45 void printdn(FILE *f, unsigned char *dnbuf, size_t size, int onlyfirst)
46 {
47     unsigned char *p;
48     
49     p = dnbuf;
50     while(p - dnbuf < size) {
51         while(*p != 0) {
52             if(*p & 0xc0) {
53                 fprintf(stderr, "domain name decompression not implemented, aborting\n");
54                 exit(1);
55             }
56             if((int)*p + (p - dnbuf) >= size) {
57                 fprintf(stderr, "malformed domain name\n");
58                 return;
59             }
60             fprintf(f, "%.*s", (int)*p, p + 1);
61             p += 1 + (int)*p;
62             if(*p != 0)
63                 fprintf(f, ".");
64         }
65         p++;
66         fprintf(f, "\n");
67         if(onlyfirst)
68             break;
69     }
70 }
71
72 void cksum(void *hdr, size_t len)
73 {
74     struct icmphdr *ih;
75     u_int8_t *cb;
76     int i;
77     int b1, b2;
78     
79     ih = (struct icmphdr *)hdr;
80     cb = (u_int8_t *)hdr;
81     ih->checksum = 0;
82     b1 = b2 = 0;
83     for(i = 0; i < (len & ~1); i += 2) {
84         b1 += cb[i];
85         b2 += cb[i + 1];
86     }
87     if(i & 1)
88         b1 += cb[len - 1];
89     while(1) {
90         if(b1 >= 256) {
91             b2 += b1 >> 8;
92             b1 &= 0xff;
93             continue;
94         }
95         if(b2 >= 256) {
96             b1 += b2 >> 8;
97             b2 &= 0xff;
98             continue;
99         }
100         break;
101     }
102     cb = (u_int8_t *)&ih->checksum;
103     cb[0] = ~(u_int8_t)b1;
104     cb[1] = ~(u_int8_t)b2;
105 }
106
107 void usage(void)
108 {
109     fprintf(stderr, "usage: idnlookup [-hTa] [-t timeout] host\n");
110 }
111
112 int main(int argc, char **argv)
113 {
114     int ret;
115     int s, c;
116     int id;
117     int namelen;
118     struct reqhdr req;
119     struct rephdr rep;
120     struct iphdr iphdr;
121     size_t hdrlen;
122     struct addrinfo *ai, *cai, aihint;
123     struct pollfd pfd;
124     struct timeval tvb, tvc;
125     struct sockaddr_storage name;
126     int timeout, dispttl, onlyfirst;
127     int elapsed, timedout, found;
128     
129     timeout = 3000;
130     dispttl = 0;
131     onlyfirst = 1;
132     while((c = getopt(argc, argv, "haTt:")) != -1) {
133         switch(c) {
134         case 't':
135             timeout = atoi(optarg);
136             break;
137         case 'a':
138             onlyfirst = 0;
139             break;
140         case 'T':
141             dispttl = 1;
142             break;
143         case 'h':
144         case '?':
145         case ':':
146         default:
147             usage();
148             exit((c == 'h')?0:1);
149         }
150     }
151     
152     if(argc - optind < 1) {
153         usage();
154         exit(1);
155     }
156     
157     memset(&aihint, 0, sizeof(aihint));
158     aihint.ai_socktype = SOCK_RAW;
159     aihint.ai_protocol = IPPROTO_ICMP;
160     if((ret = getaddrinfo(argv[optind], NULL, &aihint, &ai)) != 0) {
161         fprintf(stderr, "%s: %s\n", argv[optind], gai_strerror(ret));
162         exit(1);
163     }
164     
165     for(cai = ai; cai != NULL; cai = cai->ai_next) {
166         if((s = socket(cai->ai_family, SOCK_RAW, IPPROTO_ICMP)) < 0) {
167             perror("could not create raw socket");
168             exit(1);
169         }
170         
171         id = random() % 65536;
172         memset(&req, 0, sizeof(req));
173         req.type = ICMP_NAMEREQ;
174         req.id = htons(id);
175         cksum(&req, sizeof(req));
176         
177         ret = sendto(s, &req, sizeof(req), 0, cai->ai_addr, cai->ai_addrlen);
178         if(ret < 0) {
179             perror("sendto");
180             exit(1);
181         } else if(ret != sizeof(req)) {
182             fprintf(stderr, "socket would not send entire packet\n");
183             exit(1);
184         }
185         
186         timedout = 0;
187         found = 0;
188         gettimeofday(&tvb, NULL);
189         while(1) {
190             pfd.fd = s;
191             pfd.events = POLLIN;
192             gettimeofday(&tvc, NULL);
193             elapsed = ((tvc.tv_sec - tvb.tv_sec) * 1000) + ((tvc.tv_usec - tvb.tv_usec) / 1000);
194             if(elapsed >= timeout) {
195                 timedout = 1;
196                 break;
197             }
198             ret = poll(&pfd, 1, timeout - elapsed);
199             if(ret < 0) {
200                 perror("idnlookup: reading data");
201                 exit(1);
202             }
203             
204             if(pfd.revents & POLLIN) {
205                 namelen = sizeof(name);
206                 ret = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&name, &namelen);
207                 if(ret < 0) {
208                     perror("idnlookup: receiving data");
209                     exit(1);
210                 }
211                 
212                 if(name.ss_family != cai->ai_addr->sa_family)
213                     continue;
214                 if(name.ss_family == AF_INET) {
215                     if(memcmp(&(((struct sockaddr_in *)&name)->sin_addr), &(((struct sockaddr_in *)cai->ai_addr)->sin_addr), sizeof(struct in_addr)))
216                         continue;
217                     if(ret < sizeof(iphdr) + sizeof(rep))
218                         continue;
219                     hdrlen = sizeof(iphdr);
220                     memcpy(&iphdr, buf, sizeof(iphdr));
221                     if(iphdr.protocol != IPPROTO_ICMP)
222                         continue;
223                 } else if(name.ss_family == AF_INET6) {
224                     if(memcmp(&(((struct sockaddr_in6 *)&name)->sin6_addr), &(((struct sockaddr_in6 *)cai->ai_addr)->sin6_addr), sizeof(struct in6_addr)))
225                         continue;
226                     if(ret < sizeof(rep))
227                         continue;
228                     hdrlen = 0;
229                 } else {
230                     continue;
231                 }
232                 
233                 memcpy(&rep, buf + hdrlen, sizeof(rep));
234                 if(rep.type != ICMP_NAMEREP)
235                     continue;
236                 if((ntohs(rep.id) != id) || (ntohs(rep.seq != 0)))
237                     continue;
238                 
239                 found = 1;
240                 break;
241             }
242         }
243         
244         close(s);
245         
246         if(found) {
247             if(dispttl)
248                 printf("%i\n", ntohl(rep.ttl));
249             printdn(stdout, buf + hdrlen + sizeof(rep), ret - hdrlen - sizeof(rep), onlyfirst);
250             break;
251         }
252     }
253     
254     if(timedout) {
255         fprintf(stderr, "idnlookup: timeout\n");
256         exit(1);
257     }
258     
259     return(0);
260 }
261
262 /*
263  * Local Variables:
264  * compile-command: "make CFLAGS='-Wall -g'"
265  * End:
266  */