ucspi-ssl  0.99e
TLS encryption for IPv6 communication
dns_transmit.c
Go to the documentation of this file.
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <unistd.h>
4 #include "socket.h"
5 #include "alloc.h"
6 #include "error.h"
7 #include "byte.h"
8 #include "uint16.h"
9 #include "dns.h"
10 #include "ip6.h"
11 
12 #define UDPSIZE 1024 /* IPv6 MTU size is 1280 byte */
13 
14 static int serverwantstcp(const char *buf,unsigned int len)
15 {
16  char out[12];
17 
18  if (!dns_packet_copy(buf,len,0,out,12)) return 1;
19  if (out[2] & 2) return 1;
20  return 0;
21 }
22 
23 static int serverfailed(const char *buf,unsigned int len)
24 {
25  char out[12];
26  unsigned int rcode;
27 
28  if (!dns_packet_copy(buf,len,0,out,12)) return 1;
29  rcode = out[3];
30  rcode &= 15;
31  if (rcode && (rcode != 3)) { errno = error_again; return 1; }
32  return 0;
33 }
34 
35 static int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len)
36 {
37  char out[12];
38  char *dn;
39  unsigned int pos;
40 
41  pos = dns_packet_copy(buf,len,0,out,12); if (!pos) return 1;
42  if (byte_diff(out,2,d->query + 2)) return 1;
43  if (out[4] != 0) return 1;
44  if (out[5] != 1) return 1;
45 
46  dn = 0;
47  pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1;
48  if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; }
49  alloc_free(dn);
50 
51  pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1;
52  if (byte_diff(out,2,d->qtype)) return 1;
53  if (byte_diff(out + 2,2,DNS_C_IN)) return 1;
54 
55  return 0;
56 }
57 
58 static void packetfree(struct dns_transmit *d)
59 {
60  if (!d->packet) return;
61  alloc_free(d->packet);
62  d->packet = 0;
63 }
64 
65 static void queryfree(struct dns_transmit *d)
66 {
67  if (!d->query) return;
68  alloc_free(d->query);
69  d->query = 0;
70 }
71 
72 static void socketfree(struct dns_transmit *d)
73 {
74  if (!d->s1) return;
75  close(d->s1 - 1);
76  d->s1 = 0;
77 }
78 
80 {
81  queryfree(d);
82  socketfree(d);
83  packetfree(d);
84 }
85 
86 static int randombind(struct dns_transmit *d)
87 {
88  int j;
89 
90  for (j = 0;j < 10;++j)
91  if (socket_bind6(d->s1 - 1,d->localip,1025 + dns_random(64510),d->scope_id) == 0)
92  return 0;
93  if (socket_bind6(d->s1 - 1,d->localip,0,d->scope_id) == 0)
94  return 0;
95  return -1;
96 }
97 
98 static const int timeouts[4] = { 1, 3, 11, 45 };
99 
100 static int thisudp(struct dns_transmit *d)
101 {
102  const char *ip;
103 
104  socketfree(d);
105 
106  while (d->udploop < 4) {
107  for (;d->curserver < 16;++d->curserver) {
108  ip = d->servers + 16 * d->curserver;
109  if (byte_diff(ip,16,V6any)) {
110  d->query[2] = dns_random(256);
111  d->query[3] = dns_random(256);
112 
113  d->s1 = 1 + socket_udp6();
114  if (!d->s1) { dns_transmit_free(d); return -1; }
115  if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
116 
117  if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0)
118  if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) {
119  struct taia now;
120  taia_now(&now);
121  taia_uint(&d->deadline,timeouts[d->udploop]);
122  taia_add(&d->deadline,&d->deadline,&now);
123  d->tcpstate = 0;
124  return 0;
125  }
126 
127  socketfree(d);
128  }
129  }
130 
131  ++d->udploop;
132  d->curserver = 0;
133  }
134 
135  dns_transmit_free(d); return -1;
136 }
137 
138 static int firstudp(struct dns_transmit *d)
139 {
140  d->curserver = 0;
141  return thisudp(d);
142 }
143 
144 static int nextudp(struct dns_transmit *d)
145 {
146  ++d->curserver;
147  return thisudp(d);
148 }
149 
150 static int thistcp(struct dns_transmit *d)
151 {
152  struct taia now;
153  const char *ip;
154 
155  socketfree(d);
156  packetfree(d);
157 
158  for (;d->curserver < 16;++d->curserver) {
159  ip = d->servers + 16 * d->curserver;
160  if (byte_diff(ip,16,V6any)) {
161  d->query[2] = dns_random(256);
162  d->query[3] = dns_random(256);
163 
164  d->s1 = 1 + socket_tcp6();
165  if (!d->s1) { dns_transmit_free(d); return -1; }
166  if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
167 
168  taia_now(&now);
169  taia_uint(&d->deadline,10);
170  taia_add(&d->deadline,&d->deadline,&now);
171  if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0) {
172  d->tcpstate = 2;
173  return 0;
174  }
175  if ((errno == error_inprogress) || (errno == error_wouldblock)) {
176  d->tcpstate = 1;
177  return 0;
178  }
179 
180  socketfree(d);
181  }
182  }
183 
184  dns_transmit_free(d); return -1;
185 }
186 
187 static int firsttcp(struct dns_transmit *d)
188 {
189  d->curserver = 0;
190  return thistcp(d);
191 }
192 
193 static int nexttcp(struct dns_transmit *d)
194 {
195  ++d->curserver;
196  return thistcp(d);
197 }
198 
199 int dns_transmit_start(struct dns_transmit *d,const char servers[256],int flagrecursive,const char *q,const char qtype[2],const unsigned char localip[16])
200 {
201  unsigned int len;
202 
204  errno = error_io;
205 
206  len = dns_domain_length(q);
207  d->querylen = len + 18;
208  d->query = alloc(d->querylen);
209  if (!d->query) return -1;
210 
211  uint16_pack_big(d->query,len + 16);
212  byte_copy(d->query + 2,12,flagrecursive ? "\0\0\1\0\0\1\0\0\0\0\0\0" : "\0\0\0\0\0\1\0\0\0\0\0\0gcc-bug-workaround");
213  byte_copy(d->query + 14,len,q);
214  byte_copy(d->query + 14 + len,2,qtype);
215  byte_copy(d->query + 16 + len,2,DNS_C_IN);
216 
217  byte_copy(d->qtype,2,qtype);
218  d->servers = servers;
219  byte_copy(d->localip,16,localip);
220 
221  d->udploop = flagrecursive ? 1 : 0;
222 
223  if (len + 16 > UDPSIZE) return firsttcp(d);
224  return firstudp(d);
225 }
226 
227 void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline)
228 {
229  x->fd = d->s1 - 1;
230 
231  switch(d->tcpstate) {
232  case 0: case 3: case 4: case 5:
233  x->events = IOPAUSE_READ;
234  break;
235  case 1: case 2:
236  x->events = IOPAUSE_WRITE;
237  break;
238  }
239 
240  if (taia_less(&d->deadline,deadline))
241  *deadline = d->deadline;
242 }
243 
244 int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when)
245 {
246  char udpbuf[UDPSIZE+1];
247  unsigned char ch;
248  int r;
249  int fd;
250 
251  errno = error_io;
252  fd = d->s1 - 1;
253 
254  if (!x->revents) {
255  if (taia_less(when,&d->deadline)) return 0;
256  errno = error_timeout;
257  if (d->tcpstate == 0) return nextudp(d);
258  return nexttcp(d);
259  }
260 
261  if (d->tcpstate == 0) {
262 /*
263 have attempted to send UDP query to each server udploop times
264 have sent query to curserver on UDP socket s
265 */
266  r = recv(fd,udpbuf,sizeof udpbuf,0);
267  if (r <= 0) {
268  if (errno == error_connrefused) if (d->udploop == 2) return 0;
269  return nextudp(d);
270  }
271  if (r + 1 > sizeof udpbuf) return 0;
272 
273  if (irrelevant(d,udpbuf,r)) return 0;
274  if (serverwantstcp(udpbuf,r)) return firsttcp(d);
275  if (serverfailed(udpbuf,r)) {
276  if (d->udploop == 2) return 0;
277  return nextudp(d);
278  }
279  socketfree(d);
280 
281  d->packetlen = r;
282  d->packet = alloc(d->packetlen);
283  if (!d->packet) { dns_transmit_free(d); return -1; }
284  byte_copy(d->packet,d->packetlen,udpbuf);
285  queryfree(d);
286  return 1;
287  }
288 
289  if (d->tcpstate == 1) {
290 /*
291 have sent connection attempt to curserver on TCP socket s
292 pos not defined
293 */
294  if (!socket_connected(fd)) return nexttcp(d);
295  d->pos = 0;
296  d->tcpstate = 2;
297  return 0;
298  }
299 
300  if (d->tcpstate == 2) {
301 /*
302 have connection to curserver on TCP socket s
303 have sent pos bytes of query
304 */
305  r = write(fd,d->query + d->pos,d->querylen - d->pos);
306  if (r <= 0) return nexttcp(d);
307  d->pos += r;
308  if (d->pos == d->querylen) {
309  struct taia now;
310  taia_now(&now);
311  taia_uint(&d->deadline,10);
312  taia_add(&d->deadline,&d->deadline,&now);
313  d->tcpstate = 3;
314  }
315  return 0;
316  }
317 
318  if (d->tcpstate == 3) {
319 /*
320 have sent entire query to curserver on TCP socket s
321 pos not defined
322 */
323  r = read(fd,&ch,1);
324  if (r <= 0) return nexttcp(d);
325  d->packetlen = ch;
326  d->tcpstate = 4;
327  return 0;
328  }
329 
330  if (d->tcpstate == 4) {
331 /*
332 have sent entire query to curserver on TCP socket s
333 pos not defined
334 have received one byte of packet length into packetlen
335 */
336  r = read(fd,&ch,1);
337  if (r <= 0) return nexttcp(d);
338  d->packetlen <<= 8;
339  d->packetlen += ch;
340  d->tcpstate = 5;
341  d->pos = 0;
342  d->packet = alloc(d->packetlen);
343  if (!d->packet) { dns_transmit_free(d); return -1; }
344  return 0;
345  }
346 
347  if (d->tcpstate == 5) {
348 /*
349 have sent entire query to curserver on TCP socket s
350 have received entire packet length into packetlen
351 packet is allocated
352 have received pos bytes of packet
353 */
354  r = read(fd,d->packet + d->pos,d->packetlen - d->pos);
355  if (r <= 0) return nexttcp(d);
356  d->pos += r;
357  if (d->pos < d->packetlen) return 0;
358 
359  socketfree(d);
360  if (irrelevant(d,d->packet,d->packetlen)) return nexttcp(d);
361  if (serverwantstcp(d->packet,d->packetlen)) return nexttcp(d);
362  if (serverfailed(d->packet,d->packetlen)) return nexttcp(d);
363 
364  queryfree(d);
365  return 1;
366  }
367 
368  return 0;
369 }
Definition: taia.h:8
const char * servers
Definition: dns.h:37
char * alloc(unsigned int n)
Definition: alloc.c:15
const unsigned char V6any[16]
Definition: socket_v6any.c:2
void byte_copy(void *, unsigned int, const void *)
char localip[16]
Definition: dns.h:38
void dns_transmit_free(struct dns_transmit *d)
Definition: dns_transmit.c:79
char buf[SSL_NAME_LEN]
Definition: sslhandle.c:125
int socket_connect6(int s, const char *ip, uint16 port, uint32 scope_id)
int error_again
Definition: error.c:71
#define DNS_C_IN
Definition: dns.h:8
int error_connrefused
Definition: error.c:120
struct taia deadline
Definition: dns.h:35
int error_wouldblock
Definition: error.c:64
void uint16_pack_big(char *, uint16)
char * query
Definition: dns.h:27
int byte_diff(const void *, unsigned int n, const void *)
int s1
Definition: dns.h:31
int socket_bind6(int s, const char *ip, uint16 port, uint32 scope_id)
int tcpstate
Definition: dns.h:32
int socket_udp6(void)
Definition: socket_udp6.c:12
unsigned int udploop
Definition: dns.h:33
int dns_transmit_start(struct dns_transmit *d, const char servers[256], int flagrecursive, const char *q, const char qtype[2], const unsigned char localip[16])
Definition: dns_transmit.c:199
int dns_transmit_get(struct dns_transmit *d, const iopause_fd *x, const struct taia *when)
Definition: dns_transmit.c:244
unsigned int curserver
Definition: dns.h:34
unsigned int pos
Definition: dns.h:36
int taia_now(struct taia *)
Definition: taia_now.c:8
int taia_less(const struct taia *, const struct taia *)
Definition: taia_less.c:7
unsigned int scope_id
Definition: dns.h:39
unsigned int packetlen
Definition: dns.h:30
void dns_transmit_io(struct dns_transmit *d, iopause_fd *x, struct taia *deadline)
Definition: dns_transmit.c:227
unsigned int dns_random(unsigned int)
Definition: dns_random.c:52
int dns_domain_equal(const char *, const char *)
Definition: dns_domain.c:40
void taia_add(struct taia *, const struct taia *, const struct taia *)
Definition: taia_add.c:7
char qtype[2]
Definition: dns.h:40
void taia_uint(struct taia *, unsigned int)
Definition: taia_uint.c:7
void alloc_free(char *x)
Definition: alloc.c:25
unsigned int dns_packet_copy(const char *, unsigned int, unsigned int, char *, unsigned int)
Definition: dns_packet.c:8
unsigned int querylen
Definition: dns.h:28
unsigned int dns_domain_length(const char *)
Definition: dns_domain.c:7
char * packet
Definition: dns.h:29
int socket_tcp6(void)
Definition: socket_tcp6.c:18
int socket_connected(int)
Definition: socket_conn.c:21
int error_timeout
Definition: error.c:50
unsigned int dns_packet_getname(const char *, unsigned int, unsigned int, char **)
Definition: dns_packet.c:35
int error_io
Definition: error.c:36
#define UDPSIZE
Definition: dns_transmit.c:12
char localip[16]
Definition: sslhandle.c:78
int error_inprogress
Definition: error.c:57