/***************************************************************************
zonetransfer.cpp - handle zone transfers
-------------------
begin : za apr 19 2003
copyright : (C) 2003 by Meilof
email : meilof@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "zonetransfer.h"
#include "zones.h"
#include "primary.h"
#include <poslib/server/configuration.h>
DnsMessage *answer_axfr(pending_query *q) {
char lb[2];
DnsMessage *a = new DnsMessage();
a->ID = q->message->ID;
/* look up corresponding primary zone */
domainname retdom;
AuthMemZone *z;
pthread_mutex_lock(&m_auth_zones);
/* we don't need to look up dynamic zones (yet)? */
z = (AuthMemZone*)lookup_authoritative_zone(q->message->questions.begin()->QNAME, &retdom, false);
if (!z || (z->type != Z_PRIMARY && z->type != Z_SECONDARY) ||
q->message->questions.begin()->QNAME != retdom) {
/* no such primary zone: return NOTIMP */
a->RCODE = RCODE_NOTIMP;
pthread_mutex_unlock(&m_auth_zones);
return a;
}
/* see if the zone is actually loaded */
if (!z->zone_is_loaded) {
a->RCODE = RCODE_SRVFAIL;
pthread_mutex_unlock(&m_auth_zones);
return a;
}
/* at first, determine whether to allow a zone transfer to this client */
if ((z->allow_xfr == PrimaryZone::slaves_only &&
!in_addr_list(z->slaves, &q->querier) && !in_addrrange_list(z->allow_xfr_to, &q->querier)) ||
(z->allow_xfr == PrimaryZone::none && !in_addrrange_list(z->allow_xfr_to, &q->querier))) {
a->RCODE = RCODE_REFUSED;
pthread_mutex_unlock(&m_auth_zones);
return a;
}
/* allright, do the zone transfer */
/* try to build file */
if (!datadir || !datadir[0]) {
pos_log(context_zonedata, log_error, "%s: Request for zone transfer could not be answered because we have no data dir!", z->getfqdn().tocstr());
a->RCODE = RCODE_SRVFAIL;
pthread_mutex_unlock(&m_auth_zones);
return a;
}
FILE *f;
char filename[PATH_MAX];
char serial[16];
sprintf(serial, "%d", z->serial);
domainname dom = z->getfqdn();
stl_string domstr = dom.tostring();
if (strlen(datadir) + strlen(serial) + strlen(domstr.c_str()) >= sizeof(filename)) {
pos_log(context_zonedata, log_error, "%s: Request for zone transfer could not be answered because data dir name is too long!", z->getfqdn().tocstr());
a->RCODE = RCODE_SRVFAIL;
pthread_mutex_unlock(&m_auth_zones);
return a;
}
strcpy(filename, datadir);
strcat(filename, domstr.c_str());
strcat(filename, serial);
f = try_fopen(filename, "rb");
if (!f) {
pos_log(context_zonedata, log_error, "%s: Could not open data file %s", z->getfqdn().tocstr(), filename);
a->RCODE = RCODE_SRVFAIL;
pthread_mutex_unlock(&m_auth_zones);
return a;
}
pthread_mutex_unlock(&m_auth_zones);
delete a; a = NULL;
char *buff = NULL;
char rrbuff[65535];
int len, pos = 0, offset = 0, x = 0, n;
bool is_soa = false;
try {
/* we'll build the answer DNS message ourself for performance reasons
and because we're reading from a binary zone file either way */
if (q->transport == T_UDP) {
len = UDP_MSG_SIZE;
buff = (char *)malloc(UDP_MSG_SIZE);
} else if (q->transport == T_TCP) {
len = 65537;
buff = (char *)malloc(65537);
offset = 2;
} else {
return NULL;
}
while (!feof(f)) {
pos = offset;
buff[pos] = q->message->ID / 256;
buff[pos+1] = q->message->ID % 256;
buff[pos+2] = 132; /* QR|AA|QUERY*/
buff[pos+3] = 0; /* NOERROR */
memset(buff+pos+4, 0, 8);
pos += 12;
n = 0;
while (1) {
/* read item */
if (x) {
memcpy(buff + pos, rrbuff, x);
pos += x; n++;
x = 0;
continue;
} else if (feof(f)) {
/* switch back to SOA */
is_soa = true;
fseek(f, 0, SEEK_SET);
}
if ((x = fread(lb, 1, 2, f)) < 2) {
if (feof(f)) {
if (is_soa) break; else {
is_soa = true;
fseek(f, 0, SEEK_SET);
if (fread(lb, 1, 2, f) < 2) break;
}
} else throw PException("");
}
x = uint16_value(lb);
if (fread(rrbuff, 1, x, f) != (size_t)x) throw PException("");
if (pos + x < len) {
memcpy(buff + pos, rrbuff, x);
pos += x; n++; x = 0;
if (is_soa) {
fseek(f, 0, SEEK_END);
break;
}
continue;
} else break;
}
/* send message */
buff[offset+6] = n / 256;
buff[offset+7] = n % 256;
if (q->transport == T_TCP) {
buff[0] = (pos - 2) / 256;
buff[1] = (pos - 2) % 256;
tcpsendall(q->sockid, buff, pos, conf_get_tcp_io_timeout());
} else {
udpsend(q->sockid, buff, pos, &q->querier);
return NULL;
}
}
} catch (PException p) {
if (buff) free(buff);
if (f) fclose(f);
a = new DnsMessage();
a->ID = q->message->ID;
a->RCODE = RCODE_SRVFAIL;
return a;
}
if (f) fclose(f);
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1