Incorporate make system changes by Oron Peled.
[icmp-dn.git] / src / icmpdnd.c
1 /*
2  *  icmpdnd - ICMP Domain Name responder daemon for Linux
3  *  Copyright (C) 2005 Fredrik Tolf <fredrik@dolda2000.com>
4  *  
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *  
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *  
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <syslog.h>
25 #include <time.h>
26 #include <sys/socket.h>
27 #include <sys/poll.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <netinet/ip.h>
31 #include <sys/types.h>
32 #include "config.h"
33
34 struct icmphdr {
35     u_int8_t type;
36     u_int8_t code;
37     u_int16_t checksum;
38 };
39
40 struct reqhdr {
41     u_int8_t type;
42     u_int8_t code;
43     u_int16_t checksum;
44     u_int16_t id;
45     u_int16_t seq;
46 };
47
48 struct rephdr {
49     u_int8_t type;
50     u_int8_t code;
51     u_int16_t checksum;
52     u_int16_t id;
53     u_int16_t seq;
54     int32_t ttl;
55 };
56
57 #define ICMP_NAMEREQ 37
58 #define ICMP_NAMEREP 38
59
60 volatile int alive;
61
62 size_t filldn(char *dst)
63 {
64     char *p, *p2, *dp;
65     char namebuf[1024];
66     int hl;
67     
68     if(gethostname(namebuf, sizeof(namebuf)) < 0) {
69         perror("gethostname");
70         exit(1);
71     }
72     hl = strlen(namebuf);
73     namebuf[hl++] = '.';
74     if(getdomainname(namebuf + hl, sizeof(namebuf) - hl) < 0) {
75         perror("getdomainname");
76         exit(1);
77     }
78     if(strlen(namebuf + hl) != 0) {
79         hl = strlen(namebuf);
80         namebuf[hl++] = '.';
81     }
82     namebuf[hl] = 0;
83     
84     p = namebuf;
85     dp = dst;
86     while((p2 = strchr(p, '.')) != NULL) {
87         *p2 = 0;
88         *(dp++) = p2 - p;
89         memcpy(dp, p, p2 - p);
90         dp += p2 - p;
91         p = p2 + 1;
92     }
93     *(dp++) = 0;
94     
95     return(dp - dst);
96 }
97
98 void cksum(void *hdr, size_t len)
99 {
100     struct icmphdr *ih;
101     u_int8_t *cb;
102     int i;
103     int b1, b2;
104     
105     ih = (struct icmphdr *)hdr;
106     cb = (u_int8_t *)hdr;
107     ih->checksum = 0;
108     b1 = b2 = 0;
109     for(i = 0; i < (len & ~1); i += 2) {
110         b1 += cb[i];
111         b2 += cb[i + 1];
112     }
113     if(i & 1)
114         b1 += cb[len - 1];
115     while(1) {
116         if(b1 >= 256) {
117             b2 += b1 >> 8;
118             b1 &= 0xff;
119             continue;
120         }
121         if(b2 >= 256) {
122             b1 += b2 >> 8;
123             b2 &= 0xff;
124             continue;
125         }
126         break;
127     }
128     cb = (u_int8_t *)&ih->checksum;
129     cb[0] = ~(u_int8_t)b1;
130     cb[1] = ~(u_int8_t)b2;
131 }
132
133 int main(int argc, char **argv)
134 {
135     int i, n, ret;
136     int c, cs, s4, s6, datalen;
137     int daemonize, ttl;
138     unsigned char buf[65536];
139     struct sockaddr_storage name;
140     struct reqhdr req;
141     struct rephdr rep;
142     struct iphdr iphdr;
143     struct msghdr mhdr;
144     struct iovec iov;
145     char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
146     size_t hdrlen;
147     struct pollfd pfd[2];
148     time_t curtime, lasterr;
149     
150     daemonize = 1;
151     ttl = 3600;
152     while((c = getopt(argc, argv, "nht:")) != -1) {
153         switch(c) {
154         case 't':
155             ttl = atoi(optarg);
156             break;
157         case 'n':
158             daemonize = 0;
159             break;
160         case 'h':
161         case '?':
162         case ':':
163         default:
164             fprintf(stderr, "usage: icmpdnd [-n]");
165             exit((c == 'h')?0:1);
166         }
167     }
168     
169     s4 = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
170     s6 = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMP);
171     if((s4 < 0) && (s6 < 0)) {
172         perror("could not open raw socket");
173         exit(1);
174     }
175     if(s6 >= 0) {
176         i = 1;
177         if(setsockopt(s6, IPPROTO_IPV6, IPV6_PKTINFO, &i, sizeof(i))) {
178             perror("could not set IPV6_PKTINFO sockopt");
179             exit(1);
180         }
181     }
182     
183     if(daemonize)
184         daemon(0, 0);
185     
186     openlog("icmpdnd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
187     
188     alive = 1;
189     lasterr = 0;
190     while(alive) {
191         n = 0;
192         if(s4 >= 0) {
193             pfd[n].fd = s4;
194             pfd[n].events = POLLIN;
195             n++;
196         }
197         if(s6 >= 0) {
198             pfd[n].fd = s6;
199             pfd[n].events = POLLIN;
200             n++;
201         }
202         ret = poll(pfd, n, -1);
203
204         curtime = time(NULL);
205         if(ret < 0) {
206             if(errno == EINTR)
207                 continue;
208             syslog(LOG_ERR, "error while polling sockets: %m");
209             if(lasterr == curtime) {
210                 syslog(LOG_CRIT, "exiting due to repeated errors");
211                 exit(1);
212             }
213             lasterr = curtime;
214         }
215         
216         for(i = 0; i < n; i++) {
217             if((pfd[i].revents & POLLIN) == 0)
218                 continue;
219             cs = pfd[i].fd;
220             memset(&name, 0, sizeof(name));
221             
222             iov.iov_len = sizeof(buf);
223             iov.iov_base = buf;
224             mhdr.msg_name = &name;
225             mhdr.msg_namelen = sizeof(name);
226             mhdr.msg_iov = &iov;
227             mhdr.msg_iovlen = 1;
228             mhdr.msg_control = cmsgbuf;
229             mhdr.msg_controllen = sizeof(cmsgbuf);
230             
231             ret = recvmsg(cs, &mhdr, 0);
232             if(ret < 0) {
233                 syslog(LOG_WARNING, "error while receiving datagram: %m");
234                 continue;
235             }
236             
237             if(cs == s4) {
238                 if(ret < sizeof(iphdr) + sizeof(req))
239                     continue;
240                 hdrlen = sizeof(iphdr);
241                 memcpy(&iphdr, buf, sizeof(iphdr));
242                 if(iphdr.protocol != IPPROTO_ICMP)
243                     continue;
244                 mhdr.msg_control = NULL;
245                 mhdr.msg_controllen = 0;
246             } else if(cs == s6) {
247                 if(ret < sizeof(req))
248                     continue;
249                 ((struct sockaddr_in6 *)&name)->sin6_port = 0;
250                 hdrlen = 0;
251                 /* Just keep mhdr.msg_control. */
252             } else {
253                 syslog(LOG_CRIT, "strangeness!");
254                 abort();
255             }
256             memcpy(&req, buf + hdrlen, sizeof(req));
257             if(req.type != ICMP_NAMEREQ)
258                 continue;
259             rep.type = ICMP_NAMEREP;
260             rep.code = 0;
261             rep.id = req.id;
262             rep.seq = req.seq;
263             rep.ttl = htonl(ttl);
264             memcpy(buf, &rep, sizeof(rep));
265             datalen = filldn(buf + sizeof(rep));
266         
267             cksum(buf, datalen + sizeof(rep));
268             
269             iov.iov_len = sizeof(rep) + datalen;
270             iov.iov_base = buf;
271             mhdr.msg_iov = &iov;
272             mhdr.msg_iovlen = 1;
273             ret = sendmsg(cs, &mhdr, 0);
274             if(ret < 0)
275                 syslog(LOG_WARNING, "error in sending reply: %m");
276         }
277     }
278     
279     close(s4);
280     close(s6);
281     return(0);
282 }
283
284 /*
285  * Local Variables:
286  * compile-command: "gcc -Wall -g -o icmpdnd icmpdnd.c"
287  * End:
288  */