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