Download | Plain Text | Line Numbers


/* dnscache-compact.c
   diet -Os gcc -s -o dnscache-compact dnscache-compact.c -Wall -W -lowfat
   http://riemann.fmi.uni-sofia.bg/
 
   or in ./djbdns-1.05/ with:
   ./compile dnscache-compact.c
   ./load    dnscache-compact buffer.a byte.a libtai.a unix.a dns.a alloc.a
 
   I recommend using the second method (Nikola Vladov).
*/
 
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include "str.h"
#include "case.h"
#include "tai.h"
#include "buffer.h"
#include "uint32.h"
#include "dns.h"
#include "scan.h"
 
void die(int n, const char *s1, const char *s2, const char *s3) {
  if (s1) buffer_puts(buffer_2, s1);
  if (s2) buffer_puts(buffer_2, s2);
  if (s3) buffer_puts(buffer_2, s3);
  buffer_puts(buffer_2, "\n");
  buffer_flush(buffer_2);
  _exit(n);
}
 
char **convert_hosts(int argc, char **argv) {
  char **hh,**h,**a,*q;
 
  if (!(hh = malloc((argc+1) * sizeof (char *))))
    die(111, "malloc ", "error", 0);
 
  for(q=0,h=hh,a=argv; *a; a++)
    if (dns_domain_fromdot(&q, *a, str_len(*a)))
      {*h++=q; q=0;}
 
  return hh;
}
 
void dumpandwait(unsigned long pid, char *fn) {    
  struct stat st;
  time_t mtime=0;
  struct timespec ts = {0, 100000000}; /* 0.1s */
  int i;
 
  if (!stat(fn, &st)) mtime = st.st_mtime;
  if (kill(pid,SIGALRM))
    die(111, "unable to kill dnscache: ", "SIGALRM", 0);
 
  for (i=0; i<60; i++) {  /* 6-7 sec */
    if (!stat(fn, &st)) 
      if (mtime != st.st_mtime) return;
    nanosleep(&ts, 0);
  }
  die(111, "timeout waiting for dnscache to dump", 0,0);
}
 
int main(int argc, char **argv) {
  unsigned long flagkill = 0;
  uint32 pos, len;
  uint32 keylen;
  uint32 datalen;
  uint32 out;
  struct tai now;
  struct tai expire;
  char buf[8192];
  char *p,**hosts=0;
  int fd,i;
  buffer b= BUFFER_INIT((int (*)(int, const char *, unsigned int))write, 
			 -1 , buf, sizeof buf);
 
  if (argc>1 && argv[1][0] == '-') {
    if (!scan_ulong(argv[1]+1, &flagkill) || flagkill <= 1)
      die(111, "bad pid: ", argv[1]+1, 0);
    argc--; argv++;
  }
  if (argc<4) 
    die(1,"usage: dnscache-compact [-pid] data new.tmp " 
	"new [key1] [key2] ...",0,0);
 
  if (flagkill) dumpandwait(flagkill, argv[1]);
 
  if ((fd=open(argv[1],O_RDONLY)) == -1) 
    die(21, argv[1], ": open ", "error");
  if ((len=lseek(fd,0,SEEK_END)) == (uint32)-1) 
    die(21, argv[1], ": lseek ", "error");
  if ((p=mmap(0,len,PROT_READ,MAP_SHARED,fd,0))==MAP_FAILED)
    die(21, "mmap ", "error", 0);
  close(fd);
 
  if ((b.fd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC, 0644))==-1)
    die(22, argv[2], ": open ", "error");
 
  argc -= 4;
  if (argc) hosts = convert_hosts(argc, argv+4);
  tai_now(&now);
  pos = 0;
 
  while (pos+16 <= len) {
    uint32_unpack(p + pos, &keylen);
    uint32_unpack(p + pos + 4, &datalen);
    tai_unpack(p + pos + 8, &expire);
 
    out = 16 + keylen + datalen;
    if (pos + out > len) goto die;
 
    if (!tai_less(&expire,&now)) {
      for (i=0; i<argc; i++) {
	if (p[pos + 15 + keylen] == 0 && 
	    !case_diffb(hosts[i], keylen-2, p + pos + 18))
	  goto skip;
      }
      if (buffer_put(&b, p + pos, out)) goto die;
    }
 
  skip:
    pos += out;
    continue;
 
  die:
    munmap(p,len);
    close (b.fd); unlink(argv[2]);
    die(1, "input/output ", "error", 0);
  }
 
  if (buffer_flush(&b)) goto die;
  if (pos != len) goto die;
  if (fsync(b.fd) || close(b.fd)) goto die;
  munmap(p,len);
  if (rename(argv[2],argv[3])) goto die;
 
  if (flagkill) 
    if (kill(flagkill,SIGHUP))
      die(111, "unable to kill dnscache: ", "SIGHUP", 0);
  return 0;
}