X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=icmpdnd.c;h=1dfb0286b5be139234e2d7a5d4c97c4388ff6219;hb=df4e01a579663cd726eb2b3c2d5d8800cf2c3f0c;hp=0169e6daf552da7e759c03cc8c0f6d87442ecc90;hpb=fa6ac2fc3180a275d59bff8da4e4883fd40a5de7;p=icmp-dn.git diff --git a/icmpdnd.c b/icmpdnd.c index 0169e6d..1dfb028 100644 --- a/icmpdnd.c +++ b/icmpdnd.c @@ -1,6 +1,6 @@ /* * icmpdnd - ICMP Domain Name responder daemon for Linux - * Copyright (C) 2004 Fredrik Tolf + * Copyright (C) 2005 Fredrik Tolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -53,8 +56,6 @@ struct rephdr { #define ICMP_NAMEREQ 37 #define ICMP_NAMEREP 38 -#define TTL 3600 - volatile int alive; size_t filldn(char *dst) @@ -130,56 +131,151 @@ void cksum(void *hdr, size_t len) int main(int argc, char **argv) { - int ret; - int s, namelen, datalen; + int i, n, ret; + int c, cs, s4, s6, datalen; + int daemonize, ttl; unsigned char buf[65536]; - struct sockaddr_in name; + struct sockaddr_storage name; struct reqhdr req; struct rephdr rep; struct iphdr iphdr; - - if((s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { - perror("could not create raw socket"); + struct msghdr mhdr; + struct iovec iov; + char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + size_t hdrlen; + struct pollfd pfd[2]; + time_t curtime, lasterr; + + daemonize = 1; + ttl = 3600; + while((c = getopt(argc, argv, "nht:")) != -1) { + switch(c) { + case 't': + ttl = atoi(optarg); + break; + case 'n': + daemonize = 0; + break; + case 'h': + case '?': + case ':': + default: + fprintf(stderr, "usage: icmpdnd [-n]"); + exit((c == 'h')?0:1); + } + } + + s4 = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); + s6 = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMP); + if((s4 < 0) && (s6 < 0)) { + perror("could not open raw socket"); exit(1); } + if(s6 >= 0) { + i = 1; + if(setsockopt(s6, IPPROTO_IPV6, IPV6_PKTINFO, &i, sizeof(i))) { + perror("could not set IPV6_PKTINFO sockopt"); + exit(1); + } + } + + if(daemonize) + daemon(0, 0); + + openlog("icmpdnd", LOG_PID | LOG_NDELAY, LOG_DAEMON); alive = 1; + lasterr = 0; while(alive) { - namelen = sizeof(name); - ret = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&name, &namelen); + n = 0; + if(s4 >= 0) { + pfd[n].fd = s4; + pfd[n].events = POLLIN; + n++; + } + if(s6 >= 0) { + pfd[n].fd = s6; + pfd[n].events = POLLIN; + n++; + } + ret = poll(pfd, n, -1); + + curtime = time(NULL); if(ret < 0) { if(errno == EINTR) continue; - perror("recvfrom"); - exit(1); + syslog(LOG_ERR, "error while polling sockets: %m"); + if(lasterr == curtime) { + syslog(LOG_CRIT, "exiting due to repeated errors"); + exit(1); + } + lasterr = curtime; } - if(ret < sizeof(iphdr)) - continue; - memcpy(&iphdr, buf, sizeof(iphdr)); - if(iphdr.protocol != IPPROTO_ICMP) - continue; - if(ret < sizeof(iphdr) + sizeof(req)) - continue; - memcpy(&req, buf + sizeof(iphdr), sizeof(req)); - if(req.type != ICMP_NAMEREQ) - continue; - rep.type = ICMP_NAMEREP; - rep.code = 0; - rep.id = req.id; - rep.seq = req.seq; - rep.ttl = htonl(TTL); - memcpy(buf, &rep, sizeof(rep)); - datalen = filldn(buf + sizeof(rep)); - - cksum(buf, datalen + sizeof(rep)); - - ret = sendto(s, buf, datalen + sizeof(rep), 0, (struct sockaddr *)&name, namelen); - if(ret < 0) { - perror("sendto"); - exit(1); + + for(i = 0; i < n; i++) { + if((pfd[i].revents & POLLIN) == 0) + continue; + cs = pfd[i].fd; + memset(&name, 0, sizeof(name)); + + iov.iov_len = sizeof(buf); + iov.iov_base = buf; + mhdr.msg_name = &name; + mhdr.msg_namelen = sizeof(name); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = cmsgbuf; + mhdr.msg_controllen = sizeof(cmsgbuf); + + ret = recvmsg(cs, &mhdr, 0); + if(ret < 0) { + syslog(LOG_WARNING, "error while receiving datagram: %m"); + continue; + } + + if(cs == s4) { + if(ret < sizeof(iphdr) + sizeof(req)) + continue; + hdrlen = sizeof(iphdr); + memcpy(&iphdr, buf, sizeof(iphdr)); + if(iphdr.protocol != IPPROTO_ICMP) + continue; + mhdr.msg_control = NULL; + mhdr.msg_controllen = 0; + } else if(cs == s6) { + if(ret < sizeof(req)) + continue; + ((struct sockaddr_in6 *)&name)->sin6_port = 0; + hdrlen = 0; + /* Just keep mhdr.msg_control. */ + } else { + syslog(LOG_CRIT, "strangeness!"); + abort(); + } + memcpy(&req, buf + hdrlen, sizeof(req)); + if(req.type != ICMP_NAMEREQ) + continue; + rep.type = ICMP_NAMEREP; + rep.code = 0; + rep.id = req.id; + rep.seq = req.seq; + rep.ttl = htonl(ttl); + memcpy(buf, &rep, sizeof(rep)); + datalen = filldn(buf + sizeof(rep)); + + cksum(buf, datalen + sizeof(rep)); + + iov.iov_len = sizeof(rep) + datalen; + iov.iov_base = buf; + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + ret = sendmsg(cs, &mhdr, 0); + if(ret < 0) + syslog(LOG_WARNING, "error in sending reply: %m"); } } - close(s); + close(s4); + close(s6); return(0); }