1/* dnsmasq is Copyright (c) 2000 Simon Kelley 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 dated June, 1991. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11*/ 12 13/* Author's email: simon@thekelleys.org.uk */ 14 15#include "dnsmasq.h" 16 17static struct frec *ftab; 18 19static struct frec *get_new_frec(time_t now); 20static struct frec *lookup_frec(unsigned short id); 21static struct frec *lookup_frec_by_sender(unsigned short id, 22 union mysockaddr *addr); 23static unsigned short get_id(void); 24 25/* May be called more than once. */ 26void forward_init(int first) 27{ 28 int i; 29 30 if (first) 31 ftab = safe_malloc(FTABSIZ*sizeof(struct frec)); 32 for (i=0; i<FTABSIZ; i++) 33 ftab[i].new_id = 0; 34} 35 36/* returns new last_server */ 37struct server *forward_query(int udpfd, int peerfd, int peerfd6, 38 union mysockaddr *udpaddr, HEADER *header, 39 int plen, int strict_order, char *dnamebuff, 40 struct server *servers, struct server *last_server) 41{ 42 time_t now = time(NULL); 43 struct frec *forward; 44 char *domain = NULL; 45 struct server *serv; 46 int gotname = extract_request(header, (unsigned int)plen, dnamebuff); 47 48 /* may be recursion not speced or no servers available. */ 49 if (!header->rd || !servers) 50 forward = NULL; 51 else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr))) 52 { 53 /* retry on existing query, send to next server */ 54 domain = forward->sentto->domain; 55 if (!(forward->sentto = forward->sentto->next)) 56 forward->sentto = servers; /* at end of list, recycle */ 57 header->id = htons(forward->new_id); 58 } 59 else 60 { 61 /* new query, pick nameserver and send */ 62 forward = get_new_frec(now); 63 64 /* If the query ends in the domain in one of our servers, set 65 domain to point to that name. We find the largest match to allow both 66 domain.org and sub.domain.org to exist. */ 67 68 if (gotname) 69 { 70 unsigned int namelen = strlen(dnamebuff); 71 unsigned int matchlen = 0; 72 for (serv=servers; serv; serv=serv->next) 73 if (serv->domain) 74 { 75 unsigned int domainlen = strlen(serv->domain); 76 if (namelen >= domainlen && 77 strcmp(dnamebuff + namelen - domainlen, serv->domain) == 0 && 78 domainlen > matchlen) 79 { 80 domain = serv->domain; 81 matchlen = domainlen; 82 } 83 } 84 } 85 86 /* In strict_order mode, or when using domain specific servers 87 always try servers in the order specified in resolv.conf, 88 otherwise, use the one last known to work. */ 89 90 if (domain || strict_order) 91 forward->sentto = servers; 92 else 93 forward->sentto = last_server; 94 95 forward->source = *udpaddr; 96 forward->new_id = get_id(); 97 forward->fd = udpfd; 98 forward->orig_id = ntohs(header->id); 99 header->id = htons(forward->new_id); 100 } 101 102 /* check for send errors here (no route to host) 103 if we fail to send to all nameservers, send back an error 104 packet straight away (helps modem users when offline) */ 105 106 if (forward) 107 { 108 struct server *firstsentto = forward->sentto; 109 110 while (1) 111 { 112 int af = forward->sentto->addr.sa.sa_family; 113 int fd = af == AF_INET ? peerfd : peerfd6; 114 115 /* only send to servers dealing with our domain. 116 domain may be NULL, in which case server->domain 117 must be NULL also. */ 118 119 if ((!domain && !forward->sentto->domain) || 120 (domain && forward->sentto->domain && strcmp(domain, forward->sentto->domain) == 0)) 121 { 122 if (sendto(fd, (char *)header, plen, 0, 123 &forward->sentto->addr.sa, 124 sa_len(&forward->sentto->addr)) != -1) 125 { 126 if (af == AF_INET) 127 log_query(F_SERVER | F_IPV4 | F_FORWARD, gotname ? dnamebuff : "query", 128 (struct all_addr *)&forward->sentto->addr.in.sin_addr); 129#ifdef HAVE_IPV6 130 else 131 log_query(F_SERVER | F_IPV6 | F_FORWARD, gotname ? dnamebuff : "query", 132 (struct all_addr *)&forward->sentto->addr.in6.sin6_addr); 133#endif 134 /* for no-domain, dont't update last_server */ 135 return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers); 136 } 137 } 138 139 if (!(forward->sentto = forward->sentto->next)) 140 forward->sentto = servers; 141 142 /* check if we tried all without success */ 143 if (forward->sentto == firstsentto) 144 break; 145 } 146 147 /* could not send on, prepare to return */ 148 header->id = htons(forward->orig_id); 149 forward->new_id = 0; /* cancel */ 150 } 151 152 /* could not send on, return empty answer */ 153 header->qr = 1; /* response */ 154 header->aa = 0; /* authoritive - never */ 155 header->ra = 1; /* recursion if available */ 156 header->tc = 0; /* not truncated */ 157 header->rcode = NOERROR; /* no error */ 158 header->ancount = htons(0); /* no answers */ 159 header->nscount = htons(0); 160 header->arcount = htons(0); 161 sendto(udpfd, (char *)header, plen, 0, &udpaddr->sa, sa_len(udpaddr)); 162 163 return last_server; 164} 165 166/* returns new last_server */ 167struct server *reply_query(int fd, char *packet, char *dnamebuff, struct server *last_server) 168{ 169 /* packet from peer server, extract data for cache, and send to 170 original requester */ 171 struct frec *forward; 172 HEADER *header; 173 int n = recv(fd, packet, PACKETSZ, 0); 174 175 header = (HEADER *)packet; 176 if (n >= (int)sizeof(HEADER) && header->qr) 177 { 178 if ((forward = lookup_frec(ntohs(header->id)))) 179 { 180 if (header->rcode == NOERROR || header->rcode == NXDOMAIN) 181 { 182 if (!forward->sentto->domain) 183 last_server = forward->sentto; /* known good */ 184 if (header->opcode == QUERY) 185 extract_addresses(header, (unsigned int)n, dnamebuff); 186 } 187 188 header->id = htons(forward->orig_id); 189 /* There's no point returning an upstream reply marked as truncated, 190 since that will prod the resolver into moving to TCP - which we 191 don't support. */ 192 header->tc = 0; /* goodbye truncate */ 193 sendto(forward->fd, packet, n, 0, 194 &forward->source.sa, sa_len(&forward->source)); 195 forward->new_id = 0; /* cancel */ 196 } 197 } 198 199 return last_server; 200} 201 202 203static struct frec *get_new_frec(time_t now) 204{ 205 int i; 206 struct frec *oldest = &ftab[0]; 207 time_t oldtime = now; 208 209 for(i=0; i<FTABSIZ; i++) 210 { 211 struct frec *f = &ftab[i]; 212 if (f->time <= oldtime) 213 { 214 oldtime = f->time; 215 oldest = f; 216 } 217 if (f->new_id == 0) 218 { 219 f->time = now; 220 return f; 221 } 222 } 223 224 /* table full, use oldest */ 225 226 oldest->time = now; 227 return oldest; 228} 229 230static struct frec *lookup_frec(unsigned short id) 231{ 232 int i; 233 for(i=0; i<FTABSIZ; i++) 234 { 235 struct frec *f = &ftab[i]; 236 if (f->new_id == id) 237 return f; 238 } 239 return NULL; 240} 241 242static struct frec *lookup_frec_by_sender(unsigned short id, 243 union mysockaddr *addr) 244{ 245 int i; 246 for(i=0; i<FTABSIZ; i++) 247 { 248 struct frec *f = &ftab[i]; 249 if (f->new_id && 250 f->orig_id == id && 251 sockaddr_isequal(&f->source, addr)) 252 return f; 253 } 254 return NULL; 255} 256 257 258/* return unique random ids between 1 and 65535 */ 259static unsigned short get_id(void) 260{ 261 unsigned short ret = 0; 262 263 while (ret == 0) 264 { 265 ret = rand16(); 266 267 /* scrap ids already in use */ 268 if ((ret != 0) && lookup_frec(ret)) 269 ret = 0; 270 } 271 272 return ret; 273} 274 275 276