Fill in the source address properly.
[icmp-dn.git] / icmpdnd.c
index 0169e6d..1dfb028 100644 (file)
--- a/icmpdnd.c
+++ b/icmpdnd.c
@@ -1,6 +1,6 @@
 /*
  *  icmpdnd - ICMP Domain Name responder daemon for Linux
- *  Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
+ *  Copyright (C) 2005 Fredrik Tolf <fredrik@dolda2000.com>
  *  
  *  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
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <syslog.h>
+#include <time.h>
 #include <sys/socket.h>
+#include <sys/poll.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netinet/ip.h>
@@ -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);
 }