Fill in the source address properly.
[icmp-dn.git] / 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
33 struct icmphdr {
34     u_int8_t type;
35     u_int8_t code;
36     u_int16_t checksum;
37 };
38
39 struct reqhdr {
40     u_int8_t type;
41     u_int8_t code;
42     u_int16_t checksum;
43     u_int16_t id;
44     u_int16_t seq;
45 };
46
47 struct rephdr {
48     u_int8_t type;
49     u_int8_t code;
50     u_int16_t checksum;
51     u_int16_t id;
52     u_int16_t seq;
53     int32_t ttl;
54 };
55
56 #define ICMP_NAMEREQ 37
57 #define ICMP_NAMEREP 38
58
59 volatile int alive;
60
61 size_t filldn(char *dst)
62 {
63     char *p, *p2, *dp;
64     char namebuf[1024];
65     int hl;
66     
67     if(gethostname(namebuf, sizeof(namebuf)) < 0) {
68         perror("gethostname");
69         exit(1);
70     }
71     hl = strlen(namebuf);
72     namebuf[hl++] = '.';
73     if(getdomainname(namebuf + hl, sizeof(namebuf) - hl) < 0) {
74         perror("getdomainname");
75         exit(1);
76     }
77     if(strlen(namebuf + hl) != 0) {
78         hl = strlen(namebuf);
79         namebuf[hl++] = '.';
80     }
81     namebuf[hl] = 0;
82     
83     p = namebuf;
84     dp = dst;
85     while((p2 = strchr(p, '.')) != NULL) {
86         *p2 = 0;
87         *(dp++) = p2 - p;
88         memcpy(dp, p, p2 - p);
89         dp += p2 - p;
90         p = p2 + 1;
91     }
92     *(dp++) = 0;
93     
94     return(dp - dst);
95 }
96
97 void cksum(void *hdr, size_t len)
98 {
99     struct icmphdr *ih;
100     u_int8_t *cb;
101     int i;
102     int b1, b2;
103     
104     ih = (struct icmphdr *)hdr;
105     cb = (u_int8_t *)hdr;
106     ih->checksum = 0;
107     b1 = b2 = 0;
108     for(i = 0; i < (len & ~1); i += 2) {
109         b1 += cb[i];
110         b2 += cb[i + 1];
111     }
112     if(i & 1)
113         b1 += cb[len - 1];
114     while(1) {
115         if(b1 >= 256) {
116             b2 += b1 >> 8;
117             b1 &= 0xff;
118             continue;
119         }
120         if(b2 >= 256) {
121             b1 += b2 >> 8;
122             b2 &= 0xff;
123             continue;
124         }
125         break;
126     }
127     cb = (u_int8_t *)&ih->checksum;
128     cb[0] = ~(u_int8_t)b1;
129     cb[1] = ~(u_int8_t)b2;
130 }
131
132 int main(int argc, char **argv)
133 {
134     int i, n, ret;
135     int c, cs, s4, s6, datalen;
136     int daemonize, ttl;
137     unsigned char buf[65536];
138     struct sockaddr_storage name;
139     struct reqhdr req;
140     struct rephdr rep;
141     struct iphdr iphdr;
142     struct msghdr mhdr;
143     struct iovec iov;
144     char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
145     size_t hdrlen;
146     struct pollfd pfd[2];
147     time_t curtime, lasterr;
148     
149     daemonize = 1;
150     ttl = 3600;
151     while((c = getopt(argc, argv, "nht:")) != -1) {
152         switch(c) {
153         case 't':
154             ttl = atoi(optarg);
155             break;
156         case 'n':
157             daemonize = 0;
158             break;
159         case 'h':
160         case '?':
161         case ':':
162         default:
163             fprintf(stderr, "usage: icmpdnd [-n]");
164             exit((c == 'h')?0:1);
165         }
166     }
167     
168     s4 = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
169     s6 = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMP);
170     if((s4 < 0) && (s6 < 0)) {
171         perror("could not open raw socket");
172         exit(1);
173     }
174     if(s6 >= 0) {
175         i = 1;
176         if(setsockopt(s6, IPPROTO_IPV6, IPV6_PKTINFO, &i, sizeof(i))) {
177             perror("could not set IPV6_PKTINFO sockopt");
178             exit(1);
179         }
180     }
181     
182     if(daemonize)
183         daemon(0, 0);
184     
185     openlog("icmpdnd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
186     
187     alive = 1;
188     lasterr = 0;
189     while(alive) {
190         n = 0;
191         if(s4 >= 0) {
192             pfd[n].fd = s4;
193             pfd[n].events = POLLIN;
194             n++;
195         }
196         if(s6 >= 0) {
197             pfd[n].fd = s6;
198             pfd[n].events = POLLIN;
199             n++;
200         }
201         ret = poll(pfd, n, -1);
202
203         curtime = time(NULL);
204         if(ret < 0) {
205             if(errno == EINTR)
206                 continue;
207             syslog(LOG_ERR, "error while polling sockets: %m");
208             if(lasterr == curtime) {
209                 syslog(LOG_CRIT, "exiting due to repeated errors");
210                 exit(1);
211             }
212             lasterr = curtime;
213         }
214         
215         for(i = 0; i < n; i++) {
216             if((pfd[i].revents & POLLIN) == 0)
217                 continue;
218             cs = pfd[i].fd;
219             memset(&name, 0, sizeof(name));
220             
221             iov.iov_len = sizeof(buf);
222             iov.iov_base = buf;
223             mhdr.msg_name = &name;
224             mhdr.msg_namelen = sizeof(name);
225             mhdr.msg_iov = &iov;
226             mhdr.msg_iovlen = 1;
227             mhdr.msg_control = cmsgbuf;
228             mhdr.msg_controllen = sizeof(cmsgbuf);
229             
230             ret = recvmsg(cs, &mhdr, 0);
231             if(ret < 0) {
232                 syslog(LOG_WARNING, "error while receiving datagram: %m");
233                 continue;
234             }
235             
236             if(cs == s4) {
237                 if(ret < sizeof(iphdr) + sizeof(req))
238                     continue;
239                 hdrlen = sizeof(iphdr);
240                 memcpy(&iphdr, buf, sizeof(iphdr));
241                 if(iphdr.protocol != IPPROTO_ICMP)
242                     continue;
243                 mhdr.msg_control = NULL;
244                 mhdr.msg_controllen = 0;
245             } else if(cs == s6) {
246                 if(ret < sizeof(req))
247                     continue;
248                 ((struct sockaddr_in6 *)&name)->sin6_port = 0;
249                 hdrlen = 0;
250                 /* Just keep mhdr.msg_control. */
251             } else {
252                 syslog(LOG_CRIT, "strangeness!");
253                 abort();
254             }
255             memcpy(&req, buf + hdrlen, sizeof(req));
256             if(req.type != ICMP_NAMEREQ)
257                 continue;
258             rep.type = ICMP_NAMEREP;
259             rep.code = 0;
260             rep.id = req.id;
261             rep.seq = req.seq;
262             rep.ttl = htonl(ttl);
263             memcpy(buf, &rep, sizeof(rep));
264             datalen = filldn(buf + sizeof(rep));
265         
266             cksum(buf, datalen + sizeof(rep));
267             
268             iov.iov_len = sizeof(rep) + datalen;
269             iov.iov_base = buf;
270             mhdr.msg_iov = &iov;
271             mhdr.msg_iovlen = 1;
272             ret = sendmsg(cs, &mhdr, 0);
273             if(ret < 0)
274                 syslog(LOG_WARNING, "error in sending reply: %m");
275         }
276     }
277     
278     close(s4);
279     close(s6);
280     return(0);
281 }