summaryrefslogtreecommitdiffstats
path: root/lib/addns/dnsrecord.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/addns/dnsrecord.c')
-rw-r--r--lib/addns/dnsrecord.c484
1 files changed, 484 insertions, 0 deletions
diff --git a/lib/addns/dnsrecord.c b/lib/addns/dnsrecord.c
new file mode 100644
index 0000000..c1a6595
--- /dev/null
+++ b/lib/addns/dnsrecord.c
@@ -0,0 +1,484 @@
+/*
+ Linux DNS client library implementation
+ Copyright (C) 2006 Krishna Ganugapati <krishnag@centeris.com>
+ Copyright (C) 2006 Gerald Carter <jerry@samba.org>
+
+ ** NOTE! The following LGPL license applies to the libaddns
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dns.h"
+#include "lib/util/genrand.h"
+
+DNS_ERROR dns_create_query( TALLOC_CTX *mem_ctx, const char *name,
+ uint16_t q_type, uint16_t q_class,
+ struct dns_request **preq )
+{
+ struct dns_request *req = NULL;
+ struct dns_question *q = NULL;
+ DNS_ERROR err;
+
+ if (!(req = talloc_zero(mem_ctx, struct dns_request)) ||
+ !(req->questions = talloc_array(req, struct dns_question *, 1)) ||
+ !(req->questions[0] = talloc(req->questions,
+ struct dns_question))) {
+ TALLOC_FREE(req);
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ generate_random_buffer((uint8_t *)&req->id, sizeof(req->id));
+
+ req->num_questions = 1;
+ q = req->questions[0];
+
+ err = dns_domain_name_from_string(q, name, &q->name);
+ if (!ERR_DNS_IS_OK(err)) {
+ TALLOC_FREE(req);
+ return err;
+ }
+
+ q->q_type = q_type;
+ q->q_class = q_class;
+
+ *preq = req;
+ return ERROR_DNS_SUCCESS;
+}
+
+DNS_ERROR dns_create_update( TALLOC_CTX *mem_ctx, const char *name,
+ struct dns_update_request **preq )
+{
+ struct dns_update_request *req = NULL;
+ struct dns_zone *z = NULL;
+ DNS_ERROR err;
+
+ if (!(req = talloc_zero(mem_ctx, struct dns_update_request)) ||
+ !(req->zones = talloc_array(req, struct dns_zone *, 1)) ||
+ !(req->zones[0] = talloc(req->zones, struct dns_zone))) {
+ TALLOC_FREE(req);
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ req->id = random();
+ req->flags = 0x2800; /* Dynamic update */
+
+ req->num_zones = 1;
+ z = req->zones[0];
+
+ err = dns_domain_name_from_string(z, name, &z->name);
+ if (!ERR_DNS_IS_OK(err)) {
+ TALLOC_FREE(req);
+ return err;
+ }
+
+ z->z_type = QTYPE_SOA;
+ z->z_class = DNS_CLASS_IN;
+
+ *preq = req;
+ return ERROR_DNS_SUCCESS;
+}
+
+DNS_ERROR dns_create_rrec(TALLOC_CTX *mem_ctx, const char *name,
+ uint16_t type, uint16_t r_class, uint32_t ttl,
+ uint16_t data_length, uint8_t *data,
+ struct dns_rrec **prec)
+{
+ struct dns_rrec *rec = NULL;
+ DNS_ERROR err;
+
+ if (!(rec = talloc(mem_ctx, struct dns_rrec))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_domain_name_from_string(rec, name, &rec->name);
+ if (!(ERR_DNS_IS_OK(err))) {
+ TALLOC_FREE(rec);
+ return err;
+ }
+
+ rec->type = type;
+ rec->r_class = r_class;
+ rec->ttl = ttl;
+ rec->data_length = data_length;
+ rec->data = talloc_move(rec, &data);
+
+ *prec = rec;
+ return ERROR_DNS_SUCCESS;
+}
+
+DNS_ERROR dns_create_a_record(TALLOC_CTX *mem_ctx, const char *host,
+ uint32_t ttl, const struct sockaddr_storage *pss,
+ struct dns_rrec **prec)
+{
+ uint8_t *data;
+ DNS_ERROR err;
+ struct in_addr ip;
+
+ if (pss->ss_family != AF_INET) {
+ return ERROR_DNS_INVALID_PARAMETER;
+ }
+
+ ip = ((const struct sockaddr_in *)pss)->sin_addr;
+ if (!(data = (uint8_t *)talloc_memdup(mem_ctx, (const void *)&ip.s_addr,
+ sizeof(ip.s_addr)))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_create_rrec(mem_ctx, host, QTYPE_A, DNS_CLASS_IN, ttl,
+ sizeof(ip.s_addr), data, prec);
+
+ if (!ERR_DNS_IS_OK(err)) {
+ TALLOC_FREE(data);
+ }
+
+ return err;
+}
+
+DNS_ERROR dns_create_aaaa_record(TALLOC_CTX *mem_ctx, const char *host,
+ uint32_t ttl, const struct sockaddr_storage *pss,
+ struct dns_rrec **prec)
+{
+#ifdef HAVE_IPV6
+ uint8_t *data;
+ DNS_ERROR err;
+ struct in6_addr ip6;
+
+ if (pss->ss_family != AF_INET6) {
+ return ERROR_DNS_INVALID_PARAMETER;
+ }
+
+ ip6 = ((const struct sockaddr_in6 *)pss)->sin6_addr;
+ if (!(data = (uint8_t *)talloc_memdup(mem_ctx, (const void *)&ip6.s6_addr,
+ sizeof(ip6.s6_addr)))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_create_rrec(mem_ctx, host, QTYPE_AAAA, DNS_CLASS_IN, ttl,
+ sizeof(ip6.s6_addr), data, prec);
+
+ if (!ERR_DNS_IS_OK(err)) {
+ TALLOC_FREE(data);
+ }
+
+ return err;
+#else
+ return ERROR_DNS_INVALID_PARAMETER;
+#endif
+}
+
+DNS_ERROR dns_create_name_in_use_record(TALLOC_CTX *mem_ctx,
+ const char *name,
+ const struct sockaddr_storage *ss,
+ struct dns_rrec **prec)
+{
+ if (ss != NULL) {
+ switch (ss->ss_family) {
+ case AF_INET:
+ return dns_create_a_record(mem_ctx, name, 0, ss, prec);
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ return dns_create_aaaa_record(mem_ctx, name, 0, ss, prec);
+#endif
+ default:
+ return ERROR_DNS_INVALID_PARAMETER;
+ }
+ }
+
+ return dns_create_rrec(mem_ctx, name, QTYPE_ANY, DNS_CLASS_IN, 0, 0,
+ NULL, prec);
+}
+
+DNS_ERROR dns_create_name_not_in_use_record(TALLOC_CTX *mem_ctx,
+ const char *name, uint32_t type,
+ struct dns_rrec **prec)
+{
+ return dns_create_rrec(mem_ctx, name, type, DNS_CLASS_NONE, 0,
+ 0, NULL, prec);
+}
+
+DNS_ERROR dns_create_delete_record(TALLOC_CTX *mem_ctx, const char *name,
+ uint16_t type, uint16_t r_class,
+ struct dns_rrec **prec)
+{
+ return dns_create_rrec(mem_ctx, name, type, r_class, 0, 0, NULL, prec);
+}
+
+DNS_ERROR dns_create_tkey_record(TALLOC_CTX *mem_ctx, const char *keyname,
+ const char *algorithm_name, time_t inception,
+ time_t expiration, uint16_t mode, uint16_t error,
+ uint16_t key_length, const uint8_t *key,
+ struct dns_rrec **prec)
+{
+ struct dns_buffer *buf = NULL;
+ struct dns_domain_name *algorithm = NULL;
+ DNS_ERROR err;
+
+ if (!(buf = dns_create_buffer(mem_ctx))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_domain_name_from_string(buf, algorithm_name, &algorithm);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ dns_marshall_domain_name(buf, algorithm);
+ dns_marshall_uint32(buf, inception);
+ dns_marshall_uint32(buf, expiration);
+ dns_marshall_uint16(buf, mode);
+ dns_marshall_uint16(buf, error);
+ dns_marshall_uint16(buf, key_length);
+ dns_marshall_buffer(buf, key, key_length);
+ dns_marshall_uint16(buf, 0); /* Other Size */
+
+ if (!ERR_DNS_IS_OK(buf->error)) {
+ err = buf->error;
+ goto error;
+ }
+
+ err = dns_create_rrec(mem_ctx, keyname, QTYPE_TKEY, DNS_CLASS_ANY, 0,
+ buf->offset, buf->data, prec);
+
+ error:
+ TALLOC_FREE(buf);
+ return err;
+}
+
+DNS_ERROR dns_unmarshall_tkey_record(TALLOC_CTX *mem_ctx, struct dns_rrec *rec,
+ struct dns_tkey_record **ptkey)
+{
+ struct dns_tkey_record *tkey;
+ struct dns_buffer buf;
+ uint32_t tmp_inception, tmp_expiration;
+
+ if (!(tkey = talloc(mem_ctx, struct dns_tkey_record))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ buf.data = rec->data;
+ buf.size = rec->data_length;
+ buf.offset = 0;
+ buf.error = ERROR_DNS_SUCCESS;
+
+ dns_unmarshall_domain_name(tkey, &buf, &tkey->algorithm);
+ dns_unmarshall_uint32(&buf, &tmp_inception);
+ dns_unmarshall_uint32(&buf, &tmp_expiration);
+ dns_unmarshall_uint16(&buf, &tkey->mode);
+ dns_unmarshall_uint16(&buf, &tkey->error);
+ dns_unmarshall_uint16(&buf, &tkey->key_length);
+
+ if (!ERR_DNS_IS_OK(buf.error)) goto error;
+
+ if (tkey->key_length) {
+ if (!(tkey->key = talloc_array(tkey, uint8_t, tkey->key_length))) {
+ buf.error = ERROR_DNS_NO_MEMORY;
+ goto error;
+ }
+ } else {
+ tkey->key = NULL;
+ }
+
+ dns_unmarshall_buffer(&buf, tkey->key, tkey->key_length);
+ if (!ERR_DNS_IS_OK(buf.error)) goto error;
+
+ tkey->inception = (time_t)tmp_inception;
+ tkey->expiration = (time_t)tmp_expiration;
+
+ *ptkey = tkey;
+ return ERROR_DNS_SUCCESS;
+
+ error:
+ TALLOC_FREE(tkey);
+ return buf.error;
+}
+
+DNS_ERROR dns_create_tsig_record(TALLOC_CTX *mem_ctx, const char *keyname,
+ const char *algorithm_name,
+ time_t time_signed, uint16_t fudge,
+ uint16_t mac_length, const uint8_t *mac,
+ uint16_t original_id, uint16_t error,
+ struct dns_rrec **prec)
+{
+ struct dns_buffer *buf = NULL;
+ struct dns_domain_name *algorithm = NULL;
+ DNS_ERROR err;
+
+ if (!(buf = dns_create_buffer(mem_ctx))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_domain_name_from_string(buf, algorithm_name, &algorithm);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ dns_marshall_domain_name(buf, algorithm);
+ dns_marshall_uint16(buf, 0); /* time prefix */
+ dns_marshall_uint32(buf, time_signed);
+ dns_marshall_uint16(buf, fudge);
+ dns_marshall_uint16(buf, mac_length);
+ dns_marshall_buffer(buf, mac, mac_length);
+ dns_marshall_uint16(buf, original_id);
+ dns_marshall_uint16(buf, error);
+ dns_marshall_uint16(buf, 0); /* Other Size */
+
+ if (!ERR_DNS_IS_OK(buf->error)) {
+ err = buf->error;
+ goto error;
+ }
+
+ err = dns_create_rrec(mem_ctx, keyname, QTYPE_TSIG, DNS_CLASS_ANY, 0,
+ buf->offset, buf->data, prec);
+
+ error:
+ TALLOC_FREE(buf);
+ return err;
+}
+
+DNS_ERROR dns_add_rrec(TALLOC_CTX *mem_ctx, struct dns_rrec *rec,
+ uint16_t *num_records, struct dns_rrec ***records)
+{
+ struct dns_rrec **new_records;
+
+ if (!(new_records = talloc_realloc(mem_ctx, *records,
+ struct dns_rrec *,
+ (*num_records)+1))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ new_records[*num_records] = talloc_move(new_records, &rec);
+
+ *num_records += 1;
+ *records = new_records;
+ return ERROR_DNS_SUCCESS;
+}
+
+/*
+ * Create a request that probes a server whether the list of IP addresses
+ * provides meets our expectations
+ */
+
+DNS_ERROR dns_create_probe(TALLOC_CTX *mem_ctx, const char *zone,
+ const char *host, int num_ips,
+ const struct sockaddr_storage *sslist,
+ struct dns_update_request **preq)
+{
+ struct dns_update_request *req = NULL;
+ struct dns_rrec *rec = NULL;
+ DNS_ERROR err;
+ uint16_t i;
+
+ err = dns_create_update(mem_ctx, zone, &req);
+ if (!ERR_DNS_IS_OK(err)) return err;
+
+ err = dns_create_name_not_in_use_record(req, host, QTYPE_CNAME, &rec);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_preqs, &req->preqs);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ for (i=0; i<num_ips; i++) {
+ err = dns_create_name_in_use_record(req, host,
+ &sslist[i], &rec);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_preqs, &req->preqs);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+ }
+
+ *preq = req;
+ return ERROR_DNS_SUCCESS;
+
+ error:
+ TALLOC_FREE(req);
+ return err;
+}
+
+DNS_ERROR dns_create_update_request(TALLOC_CTX *mem_ctx,
+ const char *domainname,
+ const char *hostname,
+ const struct sockaddr_storage *ss_addrs,
+ size_t num_addrs,
+ uint32_t ttl,
+ struct dns_update_request **preq)
+{
+ struct dns_update_request *req = NULL;
+ struct dns_rrec *rec = NULL;
+ DNS_ERROR err;
+ size_t i;
+
+ err = dns_create_update(mem_ctx, domainname, &req);
+ if (!ERR_DNS_IS_OK(err)) return err;
+
+ /*
+ * Use the same prereq as WinXP -- No CNAME records for this host.
+ */
+
+ err = dns_create_rrec(req, hostname, QTYPE_CNAME, DNS_CLASS_NONE,
+ 0, 0, NULL, &rec);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_preqs, &req->preqs);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ /*
+ * Delete all existing RRsets from our name
+ */
+
+ err = dns_create_delete_record(req, hostname, QTYPE_ANY, DNS_CLASS_ANY,
+ &rec);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_updates, &req->updates);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ /*
+ * .. and add our IPs
+ */
+
+ for ( i=0; i<num_addrs; i++ ) {
+
+ switch(ss_addrs[i].ss_family) {
+ case AF_INET:
+ err = dns_create_a_record(req,
+ hostname,
+ ttl,
+ &ss_addrs[i],
+ &rec);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ err = dns_create_aaaa_record(req,
+ hostname,
+ ttl,
+ &ss_addrs[i],
+ &rec);
+ break;
+#endif
+ default:
+ continue;
+ }
+ if (!ERR_DNS_IS_OK(err))
+ goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_updates, &req->updates);
+ if (!ERR_DNS_IS_OK(err))
+ goto error;
+ }
+
+ *preq = req;
+ return ERROR_DNS_SUCCESS;
+
+ error:
+ TALLOC_FREE(req);
+ return err;
+}