Shouldn't exit on send failure.
[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 <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <netinet/ip.h>
29 #include <sys/types.h>
30
31 struct icmphdr {
32     u_int8_t type;
33     u_int8_t code;
34     u_int16_t checksum;
35 };
36
37 struct reqhdr {
38     u_int8_t type;
39     u_int8_t code;
40     u_int16_t checksum;
41     u_int16_t id;
42     u_int16_t seq;
43 };
44
45 struct rephdr {
46     u_int8_t type;
47     u_int8_t code;
48     u_int16_t checksum;
49     u_int16_t id;
50     u_int16_t seq;
51     int32_t ttl;
52 };
53
54 #define ICMP_NAMEREQ 37
55 #define ICMP_NAMEREP 38
56
57 volatile int alive;
58
59 size_t filldn(char *dst)
60 {
61     char *p, *p2, *dp;
62     char namebuf[1024];
63     int hl;
64     
65     if(gethostname(namebuf, sizeof(namebuf)) < 0) {
66         perror("gethostname");
67         exit(1);
68     }
69     hl = strlen(namebuf);
70     namebuf[hl++] = '.';
71     if(getdomainname(namebuf + hl, sizeof(namebuf) - hl) < 0) {
72         perror("getdomainname");
73         exit(1);
74     }
75     if(strlen(namebuf + hl) != 0) {
76         hl = strlen(namebuf);
77         namebuf[hl++] = '.';
78     }
79     namebuf[hl] = 0;
80     
81     p = namebuf;
82     dp = dst;
83     while((p2 = strchr(p, '.')) != NULL) {
84         *p2 = 0;
85         *(dp++) = p2 - p;
86         memcpy(dp, p, p2 - p);
87         dp += p2 - p;
88         p = p2 + 1;
89     }
90     *(dp++) = 0;
91     
92     return(dp - dst);
93 }
94
95 void cksum(void *hdr, size_t len)
96 {
97     struct icmphdr *ih;
98     u_int8_t *cb;
99     int i;
100     int b1, b2;
101     
102     ih = (struct icmphdr *)hdr;
103     cb = (u_int8_t *)hdr;
104     ih->checksum = 0;
105     b1 = b2 = 0;
106     for(i = 0; i < (len & ~1); i += 2) {
107         b1 += cb[i];
108         b2 += cb[i + 1];
109     }
110     if(i & 1)
111         b1 += cb[len - 1];
112     while(1) {
113         if(b1 >= 256) {
114             b2 += b1 >> 8;
115             b1 &= 0xff;
116             continue;
117         }
118         if(b2 >= 256) {
119             b1 += b2 >> 8;
120             b2 &= 0xff;
121             continue;
122         }
123         break;
124     }
125     cb = (u_int8_t *)&ih->checksum;
126     cb[0] = ~(u_int8_t)b1;
127     cb[1] = ~(u_int8_t)b2;
128 }
129
130 int main(int argc, char **argv)
131 {
132     int ret;
133     int c, s, namelen, datalen;
134     int daemonize, ttl;
135     unsigned char buf[65536];
136     struct sockaddr_in name;
137     struct reqhdr req;
138     struct rephdr rep;
139     struct iphdr iphdr;
140     
141     daemonize = 1;
142     ttl = 3600;
143     while((c = getopt(argc, argv, "nht:")) != -1) {
144         switch(c) {
145         case 't':
146             ttl = atoi(optarg);
147             break;
148         case 'n':
149             daemonize = 0;
150             break;
151         case 'h':
152         case '?':
153         case ':':
154         default:
155             fprintf(stderr, "usage: icmpdnd [-n]");
156             exit((c == 'h')?0:1);
157         }
158     }
159     
160     if((s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
161         perror("could not create raw socket");
162         exit(1);
163     }
164     
165     if(daemonize)
166         daemon(0, 0);
167     
168     openlog("icmpdnd", LOG_PID, LOG_DAEMON);
169     
170     alive = 1;
171     while(alive) {
172         namelen = sizeof(name);
173         ret = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&name, &namelen);
174         if(ret < 0) {
175             if(errno == EINTR)
176                 continue;
177             syslog(LOG_ERR, "error in receiving datagram: %m");
178             exit(1);
179         }
180         if(ret < sizeof(iphdr) + sizeof(req))
181             continue;
182         memcpy(&iphdr, buf, sizeof(iphdr));
183         memcpy(&req, buf + sizeof(iphdr), sizeof(req));
184         if(iphdr.protocol != IPPROTO_ICMP)
185             continue;
186         if(req.type != ICMP_NAMEREQ)
187             continue;
188         rep.type = ICMP_NAMEREP;
189         rep.code = 0;
190         rep.id = req.id;
191         rep.seq = req.seq;
192         rep.ttl = htonl(ttl);
193         memcpy(buf, &rep, sizeof(rep));
194         datalen = filldn(buf + sizeof(rep));
195         
196         cksum(buf, datalen + sizeof(rep));
197         
198         /* XXX: The correct source address needs to be filled in from
199          * the request's destination address. */
200         ret = sendto(s, buf, datalen + sizeof(rep), 0, (struct sockaddr *)&name, namelen);
201         if(ret < 0)
202             syslog(LOG_WARNING, "error in sending reply: %m");
203     }
204     
205     close(s);
206     return(0);
207 }