chiark / gitweb /
Shorten all per-file copyright notices
[adns.git] / src / transmit.c
1 /*
2  * transmit.c
3  * - construct queries
4  * - send queries
5  */
6 /*
7  *  This file is part of adns, which is Copyright Ian Jackson
8  *  and contributors (see the file INSTALL for full details).
9  *  
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 3, or (at your option)
13  *  any later version.
14  *  
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *  
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software Foundation.
22  */
23
24 #include <errno.h>
25
26 #include <sys/types.h>
27 #include <sys/uio.h>
28
29 #include "internal.h"
30 #include "tvarith.h"
31
32 #define MKQUERY_START(vb) (rqp= (vb)->buf+(vb)->used)
33 #define MKQUERY_ADDB(b) *rqp++= (b)
34 #define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
35 #define MKQUERY_STOP(vb) ((vb)->used= rqp-(vb)->buf)
36
37 static adns_status mkquery_header(adns_state ads, vbuf *vb,
38                                   int *id_r, int qdlen) {
39   int id;
40   byte *rqp;
41   
42   if (!adns__vbuf_ensure(vb,DNS_HDRSIZE+qdlen+4)) return adns_s_nomemory;
43
44   vb->used= 0;
45   MKQUERY_START(vb);
46   
47   *id_r= id= (ads->nextid++) & 0x0ffff;
48   MKQUERY_ADDW(id);
49   MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
50   MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
51   MKQUERY_ADDW(1); /* QDCOUNT=1 */
52   MKQUERY_ADDW(0); /* ANCOUNT=0 */
53   MKQUERY_ADDW(0); /* NSCOUNT=0 */
54   MKQUERY_ADDW(0); /* ARCOUNT=0 */
55
56   MKQUERY_STOP(vb);
57   
58   return adns_s_ok;
59 }
60
61 static adns_status mkquery_footer(vbuf *vb, adns_rrtype type) {
62   byte *rqp;
63
64   MKQUERY_START(vb);
65   MKQUERY_ADDW(type & adns_rrt_typemask); /* QTYPE */
66   MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */
67   MKQUERY_STOP(vb);
68   assert(vb->used <= vb->avail);
69   
70   return adns_s_ok;
71 }
72
73 static adns_status qdparselabel(adns_state ads,
74                                 const char **p_io, const char *pe,
75                                 char label_r[], int *ll_io,
76                                 adns_queryflags flags) {
77   int ll, c;
78   const char *p;
79
80   ll= 0;
81   p= *p_io;
82   
83   while (p!=pe && (c= *p++)!='.') {
84     if (c=='\\') {
85       if (!(flags & adns_qf_quoteok_query)) return adns_s_querydomaininvalid;
86       if (p==pe) return adns_s_querydomaininvalid;
87       if (ctype_digit(p[0])) {
88         if (p+1==pe || p+2==pe) return adns_s_querydomaininvalid;
89         if (ctype_digit(p[1]) && ctype_digit(p[2])) {
90           c= (*p++ - '0')*100;
91           c += (*p++ - '0')*10;
92           c += (*p++ - '0');
93           if (c >= 256) return adns_s_querydomaininvalid;
94         } else {
95           return adns_s_querydomaininvalid;
96         }
97       } else if (!(c= *p++)) {
98         return adns_s_querydomaininvalid;
99       }
100     }
101     if (ll == *ll_io) return adns_s_querydomaininvalid;
102     label_r[ll++]= c;
103   }
104   
105   *p_io= p;
106   *ll_io= ll;
107   return adns_s_ok;
108 }
109
110 adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
111                           const char *owner, int ol,
112                           const typeinfo *typei, adns_rrtype type,
113                           adns_queryflags flags) {
114   int ll, nbytes;
115   byte label[255];
116   byte *rqp;
117   const char *p, *pe;
118   adns_status st;
119
120   st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st;
121   
122   MKQUERY_START(vb);
123
124   p= owner; pe= owner+ol;
125   nbytes= 0;
126   while (p!=pe) {
127     ll= sizeof(label);
128     st= qdparselabel(ads, &p,pe, label, &ll, flags);
129     if (st) return st;
130     if (!ll) return adns_s_querydomaininvalid;
131     if (ll > DNS_MAXLABEL) return adns_s_querydomaintoolong;
132     nbytes+= ll+1;
133     if (nbytes >= DNS_MAXDOMAIN) return adns_s_querydomaintoolong;
134     MKQUERY_ADDB(ll);
135     memcpy(rqp,label,ll); rqp+= ll;
136   }
137   MKQUERY_ADDB(0);
138
139   MKQUERY_STOP(vb);
140   
141   st= mkquery_footer(vb,type);
142   
143   return adns_s_ok;
144 }
145
146 adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
147                                   const byte *qd_dgram, int qd_dglen,
148                                   int qd_begin,
149                                   adns_rrtype type, adns_queryflags flags) {
150   byte *rqp;
151   findlabel_state fls;
152   int lablen, labstart;
153   adns_status st;
154
155   st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st;
156
157   MKQUERY_START(vb);
158
159   adns__findlabel_start(&fls,ads,-1,0,qd_dgram,qd_dglen,qd_dglen,qd_begin,0);
160   for (;;) {
161     st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
162     if (!lablen) break;
163     assert(lablen<255);
164     MKQUERY_ADDB(lablen);
165     memcpy(rqp,qd_dgram+labstart,lablen);
166     rqp+= lablen;
167   }
168   MKQUERY_ADDB(0);
169
170   MKQUERY_STOP(vb);
171   
172   st= mkquery_footer(vb,type);
173   
174   return adns_s_ok;
175 }
176
177 void adns__querysend_tcp(adns_query qu, struct timeval now) {
178   byte length[2];
179   struct iovec iov[2];
180   int wr, r;
181   adns_state ads;
182
183   if (qu->ads->tcpstate != server_ok) return;
184
185   assert(qu->state == query_tcpw);
186
187   length[0]= (qu->query_dglen&0x0ff00U) >>8;
188   length[1]= (qu->query_dglen&0x0ff);
189
190   ads= qu->ads;
191   if (!adns__vbuf_ensure(&ads->tcpsend,ads->tcpsend.used+qu->query_dglen+2))
192     return;
193
194   qu->retries++;
195
196   /* Reset idle timeout. */
197   ads->tcptimeout.tv_sec= ads->tcptimeout.tv_usec= 0;
198
199   if (ads->tcpsend.used) {
200     wr= 0;
201   } else {
202     iov[0].iov_base= length;
203     iov[0].iov_len= 2;
204     iov[1].iov_base= qu->query_dgram;
205     iov[1].iov_len= qu->query_dglen;
206     adns__sigpipe_protect(qu->ads);
207     wr= writev(qu->ads->tcpsocket,iov,2);
208     adns__sigpipe_unprotect(qu->ads);
209     if (wr < 0) {
210       if (!(errno == EAGAIN || errno == EINTR || errno == ENOSPC ||
211             errno == ENOBUFS || errno == ENOMEM)) {
212         adns__tcp_broken(ads,"write",strerror(errno));
213         return;
214       }
215       wr= 0;
216     }
217   }
218
219   if (wr<2) {
220     r= adns__vbuf_append(&ads->tcpsend,length,2-wr); assert(r);
221     wr= 0;
222   } else {
223     wr-= 2;
224   }
225   if (wr<qu->query_dglen) {
226     r= adns__vbuf_append(&ads->tcpsend,qu->query_dgram+wr,qu->query_dglen-wr);
227     assert(r);
228   }
229 }
230
231 static void query_usetcp(adns_query qu, struct timeval now) {
232   qu->state= query_tcpw;
233   adns__timeout_set(qu,now,TCPWAITMS);
234   LIST_LINK_TAIL(qu->ads->tcpw,qu);
235   adns__querysend_tcp(qu,now);
236   adns__tcp_tryconnect(qu->ads,now);
237 }
238
239 struct udpsocket *adns__udpsocket_by_af(adns_state ads, int af) {
240   int i;
241   for (i=0; i<ads->nudpsockets; i++)
242     if (ads->udpsockets[i].af == af) return &ads->udpsockets[i];
243   return 0;
244 }
245
246 void adns__query_send(adns_query qu, struct timeval now) {
247   int serv, r;
248   adns_state ads;
249   struct udpsocket *udp;
250   adns_rr_addr *addr;
251
252   assert(qu->state == query_tosend);
253   if ((qu->flags & adns_qf_usevc) || (qu->query_dglen > DNS_MAXUDP)) {
254     query_usetcp(qu,now);
255     return;
256   }
257
258   if (qu->retries >= UDPMAXRETRIES) {
259     adns__query_fail(qu,adns_s_timeout);
260     return;
261   }
262
263   ads= qu->ads;
264   serv= qu->udpnextserver;
265   addr= &ads->servers[serv];
266   udp= adns__udpsocket_by_af(ads, addr->addr.sa.sa_family);
267   assert(udp);
268   
269   r= sendto(udp->fd,qu->query_dgram,qu->query_dglen,0,
270             &addr->addr.sa,addr->len);
271   if (r<0 && errno == EMSGSIZE) {
272     qu->retries= 0;
273     query_usetcp(qu,now);
274     return;
275   }
276   if (r<0 && errno != EAGAIN)
277     adns__warn(ads,serv,0,"sendto failed: %s",strerror(errno));
278   
279   adns__timeout_set(qu, now, UDPRETRYMS);
280   qu->udpsent |= (1<<serv);
281   qu->udpnextserver= (serv+1)%ads->nservers;
282   qu->retries++;
283   LIST_LINK_TAIL(ads->udpw,qu);
284 }