📄 update.c
字号:
/************************************************************************************************** $Id: update.c,v 1.10 2005/12/18 19:16:41 bboy Exp $ update.c: Code to implement RFC 2136 (DNS UPDATE) Copyright (C) 2005 Don Moore <bboy@bboy.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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA**************************************************************************************************/#include "named.h"/* Make this nonzero to enable debugging for this source file */#define DEBUG_UPDATE 1#define DEBUG_UPDATE_SQL 0typedef struct _update_query_rr{ char name[DNS_MAXNAMELEN]; dns_qtype_t type; dns_class_t class; uint32_t ttl; uint16_t rdlength; unsigned char rdata[DNS_MAXPACKETLEN_UDP + 1];} UQRR;/* This is the temporary RRset described in RFC 2136, 3.2.3 */typedef struct _update_temp_rrset{ char name[DNS_MAXNAMELEN]; dns_qtype_t type; char data[DNS_MAXPACKETLEN_UDP + 1]; uint32_t aux; int checked; /* Have we checked this unique name/type? */} TMPRR;typedef struct _update_query{ /* Zone section */ char name[DNS_MAXNAMELEN]; /* The zone name */ dns_qtype_t type; /* Must be DNS_QTYPE_SOA */ dns_class_t class; /* The zone's class */ UQRR *PR; /* Prerequisite section RRs */ int numPR; /* Number of items in 'PR' */ UQRR *UP; /* Update section RRs */ int numUP; /* Number of items in 'UP' */ UQRR *AD; /* Additional data section RRs */ int numAD; /* Number of items in 'AD' */ TMPRR **tmprr; /* Temporary RR list for prerequisite */ int num_tmprr; /* Number of items in "tmprr" */} UQ;/************************************************************************************************** FREE_UQ Frees a 'UQ' structure.**************************************************************************************************/static voidfree_uq(UQ *uq){ Free(uq->PR); Free(uq->UP); Free(uq->AD); if (uq->num_tmprr) { int n; for (n = 0; n < uq->num_tmprr; n++) Free(uq->tmprr[n]); Free(uq->tmprr); } Free(uq);}/*--- free_uq() ---------------------------------------------------------------------------------*//************************************************************************************************** UPDATE_TRANSACTION Start/commit/rollback a transaction for the UPDATE queries. Returns 0 on success, -1 on failure.**************************************************************************************************/static intupdate_transaction(TASK *t, const char *query){ if (sql_nrquery(sql, query, strlen(query)) != 0) { WarnSQL(sql, "%s: %s", desctask(t), _("error deleting all RRsets via DNS UPDATE")); return dnserror(t, DNS_RCODE_SERVFAIL, ERR_DB_ERROR); } return 0;}/*--- update_transaction() ----------------------------------------------------------------------*//************************************************************************************************** CHECK_UPDATE If the "update" column exists in the soa table, it should contain a list of wildcards separated by commas. In order for the DNS UPDATE to continue, one of the wildcards must match the client's IP address. Returns 0 if okay, -1 if denied.**************************************************************************************************/static intcheck_update(TASK *t, MYDNS_SOA *soa){ SQL_RES *res = NULL; SQL_ROW row; char ip[256]; char query[512]; size_t querylen; int ok = 0; /* If the 'soa' table does not have an 'update' column, listing access rules, allow DNS UPDATE only from 127.0.0.1 */ /* TODO: Allow from all listening addresses */ if (!mydns_soa_use_update_acl) { strncpy(ip, clientaddr(t), sizeof(ip)-1); if (!strcmp(ip, "127.0.0.1")) /* OK from localhost */ return 0; return dnserror(t, DNS_RCODE_REFUSED, ERR_NO_UPDATE); } strncpy(ip, clientaddr(t), sizeof(ip)-1); querylen = snprintf(query, sizeof(query), "SELECT update_acl FROM %s WHERE id=%u%s", mydns_soa_table_name, soa->id, mydns_rr_use_active ? " AND active=1" : "");#if DEBUG_UPDATE_SQL Verbose("%s: DNS UPDATE: %s", desctask(t), query);#endif if (!(res = sql_query(sql, query, querylen))) ErrSQL(sql, "%s: %s", desctask(t), _("error loading DNS UPDATE access rules")); if ((row = sql_getrow(res))) { char *wild, *r;#if DEBUG_ENABLED && DEBUG_UPDATE Debug("%s: checking DNS UPDATE access rule '%s'", desctask(t), row[0]);#endif for (r = row[0]; !ok && (wild = strsep(&r, ",")); ) { if (strchr(wild, '/')) { if (t->family == AF_INET) ok = in_cidr(wild, t->addr4.sin_addr); } else if (wildcard_match(wild, ip)) ok = 1; } } sql_free(res); if (!ok) return dnserror(t, DNS_RCODE_REFUSED, ERR_NO_UPDATE); return 0;}/*--- check_update() ----------------------------------------------------------------------------*/#if DEBUG_ENABLED && DEBUG_UPDATE/************************************************************************************************** UPDATE_RRDUMP**************************************************************************************************/static voidupdate_rrdump(TASK *t, char *section, int which, UQRR *rr){ char buf[BUFSIZ] = "", *b = buf; int n; for (n = 0; n < rr->rdlength; n++) { if (isalnum(rr->rdata[n])) b += sprintf(b, "%c", rr->rdata[n]); else b += sprintf(b, "<%d>", rr->rdata[n]); } Debug("%s: DNS UPDATE: >>> %s %d: name=[%s] type=%s class=%s ttl=%u rdlength=%u rdata=[%s]", desctask(t), section, which, rr->name, mydns_qtype_str(rr->type), mydns_class_str(rr->class), rr->ttl, rr->rdlength, buf);}/*--- update_rrdump() ---------------------------------------------------------------------------*/#endif/************************************************************************************************** UPDATE_GOBBLE_RR Reads the next RR from the query. Returns the new source or NULL on error.**************************************************************************************************/static char *update_gobble_rr(TASK *t, MYDNS_SOA *soa, char *query, size_t querylen, char *current, UQRR *rr){ char *src = current; if (!(src = name_unencode(query, querylen, src, rr->name, sizeof(rr->name)))) { formerr(t, DNS_RCODE_FORMERR, (task_error_t)rr->name[0], NULL); return NULL; } DNS_GET16(rr->type, src); DNS_GET16(rr->class, src); DNS_GET32(rr->ttl, src); DNS_GET16(rr->rdlength, src); memcpy(rr->rdata, src, rr->rdlength); src += rr->rdlength; return src;}/*--- update_gobble_rr() ------------------------------------------------------------------------*//************************************************************************************************** PARSE_UPDATE_QUERY Parses the various sections of the update query. Returns 0 on success, -1 on error.**************************************************************************************************/static intparse_update_query(TASK *t, MYDNS_SOA *soa, UQ *q){ char *query = t->query; /* Start of query section */ int querylen = t->len; /* Length of 'query' */ char *src = query + DNS_HEADERSIZE; /* Current position in 'query' */ int n; /* ** Zone section (RFC 2136 2.3) */ if (!(src = name_unencode(query, querylen, src, q->name, sizeof(q->name)))) return formerr(t, DNS_RCODE_FORMERR, (task_error_t)q->name[0], NULL); DNS_GET16(q->type, src); DNS_GET16(q->class, src);#if DEBUG_ENABLED && DEBUG_UPDATE Debug("%s: ZONE: name=[%s] type=%s class=%s", desctask(t), q->name, mydns_qtype_str(q->type), mydns_class_str(q->class));#endif /* ZONE: Must contain exactly one RR with type SOA (RFC 2136 3.1.1) */ if (t->qdcount != 1) return dnserror(t, DNS_RCODE_FORMERR, ERR_MULTI_QUESTIONS); if (q->type != DNS_QTYPE_SOA) return dnserror(t, DNS_RCODE_FORMERR, ERR_INVALID_TYPE); /* ** Prerequisite section (RFC 2136 2.4) ** These records are in normal RR format (RFC 1035 4.1.3) */ q->numPR = t->ancount; if (!(q->PR = calloc(q->numPR, sizeof(UQRR)))) Err(_("out of memory")); for (n = 0; n < q->numPR; n++) if (!(src = update_gobble_rr(t, soa, query, querylen, src, &q->PR[n]))) return -1;#if DEBUG_ENABLED && DEBUG_UPDATE for (n = 0; n < q->numPR; n++) update_rrdump(t, "PREREQ", n, &q->PR[n]);#endif /* ** Update section (RFC 2136 2.5) ** These records are in normal RR format (RFC 1035 4.1.3) */ q->numUP = t->nscount; if (!(q->UP = calloc(q->numUP, sizeof(UQRR)))) Err(_("out of memory")); for (n = 0; n < q->numUP; n++) if (!(src = update_gobble_rr(t, soa, query, querylen, src, &q->UP[n]))) return -1;#if DEBUG_ENABLED && DEBUG_UPDATE for (n = 0; n < q->numUP; n++) update_rrdump(t, "UPDATE", n, &q->UP[n]);#endif /* ** Additional data section (RFC 2136 2.6) ** These records are in normal RR format (RFC 1035 4.1.3) */ q->numAD = t->arcount; if (!(q->AD = calloc(q->numAD, sizeof(UQRR)))) Err(_("out of memory")); for (n = 0; n < q->numAD; n++) if (!(src = update_gobble_rr(t, soa, query, querylen, src, &q->AD[n]))) return -1;#if DEBUG_ENABLED && DEBUG_UPDATE for (n = 0; n < q->numAD; n++) update_rrdump(t, " ADD'L", n, &q->AD[n]);#endif return 0;}/*--- parse_update_query() ----------------------------------------------------------------------*//************************************************************************************************** TEXT_RETRIEVE Retrieve a name from the source without end-dot encoding.**************************************************************************************************/static char *text_retrieve(char *src, char *end, char *data, size_t datalen, int one_word_only){ int n, x; /* Offset in 'data' */ for (n = 0; src < end && n < datalen; ) { int len = *src++; if (n) data[n++] = ' '; for (x = 0; x < len && src < end && n < datalen; x++) data[n++] = *src++; if (one_word_only) { data[n] = '\0'; return (src); } } data[n] = '\0'; return (src);}/*--- text_retrieve() ---------------------------------------------------------------------------*//************************************************************************************************** UPDATE_GET_RR_DATA Sets 'data' and 'aux'. Returns 0 on success, -1 on error.**************************************************************************************************/static intupdate_get_rr_data(TASK *t, MYDNS_SOA *soa, UQ *q, UQRR *rr, char *data, size_t datalen, uint32_t *aux){ char *src = rr->rdata; char *end = rr->rdata + rr->rdlength; memset(data, 0, datalen); *aux = 0; if (!rr->rdlength) return -1; switch (rr->type) { case DNS_QTYPE_A: if (rr->rdlength != 4) return -1; snprintf(data, datalen, "%d.%d.%d.%d", rr->rdata[0], rr->rdata[1], rr->rdata[2], rr->rdata[3]); return 0; case DNS_QTYPE_AAAA: if (rr->rdlength != 16) return -1; if (!(inet_ntop(AF_INET6, &rr->rdata, data, datalen - 1))) return dnserror(t, DNS_RCODE_FORMERR, ERR_INVALID_ADDRESS); return 0; case DNS_QTYPE_CNAME: if (!(src = name_unencode(t->query, t->len, src, data, datalen))) return formerr(t, DNS_RCODE_FORMERR, (task_error_t)data[0], NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -