summaryrefslogtreecommitdiffstats
path: root/mdk-stage1/dietlibc/libcruft/dnscruft.c
blob: bd8bee79a370059f740fa19878eacc22d691071f (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <resolv.h>
#include <net/if.h>
#include "dietfeatures.h"

int __dns_fd=-1;
#ifdef WANT_IPV6_DNS
int __dns_fd6=-1;
#endif

/* the ad-hoc internal API from hell ;-) */
void __dns_make_fd(void);
void __dns_make_fd6(void);
void __dns_readstartfiles(void);
int __dns_decodename(unsigned char *packet,unsigned int offset,unsigned char *dest,
		     unsigned int maxlen,unsigned char* behindpacket);

void __dns_make_fd(void) {
  int tmp;
  struct sockaddr_in si;
  if (__dns_fd>=0) return;
  tmp=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
  if (tmp<0) return;
  si.sin_family=AF_INET;
  si.sin_port=0;
  si.sin_addr.s_addr=INADDR_ANY;
  if (bind(tmp,(struct sockaddr*)&si,sizeof(si))) return;
  __dns_fd=tmp;
}

#ifdef WANT_IPV6_DNS
void __dns_make_fd6(void) {
  int tmp;
  struct sockaddr_in6 si;
  if (__dns_fd6>=0) return;
  tmp=socket(PF_INET6,SOCK_DGRAM,IPPROTO_UDP);
  if (tmp<0) return;
  si.sin6_family=AF_INET6;
  si.sin6_port=0;
  memset(&si.sin6_addr,0,16);
  if (bind(tmp,(struct sockaddr*)&si,sizeof(si))) return;
  __dns_fd6=tmp;
}
#endif

static int parsesockaddr(const char* c,void* x) {
  struct sockaddr_in to;
  if (inet_aton(c,&to.sin_addr)) {
    to.sin_port=htons(53);
    to.sin_family=AF_INET;
    memmove(x,&to,sizeof(struct sockaddr_in_pad));
    return 1;
#ifdef WANT_IPV6_DNS
  } else {
    struct sockaddr_in6 to6;
    char* d=strchr(c,'%');
    to6.sin6_flowinfo=to6.sin6_scope_id=0;
    if (d)
      to6.sin6_scope_id=if_nametoindex(d+1);
    if (inet_pton(AF_INET6,c,&to6.sin6_addr)) {
      to6.sin6_port=htons(53);
      to6.sin6_family=AF_INET6;
      memmove(x,&to6,sizeof(struct sockaddr_in_pad));
      return 1;
    }
#endif
  }
  return 0;
}

#ifdef WANT_FULL_RESOLV_CONF
int __dns_search;
char *__dns_domains[8];
#endif

void __dns_readstartfiles(void) {
  int fd;
  char __buf[4096];
  char *buf=__buf;
  int len;
  if (_res.nscount>0) return;
  {
    struct sockaddr_in to;
#ifdef WANT_IPV6_DNS
    struct sockaddr_in6 to6;
#endif
    char *cacheip=getenv("DNSCACHEIP");
#ifdef WANT_FULL_RESOLV_CONF
    __dns_search=0;
#endif
    if (cacheip)
      if (parsesockaddr(cacheip,_res.nsaddr_list))
	++_res.nscount;
  }
  _res.options=RES_RECURSE;
  if ((fd=open("/etc/resolv.conf",O_RDONLY))<0) return;
  len=read(fd,buf,4096);
  close(fd);
  {
    char *last=buf+len;
    for (; buf<last;) {
      if (!strncmp(buf,"nameserver",10)) {
	buf+=10;
	while (buf<last && *buf!='\n') {
	  while (buf<last && isblank(*buf)) ++buf;
	  {
	    char *tmp=buf;
	    struct sockaddr_in i;
	    char save;
	    while (buf<last && !isspace(*buf)) ++buf;
	    if (buf>=last) break;
	    save=*buf;
	    *buf=0;
	    if (parsesockaddr(tmp,&_res.nsaddr_list[_res.nscount]))
	      if (_res.nscount<MAXNS) ++_res.nscount;
	    *buf=save;
	  }
	}
      }
#ifdef WANT_FULL_RESOLV_CONF
      else if (!strncmp(buf,"search",6) || !strncmp(buf,"domain",6)) {
	buf+=6;
	while (buf<last && *buf!='\n') {
	  char save;
	  while (buf<last && (*buf==',' || isblank(*buf))) ++buf;
	  __dns_domains[__dns_search]=buf;
	  while (buf<last && (*buf=='.' || *buf=='-' || isalnum(*buf))) ++buf;
	  save=*buf;
	  if (buf<last) *buf=0;
	  if (__dns_domains[__dns_search]<buf &&
	      (__dns_domains[__dns_search]=strdup(__dns_domains[__dns_search])))
	    ++__dns_search;
	  if (buf<last) *buf=save;
	}
	continue;
      }
#endif
      while (buf<last && *buf!='\n') ++buf;
      while (buf<last && *buf=='\n') ++buf;
    }
  }
}

/* return length of decoded data or -1 */
int __dns_decodename(unsigned char *packet,unsigned int offset,unsigned char *dest,
		     unsigned int maxlen,unsigned char* behindpacket) {
  unsigned char *tmp;
  unsigned char *max=dest+maxlen;
  unsigned char *after=packet+offset;
  int ok=0;
  for (tmp=after; maxlen>0&&*tmp; ) {
    if (tmp>=behindpacket) return -1;
    if ((*tmp>>6)==3) {		/* goofy DNS decompression */
      unsigned int ofs=((unsigned int)(*tmp&0x3f)<<8)|*(tmp+1);
      if (ofs>=(unsigned int)offset) return -1;	/* RFC1035: "pointer to a _prior_ occurrance" */
      if (after<tmp+2) after=tmp+2;
      tmp=packet+ofs;
      ok=0;
    } else {
      unsigned int duh;
      if (dest+*tmp+1>max) return -1;
      if (tmp+*tmp+1>=behindpacket) return -1;
      for (duh=*tmp; duh>0; --duh)
	*dest++=*++tmp;
      *dest++='.'; ok=1;
      ++tmp;
      if (tmp>after) { after=tmp; if (!*tmp) ++after; }
    }
  }
  if (ok) --dest;
  *dest=0;
  return after-packet;
}