00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <sys/socket.h>
00039 #include <netinet/in.h>
00040 #include <arpa/inet.h>
00041 #include <arpa/nameser.h>
00042 #include <resolv.h>
00043 #include <errno.h>
00044
00045
00046
00047
00048 #define BASH_SIZE 8192
00049 #define HOSTNAMELEN 255
00050
00051 #define RES_ERR "DNS Resolver error: "
00052 #define RES_MSG "DNS Resolver: "
00053 #define RES_WRN "DNS Resolver warning: "
00054
00055 #define MAX_PACKETSIZE (PACKETSZ)
00056 #define MAX_DOMAINLEN (MAXDNAME)
00057
00058
00059
00060 #define nonull(s) (s) ? s : nullstring
00061 #define BASH_MODULO(x) ((x) & 8191)
00062
00063
00064
00065 #ifdef DEBUG_DNS
00066 # define RESPONSECODES_COUNT 6
00067 static char *responsecodes[RESPONSECODES_COUNT + 1] = {
00068 "no error",
00069 "format error in query",
00070 "server failure",
00071 "queried domain name does not exist",
00072 "requested query type not implemented",
00073 "refused by name server",
00074 "unknown error",
00075 };
00076 #endif
00077
00078 #ifdef DEBUG_DNS
00079 # define RESOURCETYPES_COUNT 17
00080 static const char *resourcetypes[RESOURCETYPES_COUNT + 1] = {
00081 "unknown type",
00082 "A: host address",
00083 "NS: authoritative name server",
00084 "MD: mail destination (OBSOLETE)",
00085 "MF: mail forwarder (OBSOLETE)",
00086 "CNAME: name alias",
00087 "SOA: authority record",
00088 "MB: mailbox domain name (EXPERIMENTAL)",
00089 "MG: mail group member (EXPERIMENTAL)",
00090 "MR: mail rename domain name (EXPERIMENTAL)",
00091 "NULL: NULL RR (EXPERIMENTAL)",
00092 "WKS: well known service description",
00093 "PTR: domain name pointer",
00094 "HINFO: host information",
00095 "MINFO: mailbox or mail list information",
00096 "MX: mail exchange",
00097 "TXT: text string",
00098 "unknown type",
00099 };
00100 #endif
00101
00102 #ifdef DEBUG_DNS
00103 # define CLASSTYPES_COUNT 5
00104 static const char *classtypes[CLASSTYPES_COUNT + 1] = {
00105 "unknown class",
00106 "IN: the Internet",
00107 "CS: CSNET (OBSOLETE)",
00108 "CH: CHAOS",
00109 "HS: Hesoid [Dyer 87]",
00110 "unknown class"
00111 };
00112 #endif
00113
00114 typedef struct {
00115 u_16bit_t id;
00116 u_8bit_t databyte_a;
00117
00118
00119
00120
00121
00122
00123 u_8bit_t databyte_b;
00124
00125
00126
00127
00128
00129 u_16bit_t qdcount;
00130 u_16bit_t ancount;
00131 u_16bit_t nscount;
00132 u_16bit_t arcount;
00133 } packetheader;
00134
00135 #ifndef HFIXEDSZ
00136 #define HFIXEDSZ (sizeof(packetheader))
00137 #endif
00138
00139
00140
00141
00142 #define getheader_rd(x) (x->databyte_a & 1)
00143 #define getheader_tc(x) ((x->databyte_a >> 1) & 1)
00144 #define getheader_aa(x) ((x->databyte_a >> 2) & 1)
00145 #define getheader_opcode(x) ((x->databyte_a >> 3) & 15)
00146 #define getheader_qr(x) (x->databyte_a >> 7)
00147 #define getheader_rcode(x) (x->databyte_b & 15)
00148 #define getheader_pr(x) ((x->databyte_b >> 6) & 1)
00149 #define getheader_ra(x) (x->databyte_b >> 7)
00150
00151 #define sucknetword(x) ((x)+=2,((u_16bit_t) (((x)[-2] << 8) | ((x)[-1] << 0))))
00152 #define sucknetshort(x) ((x)+=2,((short) (((x)[-2] << 8) | ((x)[-1] << 0))))
00153 #define sucknetdword(x) ((x)+=4,((dword) (((x)[-4] << 24) | ((x)[-3] << 16) | \
00154 ((x)[-2] << 8) | ((x)[-1] << 0))))
00155 #define sucknetlong(x) ((x)+=4,((long) (((x)[-4] << 24) | ((x)[-3] << 16) | \
00156 ((x)[-2] << 8) | ((x)[-1] << 0))))
00157
00158
00159 static u_32bit_t resrecvbuf[(MAX_PACKETSIZE + 7) >> 2];
00160
00161 static struct resolve *idbash[BASH_SIZE];
00162 static struct resolve *ipbash[BASH_SIZE];
00163 static struct resolve *hostbash[BASH_SIZE];
00164 static struct resolve *expireresolves = NULL;
00165
00166 static IP localhost;
00167
00168 static long idseed = 0xdeadbeef;
00169 static long aseed;
00170
00171 static int resfd;
00172
00173 static char tempstring[512];
00174 static char namestring[1024 + 1];
00175 static char stackstring[1024 + 1];
00176
00177 #ifdef DEBUG_DNS
00178 static char sendstring[1024 + 1];
00179 #endif
00180
00181 static const char nullstring[] = "";
00182
00183
00184
00185
00186
00187
00188 #ifdef DEBUG_DNS
00189
00190
00191 static char *strtdiff(char *d, long signeddiff)
00192 {
00193 u_32bit_t diff;
00194 u_32bit_t seconds, minutes, hours;
00195 long day;
00196
00197 if ((diff = labs(signeddiff))) {
00198 seconds = diff % 60;
00199 diff /= 60;
00200 minutes = diff % 60;
00201 diff /= 60;
00202 hours = diff % 24;
00203 day = signeddiff / (60 * 60 * 24);
00204 if (day)
00205 sprintf(d, "%lid", day);
00206 else
00207 *d = '\0';
00208 if (hours)
00209 sprintf(d + strlen(d), "%uh", hours);
00210 if (minutes)
00211 sprintf(d + strlen(d), "%um", minutes);
00212 if (seconds)
00213 sprintf(d + strlen(d), "%us", seconds);
00214 } else
00215 sprintf(d, "0s");
00216 return d;
00217 }
00218 #endif
00219
00220
00221
00222 static struct resolve *allocresolve()
00223 {
00224 struct resolve *rp;
00225
00226 rp = nmalloc(sizeof *rp);
00227 egg_bzero(rp, sizeof(struct resolve));
00228 return rp;
00229 }
00230
00231
00232
00233
00234
00235
00236
00237
00238 inline static u_32bit_t getidbash(u_16bit_t id)
00239 {
00240 return (u_32bit_t) BASH_MODULO(id);
00241 }
00242
00243
00244
00245 inline static u_32bit_t getipbash(IP ip)
00246 {
00247 return (u_32bit_t) BASH_MODULO(ip);
00248 }
00249
00250
00251
00252 static u_32bit_t gethostbash(char *host)
00253 {
00254 u_32bit_t bashvalue = 0;
00255
00256 for (; *host; host++) {
00257 bashvalue ^= *host;
00258 bashvalue += (*host >> 1) + (bashvalue >> 1);
00259 }
00260 return BASH_MODULO(bashvalue);
00261 }
00262
00263
00264
00265 static void linkresolveid(struct resolve *addrp)
00266 {
00267 struct resolve *rp;
00268 u_32bit_t bashnum;
00269
00270 bashnum = getidbash(addrp->id);
00271 rp = idbash[bashnum];
00272 if (rp) {
00273 while ((rp->nextid) && (addrp->id > rp->nextid->id))
00274 rp = rp->nextid;
00275 while ((rp->previousid) && (addrp->id < rp->previousid->id))
00276 rp = rp->previousid;
00277 if (rp->id < addrp->id) {
00278 addrp->previousid = rp;
00279 addrp->nextid = rp->nextid;
00280 if (rp->nextid)
00281 rp->nextid->previousid = addrp;
00282 rp->nextid = addrp;
00283 } else if (rp->id > addrp->id) {
00284 addrp->previousid = rp->previousid;
00285 addrp->nextid = rp;
00286 if (rp->previousid)
00287 rp->previousid->nextid = addrp;
00288 rp->previousid = addrp;
00289 } else
00290 return;
00291 } else
00292 addrp->nextid = addrp->previousid = NULL;
00293 idbash[bashnum] = addrp;
00294 }
00295
00296
00297
00298 static void unlinkresolveid(struct resolve *rp)
00299 {
00300 u_32bit_t bashnum;
00301
00302 bashnum = getidbash(rp->id);
00303 if (idbash[bashnum] == rp) {
00304 if (rp->previousid)
00305 idbash[bashnum] = rp->previousid;
00306 else
00307 idbash[bashnum] = rp->nextid;
00308 }
00309 if (rp->nextid)
00310 rp->nextid->previousid = rp->previousid;
00311 if (rp->previousid)
00312 rp->previousid->nextid = rp->nextid;
00313 }
00314
00315
00316
00317 static void linkresolvehost(struct resolve *addrp)
00318 {
00319 struct resolve *rp;
00320 u_32bit_t bashnum;
00321 int ret;
00322
00323 bashnum = gethostbash(addrp->hostn);
00324 rp = hostbash[bashnum];
00325 if (rp) {
00326 while ((rp->nexthost) &&
00327 (egg_strcasecmp(addrp->hostn, rp->nexthost->hostn) < 0))
00328 rp = rp->nexthost;
00329 while ((rp->previoushost) &&
00330 (egg_strcasecmp(addrp->hostn, rp->previoushost->hostn) > 0))
00331 rp = rp->previoushost;
00332 ret = egg_strcasecmp(addrp->hostn, rp->hostn);
00333 if (ret < 0) {
00334 addrp->previoushost = rp;
00335 addrp->nexthost = rp->nexthost;
00336 if (rp->nexthost)
00337 rp->nexthost->previoushost = addrp;
00338 rp->nexthost = addrp;
00339 } else if (ret > 0) {
00340 addrp->previoushost = rp->previoushost;
00341 addrp->nexthost = rp;
00342 if (rp->previoushost)
00343 rp->previoushost->nexthost = addrp;
00344 rp->previoushost = addrp;
00345 } else
00346 return;
00347 } else
00348 addrp->nexthost = addrp->previoushost = NULL;
00349 hostbash[bashnum] = addrp;
00350 }
00351
00352
00353
00354 static void unlinkresolvehost(struct resolve *rp)
00355 {
00356 u_32bit_t bashnum;
00357
00358 bashnum = gethostbash(rp->hostn);
00359 if (hostbash[bashnum] == rp) {
00360 if (rp->previoushost)
00361 hostbash[bashnum] = rp->previoushost;
00362 else
00363 hostbash[bashnum] = rp->nexthost;
00364 }
00365 if (rp->nexthost)
00366 rp->nexthost->previoushost = rp->previoushost;
00367 if (rp->previoushost)
00368 rp->previoushost->nexthost = rp->nexthost;
00369 nfree(rp->hostn);
00370 }
00371
00372
00373
00374 static void linkresolveip(struct resolve *addrp)
00375 {
00376 struct resolve *rp;
00377 u_32bit_t bashnum;
00378
00379 bashnum = getipbash(addrp->ip);
00380 rp = ipbash[bashnum];
00381 if (rp) {
00382 while ((rp->nextip) && (addrp->ip > rp->nextip->ip))
00383 rp = rp->nextip;
00384 while ((rp->previousip) && (addrp->ip < rp->previousip->ip))
00385 rp = rp->previousip;
00386 if (rp->ip < addrp->ip) {
00387 addrp->previousip = rp;
00388 addrp->nextip = rp->nextip;
00389 if (rp->nextip)
00390 rp->nextip->previousip = addrp;
00391 rp->nextip = addrp;
00392 } else if (rp->ip > addrp->ip) {
00393 addrp->previousip = rp->previousip;
00394 addrp->nextip = rp;
00395 if (rp->previousip)
00396 rp->previousip->nextip = addrp;
00397 rp->previousip = addrp;
00398 } else
00399 return;
00400 } else
00401 addrp->nextip = addrp->previousip = NULL;
00402 ipbash[bashnum] = addrp;
00403 }
00404
00405
00406
00407 static void unlinkresolveip(struct resolve *rp)
00408 {
00409 u_32bit_t bashnum;
00410
00411 bashnum = getipbash(rp->ip);
00412 if (ipbash[bashnum] == rp) {
00413 if (rp->previousip)
00414 ipbash[bashnum] = rp->previousip;
00415 else
00416 ipbash[bashnum] = rp->nextip;
00417 }
00418 if (rp->nextip)
00419 rp->nextip->previousip = rp->previousip;
00420 if (rp->previousip)
00421 rp->previousip->nextip = rp->nextip;
00422 }
00423
00424
00425
00426
00427 static void linkresolve(struct resolve *rp)
00428 {
00429 struct resolve *irp;
00430
00431 if (expireresolves) {
00432 irp = expireresolves;
00433 while ((irp->next) && (rp->expiretime >= irp->expiretime))
00434 irp = irp->next;
00435 if (rp->expiretime >= irp->expiretime) {
00436 rp->next = NULL;
00437 rp->previous = irp;
00438 irp->next = rp;
00439 } else {
00440 rp->previous = irp->previous;
00441 rp->next = irp;
00442 if (irp->previous)
00443 irp->previous->next = rp;
00444 else
00445 expireresolves = rp;
00446 irp->previous = rp;
00447 }
00448 } else {
00449 rp->next = NULL;
00450 rp->previous = NULL;
00451 expireresolves = rp;
00452 }
00453 }
00454
00455
00456
00457 static void untieresolve(struct resolve *rp)
00458 {
00459 if (rp->previous)
00460 rp->previous->next = rp->next;
00461 else
00462 expireresolves = rp->next;
00463 if (rp->next)
00464 rp->next->previous = rp->previous;
00465 }
00466
00467
00468
00469
00470 static void unlinkresolve(struct resolve *rp)
00471 {
00472
00473 untieresolve(rp);
00474
00475 unlinkresolveid(rp);
00476 unlinkresolveip(rp);
00477 if (rp->hostn)
00478 unlinkresolvehost(rp);
00479 nfree(rp);
00480 }
00481
00482
00483
00484 static struct resolve *findid(u_16bit_t id)
00485 {
00486 struct resolve *rp;
00487 int bashnum;
00488
00489 bashnum = getidbash(id);
00490 rp = idbash[bashnum];
00491 if (rp) {
00492 while ((rp->nextid) && (id >= rp->nextid->id))
00493 rp = rp->nextid;
00494 while ((rp->previousid) && (id <= rp->previousid->id))
00495 rp = rp->previousid;
00496 if (id == rp->id) {
00497 idbash[bashnum] = rp;
00498 return rp;
00499 } else
00500 return NULL;
00501 }
00502 return rp;
00503 }
00504
00505
00506
00507 static struct resolve *findhost(char *hostn)
00508 {
00509 struct resolve *rp;
00510 int bashnum;
00511
00512 bashnum = gethostbash(hostn);
00513 rp = hostbash[bashnum];
00514 if (rp) {
00515 while ((rp->nexthost) &&
00516 (egg_strcasecmp(hostn, rp->nexthost->hostn) >= 0))
00517 rp = rp->nexthost;
00518 while ((rp->previoushost) &&
00519 (egg_strcasecmp(hostn, rp->previoushost->hostn) <= 0))
00520 rp = rp->previoushost;
00521 if (egg_strcasecmp(hostn, rp->hostn))
00522 return NULL;
00523 else {
00524 hostbash[bashnum] = rp;
00525 return rp;
00526 }
00527 }
00528 return rp;
00529 }
00530
00531
00532
00533 static struct resolve *findip(IP ip)
00534 {
00535 struct resolve *rp;
00536 u_32bit_t bashnum;
00537
00538 bashnum = getipbash(ip);
00539 rp = ipbash[bashnum];
00540 if (rp) {
00541 while ((rp->nextip) && (ip >= rp->nextip->ip))
00542 rp = rp->nextip;
00543 while ((rp->previousip) && (ip <= rp->previousip->ip))
00544 rp = rp->previousip;
00545 if (ip == rp->ip) {
00546 ipbash[bashnum] = rp;
00547 return rp;
00548 } else
00549 return NULL;
00550 }
00551 return rp;
00552 }
00553
00554
00555
00556
00557
00558
00559
00560
00561 static void dorequest(char *s, int type, u_16bit_t id)
00562 {
00563 packetheader *hp;
00564 int r, i;
00565 u_8bit_t *buf;
00566
00567
00568
00569
00570
00571
00572
00573 buf = nmalloc(MAX_PACKETSIZE + 1);
00574 r = res_mkquery(QUERY, s, C_IN, type, NULL, 0, NULL, buf, MAX_PACKETSIZE);
00575 if (r == -1) {
00576 ddebug0(RES_ERR "Query too large.");
00577 return;
00578 }
00579 hp = (packetheader *) buf;
00580 hp->id = id;
00581 for (i = 0; i < _res.nscount; i++)
00582 (void) sendto(resfd, buf, r, 0,
00583 (struct sockaddr *) &_res.nsaddr_list[i],
00584 sizeof(struct sockaddr));
00585 nfree(buf);
00586 }
00587
00588
00589
00590 static void resendrequest(struct resolve *rp, int type)
00591 {
00592 rp->sends++;
00593
00594 rp->expiretime = now + (dns_retrydelay * rp->sends);
00595
00596 linkresolve(rp);
00597
00598 if (type == T_A) {
00599 dorequest(rp->hostn, type, rp->id);
00600 ddebug1(RES_MSG "Sent domain lookup request for \"%s\".", rp->hostn);
00601 } else if (type == T_PTR) {
00602 sprintf(tempstring, "%u.%u.%u.%u.in-addr.arpa",
00603 ((u_8bit_t *) & rp->ip)[3],
00604 ((u_8bit_t *) & rp->ip)[2],
00605 ((u_8bit_t *) & rp->ip)[1], ((u_8bit_t *) & rp->ip)[0]);
00606 dorequest(tempstring, type, rp->id);
00607 ddebug1(RES_MSG "Sent domain lookup request for \"%s\".", iptostr(rp->ip));
00608 }
00609 }
00610
00611
00612
00613 static void sendrequest(struct resolve *rp, int type)
00614 {
00615
00616 do {
00617 idseed = (((idseed + idseed) | (long) time(NULL))
00618 + idseed - 0x54bad4a) ^ aseed;
00619 aseed ^= idseed;
00620 rp->id = (u_16bit_t) idseed;
00621 } while (findid(rp->id));
00622 linkresolveid(rp);
00623 resendrequest(rp, type);
00624 }
00625
00626
00627
00628
00629 static void failrp(struct resolve *rp, int type)
00630 {
00631 if (rp->state == STATE_FINISHED)
00632 return;
00633 rp->expiretime = now + dns_negcache;
00634 rp->state = STATE_FAILED;
00635
00636
00637 untieresolve(rp);
00638 linkresolve(rp);
00639
00640 ddebug0(RES_MSG "Lookup failed.");
00641 dns_event_failure(rp, type);
00642 }
00643
00644
00645
00646
00647 static void passrp(struct resolve *rp, long ttl, int type)
00648 {
00649 rp->state = STATE_FINISHED;
00650
00651
00652 if (ttl < dns_cache)
00653 rp->expiretime = now + (time_t) ttl;
00654 else
00655 rp->expiretime = now + dns_cache;
00656
00657
00658 untieresolve(rp);
00659 linkresolve(rp);
00660
00661 ddebug1(RES_MSG "Lookup successful: %s", rp->hostn);
00662 dns_event_success(rp, type);
00663 }
00664
00665
00666
00667 static void parserespacket(u_8bit_t *s, int l)
00668 {
00669 struct resolve *rp;
00670 packetheader *hp;
00671 u_8bit_t *eob;
00672 u_8bit_t *c;
00673 long ttl;
00674 int r, usefulanswer;
00675 u_16bit_t rr, datatype, class, qdatatype, qclass;
00676 u_8bit_t rdatalength;
00677
00678 if (l < sizeof(packetheader)) {
00679 debug0(RES_ERR "Packet smaller than standard header size.");
00680 return;
00681 }
00682 if (l == sizeof(packetheader)) {
00683 debug0(RES_ERR "Packet has empty body.");
00684 return;
00685 }
00686 hp = (packetheader *) s;
00687
00688
00689
00690
00691
00692 rp = findid(hp->id);
00693 if (!rp)
00694 return;
00695 if ((rp->state == STATE_FINISHED) || (rp->state == STATE_FAILED))
00696 return;
00697 hp->qdcount = ntohs(hp->qdcount);
00698 hp->ancount = ntohs(hp->ancount);
00699 hp->nscount = ntohs(hp->nscount);
00700 hp->arcount = ntohs(hp->arcount);
00701 if (getheader_tc(hp)) {
00702 ddebug0(RES_ERR "Nameserver packet truncated.");
00703 return;
00704 }
00705 if (!getheader_qr(hp)) {
00706 ddebug0(RES_ERR "Query packet received on nameserver communication "
00707 "socket.");
00708 return;
00709 }
00710 if (getheader_opcode(hp)) {
00711 ddebug0(RES_ERR "Invalid opcode in response packet.");
00712 return;
00713 }
00714 eob = s + l;
00715 c = s + HFIXEDSZ;
00716 switch (getheader_rcode(hp)) {
00717 case NOERROR:
00718 if (hp->ancount) {
00719 ddebug4(RES_MSG "Received nameserver reply. (qd:%u an:%u ns:%u ar:%u)",
00720 hp->qdcount, hp->ancount, hp->nscount, hp->arcount);
00721 if (hp->qdcount != 1) {
00722 ddebug0(RES_ERR "Reply does not contain one query.");
00723 return;
00724 }
00725 if (c > eob) {
00726 ddebug0(RES_ERR "Reply too short.");
00727 return;
00728 }
00729 switch (rp->state) {
00730 case STATE_PTRREQ:
00731 sprintf(stackstring,
00732 "%u.%u.%u.%u.in-addr.arpa",
00733 ((u_8bit_t *) & rp->ip)[3],
00734 ((u_8bit_t *) & rp->ip)[2],
00735 ((u_8bit_t *) & rp->ip)[1], ((u_8bit_t *) & rp->ip)[0]);
00736 break;
00737 case STATE_AREQ:
00738 strncpy(stackstring, rp->hostn, 1024);
00739 }
00740 *namestring = '\0';
00741 r = dn_expand(s, s + l, c, namestring, MAXDNAME);
00742 if (r == -1) {
00743 ddebug0(RES_ERR "dn_expand() failed while expanding query domain.");
00744 return;
00745 }
00746 namestring[strlen(stackstring)] = '\0';
00747 if (egg_strcasecmp(stackstring, namestring)) {
00748 ddebug2(RES_MSG "Unknown query packet dropped. (\"%s\" does not "
00749 "match \"%s\")", stackstring, namestring);
00750 return;
00751 }
00752 ddebug1(RES_MSG "Queried domain name: \"%s\"", namestring);
00753 c += r;
00754 if (c + 4 > eob) {
00755 ddebug0(RES_ERR "Query resource record truncated.");
00756 return;
00757 }
00758 qdatatype = sucknetword(c);
00759 qclass = sucknetword(c);
00760 if (qclass != C_IN) {
00761 ddebug2(RES_ERR "Received unsupported query class: %u (%s)",
00762 qclass, (qclass < CLASSTYPES_COUNT) ?
00763 classtypes[qclass] : classtypes[CLASSTYPES_COUNT]);
00764 }
00765 switch (qdatatype) {
00766 case T_PTR:
00767 if (!IS_PTR(rp)) {
00768 ddebug0(RES_WRN "Ignoring response with unexpected query type "
00769 "\"PTR\".");
00770 return;
00771 }
00772 break;
00773 case T_A:
00774 if (!IS_A(rp)) {
00775 ddebug0(RES_WRN "Ignoring response with unexpected query type "
00776 "\"PTR\".");
00777 return;
00778 }
00779 break;
00780 default:
00781 ddebug2(RES_ERR "Received unimplemented query type: %u (%s)",
00782 qdatatype, (qdatatype < RESOURCETYPES_COUNT) ?
00783 resourcetypes[qdatatype] : resourcetypes[RESOURCETYPES_COUNT]);
00784 }
00785 for (rr = hp->ancount + hp->nscount + hp->arcount; rr; rr--) {
00786 if (c > eob) {
00787 ddebug0(RES_ERR "Packet does not contain all specified resouce "
00788 "records.");
00789 return;
00790 }
00791 *namestring = '\0';
00792 r = dn_expand(s, s + l, c, namestring, MAXDNAME);
00793 if (r == -1) {
00794 ddebug0(RES_ERR "dn_expand() failed while expanding answer domain.");
00795 return;
00796 }
00797 namestring[strlen(stackstring)] = '\0';
00798 if (egg_strcasecmp(stackstring, namestring))
00799 usefulanswer = 0;
00800 else
00801 usefulanswer = 1;
00802 ddebug1(RES_MSG "answered domain query: \"%s\"", namestring);
00803 c += r;
00804 if (c + 10 > eob) {
00805 ddebug0(RES_ERR "Resource record truncated.");
00806 return;
00807 }
00808 datatype = sucknetword(c);
00809 class = sucknetword(c);
00810 ttl = sucknetlong(c);
00811 rdatalength = sucknetword(c);
00812 if (class != qclass) {
00813 ddebug2(RES_MSG "query class: %u (%s)",
00814 qclass, (qclass < CLASSTYPES_COUNT) ?
00815 classtypes[qclass] : classtypes[CLASSTYPES_COUNT]);
00816 ddebug2(RES_MSG "rr class: %u (%s)", class,
00817 (class < CLASSTYPES_COUNT) ?
00818 classtypes[class] : classtypes[CLASSTYPES_COUNT]);
00819 ddebug0(RES_ERR "Answered class does not match queried class.");
00820 return;
00821 }
00822 if (!rdatalength) {
00823 ddebug0(RES_ERR "Zero size rdata.");
00824 return;
00825 }
00826 if (c + rdatalength > eob) {
00827 ddebug0(RES_ERR "Specified rdata length exceeds packet size.");
00828 return;
00829 }
00830 if (datatype == qdatatype) {
00831 ddebug1(RES_MSG "TTL: %s", strtdiff(sendstring, ttl));
00832 ddebug1(RES_MSG "TYPE: %s", (datatype < RESOURCETYPES_COUNT) ?
00833 resourcetypes[datatype] :
00834 resourcetypes[RESOURCETYPES_COUNT]);
00835 if (usefulanswer)
00836 switch (datatype) {
00837 case T_A:
00838 if (rdatalength != 4) {
00839 ddebug1(RES_ERR "Unsupported rdata format for \"A\" type. "
00840 "(%u bytes)", rdatalength);
00841 return;
00842 }
00843 my_memcpy(&rp->ip, (IP *) c, sizeof(IP));
00844 linkresolveip(rp);
00845 passrp(rp, ttl, T_A);
00846 return;
00847 case T_PTR:
00848 *namestring = '\0';
00849 r = dn_expand(s, s + l, c, namestring, MAXDNAME);
00850 if (r == -1) {
00851 ddebug0(RES_ERR "dn_expand() failed while expanding domain in "
00852 "rdata.");
00853 return;
00854 }
00855 ddebug1(RES_MSG "Answered domain: \"%s\"", namestring);
00856 if (r > HOSTNAMELEN) {
00857 ddebug0(RES_ERR "Domain name too long.");
00858 failrp(rp, T_PTR);
00859 return;
00860 }
00861 if (!rp->hostn) {
00862 rp->hostn = nmalloc(strlen(namestring) + 1);
00863 strcpy(rp->hostn, namestring);
00864 linkresolvehost(rp);
00865 passrp(rp, ttl, T_PTR);
00866 return;
00867 }
00868 break;
00869 default:
00870 ddebug2(RES_ERR "Received unimplemented data type: %u (%s)",
00871 datatype, (datatype < RESOURCETYPES_COUNT) ?
00872 resourcetypes[datatype] :
00873 resourcetypes[RESOURCETYPES_COUNT]);
00874 }
00875 } else if (datatype == T_CNAME) {
00876 *namestring = '\0';
00877 r = dn_expand(s, s + l, c, namestring, MAXDNAME);
00878 if (r == -1) {
00879 ddebug0(RES_ERR "dn_expand() failed while expanding domain in "
00880 "rdata.");
00881 return;
00882 }
00883 ddebug1(RES_MSG "answered domain is CNAME for: %s", namestring);
00884
00885
00886
00887
00888 strncpy(stackstring, namestring, 1024);
00889 } else {
00890 ddebug2(RES_MSG "Ignoring resource type %u. (%s)",
00891 datatype, (datatype < RESOURCETYPES_COUNT) ?
00892 resourcetypes[datatype] :
00893 resourcetypes[RESOURCETYPES_COUNT]);
00894 }
00895 c += rdatalength;
00896 }
00897 } else
00898 ddebug0(RES_ERR "No error returned but no answers given.");
00899 break;
00900 case NXDOMAIN:
00901 ddebug0(RES_MSG "Host not found.");
00902 switch (rp->state) {
00903 case STATE_PTRREQ:
00904 failrp(rp, T_PTR);
00905 break;
00906 case STATE_AREQ:
00907 failrp(rp, T_A);
00908 break;
00909 default:
00910 failrp(rp, 0);
00911 break;
00912 }
00913 break;
00914 default:
00915 ddebug2(RES_MSG "Received error response %u. (%s)",
00916 getheader_rcode(hp), (getheader_rcode(hp) < RESPONSECODES_COUNT) ?
00917 responsecodes[getheader_rcode(hp)] :
00918 responsecodes[RESPONSECODES_COUNT]);
00919 }
00920 }
00921
00922
00923
00924
00925 static void dns_ack(void)
00926 {
00927 struct sockaddr_in from;
00928 unsigned int fromlen = sizeof(struct sockaddr_in);
00929 int r, i;
00930
00931 r = recvfrom(resfd, (u_8bit_t *) resrecvbuf, MAX_PACKETSIZE, 0,
00932 (struct sockaddr *) &from, &fromlen);
00933 if (r <= 0) {
00934 ddebug1(RES_MSG "Socket error: %s", strerror(errno));
00935 return;
00936 }
00937
00938 if (from.sin_addr.s_addr == localhost) {
00939 for (i = 0; i < _res.nscount; i++)
00940
00941 if ((_res.nsaddr_list[i].sin_addr.s_addr == from.sin_addr.s_addr) ||
00942 (!_res.nsaddr_list[i].sin_addr.s_addr))
00943 break;
00944 } else {
00945 for (i = 0; i < _res.nscount; i++)
00946 if (_res.nsaddr_list[i].sin_addr.s_addr == from.sin_addr.s_addr)
00947 break;
00948 }
00949 if (i == _res.nscount) {
00950 ddebug1(RES_ERR "Received reply from unknown source: %s",
00951 iptostr(from.sin_addr.s_addr));
00952 } else
00953 parserespacket((u_8bit_t *) resrecvbuf, r);
00954 }
00955
00956
00957
00958 static void dns_check_expires(void)
00959 {
00960 struct resolve *rp, *nextrp;
00961
00962
00963 for (rp = expireresolves; (rp) && (now >= rp->expiretime); rp = nextrp) {
00964 nextrp = rp->next;
00965 untieresolve(rp);
00966 switch (rp->state) {
00967 case STATE_FINISHED:
00968 case STATE_FAILED:
00969 ddebug4(RES_MSG
00970 "Cache record for \"%s\" (%s) has expired. (state: %u) Marked for expire at: %ld.",
00971 nonull(rp->hostn), iptostr(rp->ip), rp->state, rp->expiretime);
00972 unlinkresolve(rp);
00973 break;
00974 case STATE_PTRREQ:
00975 if (rp->sends <= dns_maxsends) {
00976 ddebug1(RES_MSG "Resend #%d for \"PTR\" query...", rp->sends - 1);
00977 resendrequest(rp, T_PTR);
00978 } else {
00979 ddebug0(RES_MSG "\"PTR\" query timed out.");
00980 failrp(rp, T_PTR);
00981 }
00982 break;
00983 case STATE_AREQ:
00984 if (rp->sends <= dns_maxsends) {
00985 ddebug1(RES_MSG "Resend #%d for \"A\" query...", rp->sends - 1);
00986 resendrequest(rp, T_A);
00987 } else {
00988 ddebug0(RES_MSG "\"A\" query timed out.");
00989 failrp(rp, T_A);
00990 }
00991 break;
00992 default:
00993 ddebug1(RES_WRN "Unknown request state %d. Request expired.", rp->state);
00994 failrp(rp, 0);
00995 }
00996 }
00997 }
00998
00999
01000
01001 static void dns_lookup(IP ip)
01002 {
01003 struct resolve *rp;
01004
01005 ip = htonl(ip);
01006 if ((rp = findip(ip))) {
01007 if (rp->state == STATE_FINISHED || rp->state == STATE_FAILED) {
01008 if (rp->state == STATE_FINISHED && rp->hostn) {
01009 ddebug2(RES_MSG "Used cached record: %s == \"%s\".",
01010 iptostr(ip), rp->hostn);
01011 dns_event_success(rp, T_PTR);
01012 } else {
01013 ddebug1(RES_MSG "Used failed record: %s == ???", iptostr(ip));
01014 dns_event_failure(rp, T_PTR);
01015 }
01016 }
01017 return;
01018 }
01019
01020 ddebug0(RES_MSG "Creating new record");
01021 rp = allocresolve();
01022 rp->state = STATE_PTRREQ;
01023 rp->sends = 1;
01024 rp->ip = ip;
01025 linkresolveip(rp);
01026 sendrequest(rp, T_PTR);
01027 }
01028
01029
01030
01031 static void dns_forward(char *hostn)
01032 {
01033 struct resolve *rp;
01034 struct in_addr inaddr;
01035
01036
01037
01038
01039 if (egg_inet_aton(hostn, &inaddr)) {
01040 call_ipbyhost(hostn, ntohl(inaddr.s_addr), 1);
01041 return;
01042 }
01043 if ((rp = findhost(hostn))) {
01044 if (rp->state == STATE_FINISHED || rp->state == STATE_FAILED) {
01045 if (rp->state == STATE_FINISHED && rp->ip) {
01046 ddebug2(RES_MSG "Used cached record: %s == \"%s\".", hostn,
01047 iptostr(rp->ip));
01048 dns_event_success(rp, T_A);
01049 } else {
01050 ddebug1(RES_MSG "Used failed record: %s == ???", hostn);
01051 dns_event_failure(rp, T_A);
01052 }
01053 }
01054 return;
01055 }
01056 ddebug0(RES_MSG "Creating new record");
01057 rp = allocresolve();
01058 rp->state = STATE_AREQ;
01059 rp->sends = 1;
01060 rp->hostn = nmalloc(strlen(hostn) + 1);
01061 strcpy(rp->hostn, hostn);
01062 linkresolvehost(rp);
01063 sendrequest(rp, T_A);
01064 }
01065
01066
01067
01068 static int init_dns_network(void)
01069 {
01070 int option;
01071 struct in_addr inaddr;
01072
01073 resfd = socket(AF_INET, SOCK_DGRAM, 0);
01074 if (resfd == -1) {
01075 putlog(LOG_MISC, "*",
01076 "Unable to allocate socket for nameserver communication: %s",
01077 strerror(errno));
01078 return 0;
01079 }
01080 if (allocsock(resfd, SOCK_PASS) == -1) {
01081 putlog(LOG_MISC, "*",
01082 "Unable to allocate socket in socklist for nameserver communication");
01083 killsock(resfd);
01084 return 0;
01085 }
01086 option = 1;
01087 if (setsockopt(resfd, SOL_SOCKET, SO_BROADCAST, (char *) &option,
01088 sizeof(option))) {
01089 putlog(LOG_MISC, "*",
01090 "Unable to setsockopt() on nameserver communication socket: %s",
01091 strerror(errno));
01092 killsock(resfd);
01093 return 0;
01094 }
01095
01096 egg_inet_aton("127.0.0.1", &inaddr);
01097 localhost = inaddr.s_addr;
01098 return 1;
01099 }
01100
01101
01102
01103 static int init_dns_core(void)
01104 {
01105 int i;
01106
01107
01108 res_init();
01109 if (!_res.nscount)
01110 putlog(LOG_MISC, "*", "No nameservers found.");
01111 _res.options |= RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
01112 for (i = 0; i < _res.nscount; i++)
01113 _res.nsaddr_list[i].sin_family = AF_INET;
01114
01115 if (!init_dns_network())
01116 return 0;
01117
01118
01119 aseed = time(NULL) ^ (time(NULL) << 3) ^ (u_32bit_t) getpid();
01120 for (i = 0; i < BASH_SIZE; i++) {
01121 idbash[i] = NULL;
01122 ipbash[i] = NULL;
01123 hostbash[i] = NULL;
01124 }
01125 expireresolves = NULL;
01126 return 1;
01127 }