/* $Id: dns_replay2.c,v 1.13 2011/02/24 11:00:47 fujiwara Exp $ Copyright (C) 1998-2006 Kazunori Fujiwara . All rights reserved. You can redistribute it and/or modify it under either the terms of the GPL version 2. GPL is shown in . How to complile: Solaris 10: gcc -O -o dns_replay2 -DNO_ERR dns_replay2.c -lsocket -lresolv -lnsl BSD: cc -O -o dns_replay2 dns_replay.c */ #include #include #include #include #include #include #include #include #include #include #ifndef NO_ERR #include #endif #ifndef NO_SYS_SELECT_H #include #endif #include #include #include #include #include #include #include #ifdef __APPLE__ #include #endif int Xerror[256]; int SendCount = 0; int RecvCount = 0; struct addr46 { int len; union { struct sockaddr sa; struct sockaddr_in sin4; struct sockaddr_in6 sin6; } u; }; #ifdef NO_ERR void err(int err, char *format, ...) { va_list ap; va_start(ap, format); (void) vfprintf(stderr, format, ap); va_end(ap); (void)exit(err); } #endif int ipaddr2sockaddr(struct addr46 *a, char *addr, char *port) { char *p; int len; int nport; char buff[64]; bzero(a, sizeof(struct addr46)); if ((p = strchr(addr, '/')) != NULL) { len = p - addr; if (len >= sizeof(buff)) len = sizeof(buff) - 1; memcpy(buff, addr, len); buff[len] = 0; port = p + 1; addr = buff; } if (port == NULL || *port == 0) { nport = 53; } else { nport = atoi(port); } if (inet_pton(AF_INET, addr, (char *)&a->u.sin4.sin_addr)) { a->u.sin4.sin_port = htons(nport); a->u.sin4.sin_family = AF_INET; a->len = sizeof(struct sockaddr_in); return 1; } else if (inet_pton(AF_INET6, addr, (char *)&a->u.sin6.sin6_addr)) { a->u.sin6.sin6_port = htons(nport); a->u.sin6.sin6_family = AF_INET6; a->len = sizeof(struct sockaddr_in6); return 1; } return 0; } #ifdef DEBUG void HexDump(char *str, unsigned char *buf, int len) { int i; printf("%s [", str); for (i = 0; i < len; i++) { printf("%02x ", buf[i]); } printf("]\n"); } #endif long long NOW(void) { struct timeval _current; gettimeofday(&_current, NULL); return _current.tv_sec * 1000000LL + _current.tv_usec; } struct data_header { uint8_t flag; uint8_t af; uint16_t dnslen; uint32_t tv_sec; uint32_t tv_usec; uint16_t c_port; uint16_t s_port; }; /* */ static u_short id = 0; int send_packet(int s4, int s6, struct addr46 *destp, char *string, int fh) { char *qname; int qtype; unsigned char *u; int dnssec_do = 0; int edns = 0; int destaddrspecified = 0; int broken_specified = 0; int waittime; int len; int alen; char *sep = " \t\r\n"; struct addr46 dest; struct sockaddr_storage src; int srclen = sizeof(src); struct data_header header; u_char sendbuf[512]; u_char *w = sendbuf; u_char *p, *q; struct timeval now; if ((qname = strtok(string, sep)) == NULL) err(1, "1:qname=NULL"); if ((p = strtok(NULL, sep)) == NULL) err(1, "2:qname=%s qtype=NULL", qname); if ((qtype = atoi(p)) == 0 && *p != '0') err(1, "2:qname=%s qtype=%d", qname,qtype); if ((p = strtok(NULL, sep)) == NULL) err(1, "3:qname=%s qtype=%d edns=NULL", qname,qtype); if (*p == 'D') { dnssec_do = 1; edns = 1; p++; } if (*p == 'E') { edns = 1; p++; } if (*p == 'd') { destaddrspecified = 1; p++; } if (*p == 'b') { broken_specified = 2; } if ((p = strtok(NULL, sep)) == NULL) err(1, "4:qname=%s qtype=%d edns=%d wait=%s", qname,qtype,edns, p); waittime = atoi(p); if (destaddrspecified) { if ((p = strtok(NULL, sep)) == NULL) err(1, "5:qname=%s qtype=%d edns=%d wait=%s destaddrspecified=%d but destaddr is not specified", qname,qtype,edns, waittime, destaddrspecified); if (ipaddr2sockaddr(&dest, p, NULL)) { destp = &dest; } else { err(1, "6: cannot resolv %s", p); } } *w++ = (id >> 8); *w++ = (id & 0xff); id++; *w++ = 0; /* QR|opcode(4)|AA|TC|RD */ *w++ = 0; /* RA|Z(3)|OPCODE(4) */ *w++ = 0; *w++ = 1; /* QDCOUNT */ *w++ = 0; *w++ = 0; /* ANCOUNT */ *w++ = 0; *w++ = 0; /* NSCOUNT */ *w++ = 0; *w++ = edns; /* ANCOUNT */ p = qname; while(*p != 0 && *p != '.') { q = strchr(p, '.'); len = 0; len = (q == NULL) ? strlen(p) : q - p; if (len > 63 || w+len+8+11 > sendbuf+sizeof(sendbuf)) err(1, "buffer overflow:qname=%s qtype=%d edns=%d wait=%s", qname,qtype,edns, waittime); *w++ = len; memcpy(w, p, len); w += len; p += len; if (*p == '.') p++; } *w++ = 0; /* end of DNAME */ *w++ = (qtype >> 8); *w++ = (qtype & 0xff); *w++ = 0; *w++ = 1; /* class = IN */ if (edns) { *w++ = 0; /* . */ *w++ = 0; *w++ = 41; /* opt */ *w++ = 16; *w++ = 00; /* payload size = 4096 */ *w++ = 0; /* extended rcode */ *w++ = 0; /* edns version */ *w++ = dnssec_do? 0x80 : 0; /* DO=0x80 */ *w++ = 0; *w++ = 0; *w++ = 0; /* rdlen = 0 */ } len = w - sendbuf; if (broken_specified > 0) { len -= broken_specified; if (len < 0) len = 1; } gettimeofday(&now, NULL); if (sendto(destp->u.sa.sa_family==AF_INET?s4:s6, sendbuf, len, 0, (struct sockaddr *)&destp->u, destp->len) != len) { int no; if (errno < 0) no = 0; else if (errno > 255) no = 0; else no = errno; Xerror[no]++; } header.flag = 'S'; header.af = destp->u.sa.sa_family; header.tv_sec = htonl(now.tv_sec); header.tv_usec = htonl(now.tv_usec); if (header.af == AF_INET) { p = (char *)&destp->u.sin4.sin_addr; alen = sizeof(destp->u.sin4.sin_addr); header.s_port = destp->u.sin4.sin_port; if (getsockname(s4, (struct sockaddr *)&src, &srclen) < 0) err(1, "getsockname error: s4"); header.c_port = ((struct sockaddr_in *)&src)->sin_port; } else { p = (char *)&destp->u.sin6.sin6_addr; alen = sizeof(destp->u.sin6.sin6_addr); header.s_port = destp->u.sin6.sin6_port; if (getsockname(s6, (struct sockaddr *)&src, &srclen) < 0) err(1, "getsockname error: s4"); header.c_port = ((struct sockaddr_in6 *)&src)->sin6_port; } header.dnslen = len; if (write(fh, &header, sizeof(header)) != sizeof(header) || write(fh, p, alen) != alen || write(fh, sendbuf, len) != len) { err(1, "cannot write sent data"); } SendCount++; return waittime; } receive_packet(int sock, int fh) { struct sockaddr_storage ss, local; int slen = sizeof(ss); int locallen = sizeof(local); int len; struct data_header header; char *p; int alen; char buff[8192]; struct timeval now; len = recvfrom(sock, buff, sizeof(buff), 0, (struct sockaddr *)&ss, &slen); if (getsockname(sock, (struct sockaddr *)&local, &locallen) < 0) err(1, "error: getsockname at recvfrom"); header.af = ss.ss_family; header.dnslen = len; header.flag = 'R'; if (header.af == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)&ss; p = (char *)&sin->sin_addr; alen = sizeof(sin->sin_addr); header.s_port = sin->sin_port; header.c_port = ((struct sockaddr_in *)&local)->sin_port; } else if (header.af == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; p = (char *)&sin6->sin6_addr; alen = sizeof(sin6->sin6_addr); header.s_port = sin6->sin6_port; header.c_port = ((struct sockaddr_in6 *)&local)->sin6_port; } else { err(1, "received source address is broken: slen=%d family=%d", slen, ss.ss_family); } gettimeofday(&now, NULL); header.tv_sec = htonl(now.tv_sec); header.tv_usec = htonl(now.tv_usec); if (write(fh, &header, sizeof(header)) != sizeof(header) || write(fh, p, alen) != alen || write(fh, buff, len) != len) err(1, "cannot write received data"); RecvCount++; } void replay(FILE *i, int o, long long _Timeout, struct addr46 *destination, int ipv6) { int n; int nfds; long timeout; char *qname; int qtype; int edns; int dnssec_do; int destaddrspecified; long waittime; char *p; char *u; int len; int slen; int qid; long long current; long long last_sent; long long t; struct timeval _timeout; fd_set fdset0, fdset; struct addr46 *destp; struct addr46 dest; int s4 = -1; int s6 = -1; int optval; int optsize; struct data_header header; u_char buff[512]; u_char sendbuf[512]; FD_ZERO(&fdset0); s4 = socket(AF_INET, SOCK_DGRAM, 0); if (s4 < 0) err(1, "socket(AF_INET)"); memset(&dest.u.sin4, 0, sizeof(dest.u.sin4)); dest.u.sin4.sin_family = AF_INET; if (bind(s4, (struct sockaddr *)&dest.u.sin4, sizeof(dest.u.sin4)) < 0) err(1, "bind"); if (fcntl(s4, F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl(,F_SETFL,O_NONBLOCK,,)"); optsize = sizeof(optval); optval = 220 * 1024; if (setsockopt(s4, SOL_SOCKET, SO_SNDBUF, &optval, optsize) < 0) err(1, "setsocket(,SOL_SOCKET,SO_SNDBUF,,)"); optsize = sizeof(optval); optval = 220 * 1024; if (setsockopt(s4, SOL_SOCKET, SO_RCVBUF, &optval, optsize) < 0) err(1, "setsocket(,SOL_SOCKET,SO_RCVBUF,,)"); FD_SET(s4, &fdset0); nfds = s4 + 1; if (ipv6) { s6 = socket(AF_INET6, SOCK_DGRAM, 0); if (s6 < 0) err(1, "socket(AF_INET6)"); memset(&dest.u.sin6, 0, sizeof(dest.u.sin6)); dest.u.sin6.sin6_family = AF_INET6; if (bind(s6, (struct sockaddr *)&dest.u.sin6, sizeof(dest.u.sin6)) < 0) err(1, "bind"); if (fcntl(s6, F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl(,F_SETFL,O_NONBLOCK,,)"); optsize = sizeof(optval); optval = 220 * 1024; if (setsockopt(s6, SOL_SOCKET, SO_SNDBUF, &optval, optsize) < 0) err(1, "setsocket(,SOL_SOCKET,SO_SNDBUF,,)"); optsize = sizeof(optval); optval = 220 * 1024; if (setsockopt(s6, SOL_SOCKET, SO_RCVBUF, &optval, optsize) < 0) err(1, "setsocket(,SOL_SOCKET,SO_RCVBUF,,)"); FD_SET(s6, &fdset0); nfds = ((s4>s6)?s4:s6) + 1; } last_sent = current = NOW(); waittime = 0; for (;;) { current = NOW(); t = current - last_sent; if (t >= waittime) { if (waittime > 0) last_sent += waittime; if (fgets(buff, sizeof buff, i) == NULL) break; if (buff[0] == ';') continue; waittime = send_packet(s4, s6, destination, buff, o); if (waittime == 0) continue; current = NOW(); t = current - last_sent; } if (t < waittime) { timeout = waittime - t; } else { continue; } #ifdef DEBUG printf("waittime > 0: %ld: now=%lld timeout=%ld\n", waittime, current, timeout); #endif _timeout.tv_sec = timeout / 1000000; _timeout.tv_usec = timeout % 1000000; fdset = fdset0; n = select(nfds, &fdset, NULL, NULL, &_timeout); if (FD_ISSET(s4, &fdset)) receive_packet(s4, o); if (ipv6 && FD_ISSET(s6, &fdset)) receive_packet(s6, o); } waittime = _Timeout; for (;;) { current = NOW(); t = current - last_sent; if (t < waittime) { timeout = waittime - t; } else break; _timeout.tv_sec = timeout / 1000000L; _timeout.tv_usec = timeout % 1000000L; fdset = fdset0; n = select(nfds, &fdset, NULL, NULL, &_timeout); current = NOW(); if (FD_ISSET(s4, &fdset)) receive_packet(s4, o); if (ipv6 && FD_ISSET(s6, &fdset)) receive_packet(s6, o); } } void usage() { fprintf(stderr, "dns_replay [options] [host [port]]\n" "\t-l num loops specified seconds\n" "\t-i file specify input file [stdin]\n" "\t-o file specify output file [stdout]\n" "\t-h host specify remote host\n" "\t-p port specify remote port\n" "\t-t timeout specify timeout\n" "\t-v verbose\n" "\tinput from stdin\n" "\toutput to stdout\n" ); exit(1); } int main(int argc, char *argv[]) { int ch; int opt_v = 0; char *host = "127.0.0.1"; char *port = "53"; char *infile = NULL; char *ofile = NULL; int ipv6 = 1; long long _Timeout = 5 * 1000 * 1000; int opt_l = 0; FILE *in = stdin; int i; int o = 1; struct addr46 dest; memset(&Xerror, 0, sizeof(Xerror)); while((ch = getopt(argc, argv, "6vl:i:o:h:p:t:")) != -1) switch(ch) { case '6': ipv6 = ~ipv6; break; case 'l': opt_l = atoi(optarg); break; case 'i': infile = strdup(optarg); break; case 'o': ofile = strdup(optarg); break; case 'h': host = strdup(optarg); break; case 'p': port = strdup(optarg); break; case 't': _Timeout = atol(optarg); break; case 'v': opt_v++; break; default: usage(); } argc-=optind; argv+=optind; if (argc > 0) { printf("argc=%d argv[0]=%s\n", argc, argv[0]); host = argv[0]; if (argc > 1) port = argv[1]; } if (opt_v) printf("!host=%s port=%s opt_l=%d i=%s o=%s\n", host, port, opt_l, infile, ofile); if (infile != NULL) { if ((in = fopen(infile, "r")) == NULL) { err(1, "error %d: %s", errno, infile); } } if (ofile != NULL) { if ((o = creat(ofile, 0755)) < 0) { err(1, "error %d: %s", errno, ofile); } } if (host == NULL) usage(); if (!ipaddr2sockaddr(&dest, host, port)) err(1, "error %s/%s", host, port); replay(in, o, _Timeout, &dest, ipv6); fprintf(stderr, "Send: %d\nRecv: %d\n", SendCount, RecvCount); for (i = 0; i < 256; i++) { if (Xerror[i] > 0) { fprintf(stderr, "!! errno[%d] = %d\n", i, Xerror[i]); } } return 0; }