aboutsummaryrefslogtreecommitdiff
path: root/pickdns.c
blob: aa74dd8efd830a306626c785889a5afa1cefba8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <unistd.h>
#include "byte.h"
#include "case.h"
#include "dns.h"
#include "open.h"
#include "cdb.h"
#include "response.h"

const char *fatal = "pickdns: fatal: ";
const char *starting = "starting pickdns\n";

static char seed[128];

void initialize(void)
{
  dns_random_init(seed);
}

static struct cdb c;
static char key[258];
static char data[512];

static int doit(char *q,char qtype[2],char ip[16])
{
  int r;
  uint32 dlen;
  unsigned int qlen;
  int flaga;
  int flagmx;

  qlen = dns_domain_length(q);
  if (qlen > 255) return 0; /* impossible */

  flaga = byte_equal(qtype,2,DNS_T_A);
  flagmx = byte_equal(qtype,2,DNS_T_MX);
  if (byte_equal(qtype,2,DNS_T_ANY)) flaga = flagmx = 1;
  if (!flaga && !flagmx) goto REFUSE;

  key[0] = '%';
  byte_copy(key + 1,4,ip+12);

  r = cdb_find(&c,key,5);
  if (!r) r = cdb_find(&c,key,4);
  if (!r) r = cdb_find(&c,key,3);
  if (!r) r = cdb_find(&c,key,2);
  if (r == -1) return 0;

  key[0] = '+';
  byte_zero(key + 1,2);
  if (r && (cdb_datalen(&c) == 2))
    if (cdb_read(&c,key + 1,2,cdb_datapos(&c)) == -1) return 0;

  byte_copy(key + 3,qlen,q);
  case_lowerb(key + 3,qlen + 3);

  r = cdb_find(&c,key,qlen + 3);
  if (!r) {
    byte_zero(key + 1,2);
    r = cdb_find(&c,key,qlen + 3);
  }
  if (!r) goto REFUSE;
  if (r == -1) return 0;
  dlen = cdb_datalen(&c);

  if (dlen > 512) dlen = 512;
  if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return 0;

  if (flaga) {
    dns_sortip(data,dlen);
    if (dlen > 12) dlen = 12;
    while (dlen >= 4) {
      dlen -= 4;
      if (!response_rstart(q,DNS_T_A,5)) return 0;
      if (!response_addbytes(data + dlen,4)) return 0;
      response_rfinish(RESPONSE_ANSWER);
    }
  }

  return 1;


  REFUSE:
  response[2] &= ~4;
  response[3] &= ~15;
  response[3] |= 5;
  return 1;
}

int respond(char *q,char qtype[2],char ip[16])
{
  int fd;
  int result;

  fd = open_read("data.cdb");
  if (fd == -1) return 0;
  cdb_init(&c,fd);
  result = doit(q,qtype,ip);
  cdb_free(&c);
  close(fd);
  return result;
}