/* Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman * Copyright (c) 2007 Albert Lee . * Copyright (c) 2011-2012 Julian Kranz * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WINDOWS32 #include #include #else #include #ifdef DARWIN #define BIND_8_COMPAT #include #endif #include #include #include #include #include #endif #ifdef HAVE_SETCON # include #endif #include "common.h" /* The raw header used when not using DNS protocol */ const unsigned char raw_header[RAW_HDR_LEN] = { 0x10, 0xd1, 0x9e, 0x00 }; /* daemon(3) exists only in 4.4BSD or later, and in GNU libc */ #if !defined(ANDROID) && !defined(WINDOWS32) && !(defined(BSD) && (BSD >= 199306)) && !defined(__GLIBC__) static int daemon(int nochdir, int noclose) { int fd, i; switch (fork()) { case 0: break; case -1: return -1; default: _exit(0); } if (!nochdir) { chdir("/"); } if (setsid() < 0) { return -1; } if (!noclose) { if ((fd = open("/dev/null", O_RDWR)) >= 0) { for (i = 0; i < 3; i++) { dup2(fd, i); } if (fd > 2) { close(fd); } } } return 0; } #endif #if defined(__BEOS__) && !defined(__HAIKU__) int setgroups(int count, int *groups) { /* errno = ENOSYS; */ return -1; } #endif void check_superuser(void (*usage_fn)(void)) { #ifndef WINDOWS32 if (geteuid() != 0) { warnx("Run as root and you'll be happy.\n"); usage_fn(); /* NOTREACHED */ } #endif } int open_dns(int localport, in_addr_t listen_ip) { struct sockaddr_in addr; int flag = 1; int fd; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(localport); /* listen_ip already in network byte order from inet_addr, or 0 */ addr.sin_addr.s_addr = listen_ip; if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { fprintf(stderr, "got fd %d\n", fd); err(1, "socket"); } flag = 1; #ifdef SO_REUSEPORT setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &flag, sizeof(flag)); #endif setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &flag, sizeof(flag)); #ifndef WINDOWS32 /* To get destination address from each UDP datagram, see iodined.c:read_dns() */ setsockopt(fd, IPPROTO_IP, DSTADDR_SOCKOPT, (const void*) &flag, sizeof(flag)); #endif #ifdef IP_OPT_DONT_FRAG /* Set dont-fragment ip header flag */ flag = DONT_FRAG_VALUE; setsockopt(fd, IPPROTO_IP, IP_OPT_DONT_FRAG, (const void*) &flag, sizeof(flag)); #endif if(bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) err(1, "bind"); fprintf(stderr, "Opened UDP socket\n"); return fd; } int open_dns_ipv6(int localport, struct in6_addr listen_ip6) { struct sockaddr_in6 addr; int flag = 1; int fd; int false = 0; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(localport); /* listen_ip already in network byte order from inet_addr, or 0 */ addr.sin6_addr = listen_ip6; if ((fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) { fprintf(stderr, "got fd %d\n", fd); err(1, "socket"); } setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)); flag = 1; #ifdef SO_REUSEPORT setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &flag, sizeof(flag)); #endif setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &flag, sizeof(flag)); #ifndef WINDOWS32 /* To get destination address from each UDP datagram, see iodined.c:read_dns() */ setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const void*) &flag, sizeof(flag)); #endif #ifdef IP_OPT_DONT_FRAG /* Set dont-fragment ip header flag */ flag = DONT_FRAG_VALUE; setsockopt(fd, IPPROTO_IPV6, IP_OPT_DONT_FRAG, (const void*) &flag, sizeof(flag)); #endif if(bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) err(1, "bind"); fprintf(stderr, "Opened IPv6 UDP socket\n"); return fd; } void close_dns(int fd) { close(fd); } void do_chroot(char *newroot) { #if !(defined(WINDOWS32) || defined(__BEOS__) || defined(__HAIKU__)) if (chroot(newroot) != 0 || chdir("/") != 0) err(1, "%s", newroot); seteuid(geteuid()); setuid(getuid()); #else warnx("chroot not available"); #endif } void do_setcon(char *context) { #ifdef HAVE_SETCON if (-1 == setcon(context)) err(1, "%s", context); #else warnx("No SELinux support built in"); #endif } void do_pidfile(char *pidfile) { #ifndef WINDOWS32 FILE *file; if ((file = fopen(pidfile, "w")) == NULL) { syslog(LOG_ERR, "Cannot write pidfile to %s, exiting", pidfile); err(1, "do_pidfile: Can not write pidfile to %s", pidfile); } else { fprintf(file, "%d\n", (int)getpid()); fclose(file); } #else fprintf(stderr, "Windows version does not support pid file\n"); #endif } void do_detach() { #ifndef WINDOWS32 fprintf(stderr, "Detaching from terminal...\n"); daemon(0, 0); umask(0); alarm(0); #else fprintf(stderr, "Windows version does not support detaching\n"); #endif } void read_password(char *buf, size_t len) { char pwd[80] = {0}; #ifndef WINDOWS32 struct termios old; struct termios tp; tcgetattr(0, &tp); old = tp; tp.c_lflag &= (~ECHO); tcsetattr(0, TCSANOW, &tp); #else int i; #endif fprintf(stderr, "Enter password: "); fflush(stderr); #ifndef WINDOWS32 fscanf(stdin, "%79[^\n]", pwd); #else for (i = 0; i < sizeof(pwd); i++) { pwd[i] = getch(); if (pwd[i] == '\r' || pwd[i] == '\n') { pwd[i] = 0; break; } else if (pwd[i] == '\b') { i--; /* Remove the \b char */ if (i >=0) i--; /* If not first char, remove one more */ } } #endif fprintf(stderr, "\n"); #ifndef WINDOWS32 tcsetattr(0, TCSANOW, &old); #endif strncpy(buf, pwd, len); buf[len-1] = '\0'; } int check_topdomain(char *str) { int i; if(str[0] == '.') /* special case */ return 1; for( i = 0; i < strlen(str); i++) { if( isalpha(str[i]) || isdigit(str[i]) || str[i] == '-' || str[i] == '.' ) continue; else return 1; } return 0; } #if defined(WINDOWS32) || defined(ANDROID) #ifndef ANDROID int inet_aton(const char *cp, struct in_addr *inp) { inp->s_addr = inet_addr(cp); return inp->s_addr != INADDR_ANY; } #endif void warn(const char *fmt, ...) { va_list list; va_start(list, fmt); if (fmt) fprintf(stderr, fmt, list); #ifndef ANDROID if (errno == 0) { fprintf(stderr, ": WSA error %d\n", WSAGetLastError()); } else { fprintf(stderr, ": %s\n", strerror(errno)); } #endif va_end(list); } void warnx(const char *fmt, ...) { va_list list; va_start(list, fmt); if (fmt) fprintf(stderr, fmt, list); fprintf(stderr, "\n"); va_end(list); } void err(int eval, const char *fmt, ...) { va_list list; va_start(list, fmt); warn(fmt, list); va_end(list); exit(eval); } void errx(int eval, const char *fmt, ...) { va_list list; va_start(list, fmt); warnx(fmt, list); va_end(list); exit(eval); } #endif int recent_seqno(int ourseqno, int gotseqno) /* Return 1 if we've seen gotseqno recently (current or up to 3 back). Return 0 if gotseqno is new (or very old). */ { int i; for (i = 0; i < 4; i++, ourseqno--) { if (ourseqno < 0) ourseqno = 7; if (gotseqno == ourseqno) return 1; } return 0; } #ifdef LINUX void ipv6_addr_add(struct in6_addr *addr, uint8_t amount) { int i; for (i = 15; i >= 0; --i) { uint16_t next = addr->__in6_u.__u6_addr8[i]; next = next + amount; addr->__in6_u.__u6_addr8[i] = next; if(next & 0xff00) amount = 1; else break; } } char ipv6_addr_equals(struct in6_addr *a, struct in6_addr *b) { int i; for (i = 3; i >= 0; --i) if(a->__in6_u.__u6_addr32[i] != b->__in6_u.__u6_addr32[i]) return 0; return 1; } char ipv6_net_check(struct in6_addr *net, char netmask) { uint32_t netmask_full[4]; int i; for (i = 0; i < 4; ++i) if(32*(i + 1) <= netmask) netmask_full[i] = 0xffffffff; else if(32*i >= netmask) netmask_full[i] = 0x00000000; else netmask_full[i] = ~((1 << (netmask - 32*i)) - 1); for (i = 3; i >= 0; --i) if((net->__in6_u.__u6_addr32[i] & netmask_full[i]) != net->__in6_u.__u6_addr32[i]) return 0; return 1; } void ipv6_print(struct in6_addr *ip, char netmask6) { int i; for (i = 0; i < 8; ++i) fprintf(stderr, "%04x%s", ntohs(ip->__in6_u.__u6_addr16[i]), i < 7 ? ":" : "/"); printf("%d\n", netmask6); } #endif