summaryrefslogtreecommitdiffstats
path: root/lib/addns
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /lib/addns
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/addns')
-rw-r--r--lib/addns/dns.h366
-rw-r--r--lib/addns/dnserr.h87
-rw-r--r--lib/addns/dnsgss.c344
-rw-r--r--lib/addns/dnsmarshall.c534
-rw-r--r--lib/addns/dnsquery.c782
-rw-r--r--lib/addns/dnsquery.h84
-rw-r--r--lib/addns/dnsquery_srv.c560
-rw-r--r--lib/addns/dnsquery_srv.h54
-rw-r--r--lib/addns/dnsrecord.c484
-rw-r--r--lib/addns/dnssock.c420
-rw-r--r--lib/addns/dnsutils.c149
-rw-r--r--lib/addns/error.c59
-rw-r--r--lib/addns/wscript_build16
13 files changed, 3939 insertions, 0 deletions
diff --git a/lib/addns/dns.h b/lib/addns/dns.h
new file mode 100644
index 0000000..2c311e7
--- /dev/null
+++ b/lib/addns/dns.h
@@ -0,0 +1,366 @@
+/*
+ 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/>.
+*/
+
+#ifndef _DNS_H
+#define _DNS_H
+
+#include "../replace/replace.h"
+#include "system/network.h"
+#include "system/kerberos.h"
+#include "system/gssapi.h"
+
+/* make sure we have included the correct config.h */
+#ifndef NO_CONFIG_H /* for some tests */
+#ifndef CONFIG_H_IS_FROM_SAMBA
+#error "make sure you have removed all config.h files from standalone builds!"
+#error "the included config.h isn't from samba!"
+#endif
+#endif /* NO_CONFIG_H */
+
+#include <fcntl.h>
+#include <time.h>
+#include <netdb.h>
+
+#include <talloc.h>
+
+#include "dnserr.h"
+
+
+#define DNS_TCP 1
+#define DNS_UDP 2
+
+#define DNS_OPCODE_UPDATE 1
+
+/* DNS Class Types */
+
+#define DNS_CLASS_IN 1
+#define DNS_CLASS_ANY 255
+#define DNS_CLASS_NONE 254
+
+/* DNS RR Types */
+
+#define DNS_RR_A 1
+
+#define DNS_TCP_PORT 53
+#define DNS_UDP_PORT 53
+
+#define QTYPE_A 1
+#define QTYPE_NS 2
+#define QTYPE_MD 3
+#define QTYPE_CNAME 5
+#define QTYPE_SOA 6
+#define QTYPE_AAAA 28
+#define QTYPE_ANY 255
+#define QTYPE_TKEY 249
+#define QTYPE_TSIG 250
+
+/*
+MF 4 a mail forwarder (Obsolete - use MX)
+CNAME 5 the canonical name for an alias
+SOA 6 marks the start of a zone of authority
+MB 7 a mailbox domain name (EXPERIMENTAL)
+MG 8 a mail group member (EXPERIMENTAL)
+MR 9 a mail rename domain name (EXPERIMENTAL)
+NULL 10 a null RR (EXPERIMENTAL)
+WKS 11 a well known service description
+PTR 12 a domain name pointer
+HINFO 13 host information
+MINFO 14 mailbox or mail list information
+MX 15 mail exchange
+TXT 16 text strings
+*/
+
+#define QR_QUERY 0x0000
+#define QR_RESPONSE 0x0001
+
+#define OPCODE_QUERY 0x00
+#define OPCODE_IQUERY 0x01
+#define OPCODE_STATUS 0x02
+
+#define AA 1
+
+#define RECURSION_DESIRED 0x01
+
+#define RCODE_NOERROR 0
+#define RCODE_FORMATERROR 1
+#define RCODE_SERVER_FAILURE 2
+#define RCODE_NAME_ERROR 3
+#define RCODE_NOTIMPLEMENTED 4
+#define RCODE_REFUSED 5
+
+#define SENDBUFFER_SIZE 65536
+#define RECVBUFFER_SIZE 65536
+
+/*
+ * TKEY Modes from rfc2930
+ */
+
+#define DNS_TKEY_MODE_SERVER 1
+#define DNS_TKEY_MODE_DH 2
+#define DNS_TKEY_MODE_GSSAPI 3
+#define DNS_TKEY_MODE_RESOLVER 4
+#define DNS_TKEY_MODE_DELETE 5
+
+
+#define DNS_ONE_DAY_IN_SECS 86400
+#define DNS_TEN_HOURS_IN_SECS 36000
+
+#define SOCKET_ERROR -1
+#define INVALID_SOCKET -1
+
+#define DNS_NO_ERROR 0
+#define DNS_FORMAT_ERROR 1
+#define DNS_SERVER_FAILURE 2
+#define DNS_NAME_ERROR 3
+#define DNS_NOT_IMPLEMENTED 4
+#define DNS_REFUSED 5
+
+typedef long HANDLE;
+
+enum dns_ServerType { DNS_SRV_ANY, DNS_SRV_WIN2000, DNS_SRV_WIN2003 };
+
+struct dns_domain_label {
+ struct dns_domain_label *next;
+ char *label;
+ size_t len;
+};
+
+struct dns_domain_name {
+ struct dns_domain_label *pLabelList;
+};
+
+struct dns_question {
+ struct dns_domain_name *name;
+ uint16_t q_type;
+ uint16_t q_class;
+};
+
+/*
+ * Before changing the definition of dns_zone, look
+ * dns_marshall_update_request(), we rely on this being the same as
+ * dns_question right now.
+ */
+
+struct dns_zone {
+ struct dns_domain_name *name;
+ uint16_t z_type;
+ uint16_t z_class;
+};
+
+struct dns_rrec {
+ struct dns_domain_name *name;
+ uint16_t type;
+ uint16_t r_class;
+ uint32_t ttl;
+ uint16_t data_length;
+ uint8_t *data;
+};
+
+struct dns_tkey_record {
+ struct dns_domain_name *algorithm;
+ time_t inception;
+ time_t expiration;
+ uint16_t mode;
+ uint16_t error;
+ uint16_t key_length;
+ uint8_t *key;
+};
+
+struct dns_request {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t num_questions;
+ uint16_t num_answers;
+ uint16_t num_auths;
+ uint16_t num_additionals;
+ struct dns_question **questions;
+ struct dns_rrec **answers;
+ struct dns_rrec **auths;
+ struct dns_rrec **additional;
+};
+
+/*
+ * Before changing the definition of dns_update_request, look
+ * dns_marshall_update_request(), we rely on this being the same as
+ * dns_request right now.
+ */
+
+struct dns_update_request {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t num_zones;
+ uint16_t num_preqs;
+ uint16_t num_updates;
+ uint16_t num_additionals;
+ struct dns_zone **zones;
+ struct dns_rrec **preqs;
+ struct dns_rrec **updates;
+ struct dns_rrec **additional;
+};
+
+struct dns_connection {
+ int32_t hType;
+ int s;
+ struct sockaddr_storage RecvAddr;
+};
+
+struct dns_buffer {
+ uint8_t *data;
+ size_t size;
+ size_t offset;
+ DNS_ERROR error;
+};
+
+/* from dnsutils.c */
+
+DNS_ERROR dns_domain_name_from_string( TALLOC_CTX *mem_ctx,
+ const char *pszDomainName,
+ struct dns_domain_name **presult );
+char *dns_generate_keyname( TALLOC_CTX *mem_ctx );
+
+/* from dnsrecord.c */
+
+DNS_ERROR dns_create_query( TALLOC_CTX *mem_ctx, const char *name,
+ uint16_t q_type, uint16_t q_class,
+ struct dns_request **preq );
+DNS_ERROR dns_create_update( TALLOC_CTX *mem_ctx, const char *name,
+ struct dns_update_request **preq );
+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);
+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);
+DNS_ERROR dns_add_rrec(TALLOC_CTX *mem_ctx, struct dns_rrec *rec,
+ uint16_t *num_records, struct dns_rrec ***records);
+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);
+DNS_ERROR dns_create_name_in_use_record(TALLOC_CTX *mem_ctx,
+ const char *name,
+ const struct sockaddr_storage *ip,
+ struct dns_rrec **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);
+DNS_ERROR dns_create_name_not_in_use_record(TALLOC_CTX *mem_ctx,
+ const char *name, uint32_t type,
+ struct dns_rrec **prec);
+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);
+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);
+DNS_ERROR dns_unmarshall_tkey_record(TALLOC_CTX *mem_ctx, struct dns_rrec *rec,
+ struct dns_tkey_record **ptkey);
+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);
+DNS_ERROR dns_create_update_request(TALLOC_CTX *mem_ctx,
+ const char *domainname,
+ const char *hostname,
+ const struct sockaddr_storage *ip_addr,
+ size_t num_adds,
+ uint32_t ttl,
+ struct dns_update_request **preq);
+
+/* from dnssock.c */
+
+DNS_ERROR dns_open_connection( const char *nameserver, int32_t dwType,
+ TALLOC_CTX *mem_ctx,
+ struct dns_connection **conn );
+DNS_ERROR dns_send(struct dns_connection *conn, const struct dns_buffer *buf);
+DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
+ struct dns_buffer **presult);
+DNS_ERROR dns_transaction(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
+ const struct dns_request *req,
+ struct dns_request **resp);
+DNS_ERROR dns_update_transaction(TALLOC_CTX *mem_ctx,
+ struct dns_connection *conn,
+ struct dns_update_request *up_req,
+ struct dns_update_request **up_resp);
+
+/* from dnsmarshall.c */
+
+struct dns_buffer *dns_create_buffer(TALLOC_CTX *mem_ctx);
+void dns_marshall_buffer(struct dns_buffer *buf, const uint8_t *data,
+ size_t len);
+void dns_marshall_uint16(struct dns_buffer *buf, uint16_t val);
+void dns_marshall_uint32(struct dns_buffer *buf, uint32_t val);
+void dns_unmarshall_buffer(struct dns_buffer *buf, uint8_t *data,
+ size_t len);
+void dns_unmarshall_uint16(struct dns_buffer *buf, uint16_t *val);
+void dns_unmarshall_uint32(struct dns_buffer *buf, uint32_t *val);
+void dns_unmarshall_domain_name(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_domain_name **pname);
+void dns_marshall_domain_name(struct dns_buffer *buf,
+ const struct dns_domain_name *name);
+void dns_unmarshall_domain_name(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_domain_name **pname);
+DNS_ERROR dns_marshall_request(TALLOC_CTX *mem_ctx,
+ const struct dns_request *req,
+ struct dns_buffer **pbuf);
+DNS_ERROR dns_unmarshall_request(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_request **preq);
+DNS_ERROR dns_marshall_update_request(TALLOC_CTX *mem_ctx,
+ struct dns_update_request *update,
+ struct dns_buffer **pbuf);
+DNS_ERROR dns_unmarshall_update_request(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_update_request **pupreq);
+struct dns_request *dns_update2request(struct dns_update_request *update);
+struct dns_update_request *dns_request2update(struct dns_request *request);
+uint16_t dns_response_code(uint16_t flags);
+const char *dns_errstr(DNS_ERROR err);
+
+/* from dnsgss.c */
+
+#ifdef HAVE_GSSAPI
+
+void display_status( const char *msg, OM_uint32 maj_stat, OM_uint32 min_stat );
+DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
+ const char *servername,
+ const char *keyname,
+ gss_ctx_id_t *gss_ctx,
+ enum dns_ServerType srv_type );
+DNS_ERROR dns_sign_update(struct dns_update_request *req,
+ gss_ctx_id_t gss_ctx,
+ const char *keyname,
+ const char *algorithmname,
+ time_t time_signed, uint16_t fudge);
+
+#endif /* HAVE_GSSAPI */
+
+#endif /* _DNS_H */
diff --git a/lib/addns/dnserr.h b/lib/addns/dnserr.h
new file mode 100644
index 0000000..1eedc8f
--- /dev/null
+++ b/lib/addns/dnserr.h
@@ -0,0 +1,87 @@
+/*
+ Error codes for 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/>.
+*/
+
+#ifndef _DNSERR_H
+#define _DNSERR_H
+
+
+/* The Splint code analysis tool (http://www.splint.org.) doesn't
+ like immediate structures. */
+
+#ifdef _SPLINT_
+#undef HAVE_IMMEDIATE_STRUCTURES
+#endif
+
+/* Setup the DNS_ERROR typedef. Technique takes from nt_status.h */
+
+#if defined(HAVE_IMMEDIATE_STRUCTURES)
+typedef struct {uint32_t v;} DNS_ERROR;
+#define ERROR_DNS(x) ((DNS_ERROR) { x })
+#define ERROR_DNS_V(x) ((x).v)
+#else
+typedef uint32_t DNS_ERROR;
+#define ERROR_DNS(x) (x)
+#define ERROR_DNS_V(x) (x)
+#endif
+
+#define ERR_DNS_IS_OK(x) (ERROR_DNS_V(x) == 0)
+#define ERR_DNS_EQUAL(x,y) (ERROR_DNS_V(x) == ERROR_DNS_V(y))
+
+/*************************************************
+ * Define the error codes here
+ *************************************************/
+
+#define ERROR_DNS_SUCCESS ERROR_DNS(0)
+#define ERROR_DNS_RECORD_NOT_FOUND ERROR_DNS(1)
+#define ERROR_DNS_BAD_RESPONSE ERROR_DNS(2)
+#define ERROR_DNS_INVALID_PARAMETER ERROR_DNS(3)
+#define ERROR_DNS_NO_MEMORY ERROR_DNS(4)
+#define ERROR_DNS_INVALID_NAME_SERVER ERROR_DNS(5)
+#define ERROR_DNS_CONNECTION_FAILED ERROR_DNS(6)
+#define ERROR_DNS_GSS_ERROR ERROR_DNS(7)
+#define ERROR_DNS_INVALID_NAME ERROR_DNS(8)
+#define ERROR_DNS_INVALID_MESSAGE ERROR_DNS(9)
+#define ERROR_DNS_SOCKET_ERROR ERROR_DNS(10)
+#define ERROR_DNS_UPDATE_FAILED ERROR_DNS(11)
+
+/*
+ * About to be removed, transitional error
+ */
+#define ERROR_DNS_UNSUCCESSFUL ERROR_DNS(999)
+
+
+#define ERROR_BAD_RESPONSE 1
+#define ERROR_RECORD_NOT_FOUND 2
+#define ERROR_OUTOFMEMORY 8
+#if !defined(ERROR_INVALID_PARAMETER)
+#define ERROR_INVALID_PARAMETER 87
+#endif
+
+/*
+ * About to be removed, transitional error
+ */
+#define ERROR_UNSUCCESSFUL 999
+
+#endif /* _DNSERR_H */
+
diff --git a/lib/addns/dnsgss.c b/lib/addns/dnsgss.c
new file mode 100644
index 0000000..a315b80
--- /dev/null
+++ b/lib/addns/dnsgss.c
@@ -0,0 +1,344 @@
+/*
+ Public Interface file for 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 <ctype.h>
+
+
+#ifdef HAVE_GSSAPI
+
+/*********************************************************************
+*********************************************************************/
+
+#ifndef HAVE_STRUPR
+static int strupr( char *szDomainName )
+{
+ if ( !szDomainName ) {
+ return ( 0 );
+ }
+ while ( *szDomainName != '\0' ) {
+ *szDomainName = toupper( *szDomainName );
+ szDomainName++;
+ }
+ return ( 0 );
+}
+#endif
+
+#if 0
+/*********************************************************************
+*********************************************************************/
+
+static void display_status_1( const char *m, OM_uint32 code, int type )
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx;
+
+ msg_ctx = 0;
+ while ( 1 ) {
+ maj_stat = gss_display_status( &min_stat, code,
+ type, GSS_C_NULL_OID,
+ &msg_ctx, &msg );
+ fprintf( stdout, "GSS-API error %s: %s\n", m,
+ ( char * ) msg.value );
+ ( void ) gss_release_buffer( &min_stat, &msg );
+
+ if ( !msg_ctx )
+ break;
+ }
+}
+
+/*********************************************************************
+*********************************************************************/
+
+void display_status( const char *msg, OM_uint32 maj_stat, OM_uint32 min_stat )
+{
+ display_status_1( msg, maj_stat, GSS_C_GSS_CODE );
+ display_status_1( msg, min_stat, GSS_C_MECH_CODE );
+}
+#endif
+
+static DNS_ERROR dns_negotiate_gss_ctx_int( TALLOC_CTX *mem_ctx,
+ struct dns_connection *conn,
+ const char *keyname,
+ const gss_name_t target_name,
+ gss_ctx_id_t *ctx,
+ enum dns_ServerType srv_type )
+{
+ struct gss_buffer_desc_struct input_desc, *input_ptr, output_desc;
+ OM_uint32 major, minor;
+ OM_uint32 ret_flags;
+ struct dns_request *req = NULL;
+ struct dns_buffer *buf = NULL;
+ DNS_ERROR err;
+
+ gss_OID_desc krb5_oid_desc =
+ { 9, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
+
+ *ctx = GSS_C_NO_CONTEXT;
+ input_ptr = NULL;
+
+ do {
+ major = gss_init_sec_context(
+ &minor, NULL, ctx, target_name, &krb5_oid_desc,
+ GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG |
+ GSS_C_CONF_FLAG |
+ GSS_C_INTEG_FLAG,
+ 0, NULL, input_ptr, NULL, &output_desc,
+ &ret_flags, NULL );
+
+ if (input_ptr != NULL) {
+ TALLOC_FREE(input_desc.value);
+ }
+
+ if (output_desc.length != 0) {
+
+ struct dns_rrec *rec;
+
+ time_t t = time(NULL);
+
+ err = dns_create_query(mem_ctx, keyname, QTYPE_TKEY,
+ DNS_CLASS_IN, &req);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_create_tkey_record(
+ req, keyname, "gss.microsoft.com", t,
+ t + 86400, DNS_TKEY_MODE_GSSAPI, 0,
+ output_desc.length, (uint8_t *)output_desc.value,
+ &rec );
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ /* Windows 2000 DNS is broken and requires the
+ TKEY payload in the Answer section instead
+ of the Additional section like Windows 2003 */
+
+ if ( srv_type == DNS_SRV_WIN2000 ) {
+ err = dns_add_rrec(req, rec, &req->num_answers,
+ &req->answers);
+ } else {
+ err = dns_add_rrec(req, rec, &req->num_additionals,
+ &req->additional);
+ }
+
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_marshall_request(mem_ctx, req, &buf);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_send(conn, buf);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ TALLOC_FREE(buf);
+ TALLOC_FREE(req);
+ }
+
+ gss_release_buffer(&minor, &output_desc);
+
+ if ((major != GSS_S_COMPLETE) &&
+ (major != GSS_S_CONTINUE_NEEDED)) {
+ return ERROR_DNS_GSS_ERROR;
+ }
+
+ if (major == GSS_S_CONTINUE_NEEDED) {
+
+ struct dns_request *resp;
+ struct dns_tkey_record *tkey;
+ struct dns_rrec *tkey_answer = NULL;
+ uint16_t i;
+
+ err = dns_receive(mem_ctx, conn, &buf);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_unmarshall_request(buf, buf, &resp);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ /*
+ * TODO: Compare id and keyname
+ */
+
+ for (i=0; i < resp->num_answers; i++) {
+ if (resp->answers[i]->type != QTYPE_TKEY) {
+ continue;
+ }
+
+ tkey_answer = resp->answers[i];
+ }
+
+ if (tkey_answer == NULL) {
+ err = ERROR_DNS_INVALID_MESSAGE;
+ goto error;
+ }
+
+ err = dns_unmarshall_tkey_record(
+ mem_ctx, resp->answers[0], &tkey);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ input_desc.length = tkey->key_length;
+ input_desc.value = talloc_move(mem_ctx, &tkey->key);
+
+ input_ptr = &input_desc;
+
+ TALLOC_FREE(buf);
+ }
+
+ } while ( major == GSS_S_CONTINUE_NEEDED );
+
+ /* If we arrive here, we have a valid security context */
+
+ err = ERROR_DNS_SUCCESS;
+
+ error:
+
+ TALLOC_FREE(buf);
+ TALLOC_FREE(req);
+ return err;
+}
+
+DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
+ const char *servername,
+ const char *keyname,
+ gss_ctx_id_t *gss_ctx,
+ enum dns_ServerType srv_type )
+{
+ OM_uint32 major, minor;
+
+ char *upcaserealm, *targetname;
+ DNS_ERROR err;
+
+ gss_buffer_desc input_name;
+ struct dns_connection *conn;
+
+ gss_name_t targ_name;
+
+ gss_OID_desc nt_host_oid_desc =
+ {10, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
+
+ TALLOC_CTX *mem_ctx;
+
+ if (!(mem_ctx = talloc_init("dns_negotiate_sec_ctx"))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_open_connection( servername, DNS_TCP, mem_ctx, &conn );
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ if (!(upcaserealm = talloc_strdup(mem_ctx, target_realm))) {
+ err = ERROR_DNS_NO_MEMORY;
+ goto error;
+ }
+
+ strupr(upcaserealm);
+
+ if (!(targetname = talloc_asprintf(mem_ctx, "dns/%s@%s",
+ servername, upcaserealm))) {
+ err = ERROR_DNS_NO_MEMORY;
+ goto error;
+ }
+
+ input_name.value = targetname;
+ input_name.length = strlen(targetname);
+
+ major = gss_import_name( &minor, &input_name,
+ &nt_host_oid_desc, &targ_name );
+
+ if (major) {
+ err = ERROR_DNS_GSS_ERROR;
+ goto error;
+ }
+
+ err = dns_negotiate_gss_ctx_int(mem_ctx, conn, keyname,
+ targ_name, gss_ctx, srv_type );
+
+ gss_release_name( &minor, &targ_name );
+
+ error:
+ TALLOC_FREE(mem_ctx);
+
+ return err;
+}
+
+DNS_ERROR dns_sign_update(struct dns_update_request *req,
+ gss_ctx_id_t gss_ctx,
+ const char *keyname,
+ const char *algorithmname,
+ time_t time_signed, uint16_t fudge)
+{
+ struct dns_buffer *buf;
+ DNS_ERROR err;
+ struct dns_domain_name *key, *algorithm;
+ struct gss_buffer_desc_struct msg, mic;
+ OM_uint32 major, minor;
+ struct dns_rrec *rec;
+
+ err = dns_marshall_update_request(req, req, &buf);
+ if (!ERR_DNS_IS_OK(err)) return err;
+
+ err = dns_domain_name_from_string(buf, keyname, &key);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_domain_name_from_string(buf, algorithmname, &algorithm);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ dns_marshall_domain_name(buf, key);
+ dns_marshall_uint16(buf, DNS_CLASS_ANY);
+ dns_marshall_uint32(buf, 0); /* TTL */
+ dns_marshall_domain_name(buf, algorithm);
+ dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */
+ dns_marshall_uint32(buf, time_signed);
+ dns_marshall_uint16(buf, fudge);
+ dns_marshall_uint16(buf, 0); /* error */
+ dns_marshall_uint16(buf, 0); /* other len */
+
+ err = buf->error;
+ if (!ERR_DNS_IS_OK(buf->error)) goto error;
+
+ msg.value = (void *)buf->data;
+ msg.length = buf->offset;
+
+ major = gss_get_mic(&minor, gss_ctx, 0, &msg, &mic);
+ if (major != 0) {
+ err = ERROR_DNS_GSS_ERROR;
+ goto error;
+ }
+
+ if (mic.length > 0xffff) {
+ gss_release_buffer(&minor, &mic);
+ err = ERROR_DNS_GSS_ERROR;
+ goto error;
+ }
+
+ err = dns_create_tsig_record(buf, keyname, algorithmname, time_signed,
+ fudge, mic.length, (uint8_t *)mic.value,
+ req->id, 0, &rec);
+ gss_release_buffer(&minor, &mic);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_additionals, &req->additional);
+
+ error:
+ TALLOC_FREE(buf);
+ return err;
+}
+
+#endif /* HAVE_GSSAPI */
diff --git a/lib/addns/dnsmarshall.c b/lib/addns/dnsmarshall.c
new file mode 100644
index 0000000..c954203
--- /dev/null
+++ b/lib/addns/dnsmarshall.c
@@ -0,0 +1,534 @@
+/*
+ Linux DNS client library implementation
+ 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 "assert.h"
+
+struct dns_buffer *dns_create_buffer(TALLOC_CTX *mem_ctx)
+{
+ struct dns_buffer *result;
+
+ if (!(result = talloc_zero(mem_ctx, struct dns_buffer))) {
+ return NULL;
+ }
+
+ result->offset = 0;
+ result->error = ERROR_DNS_SUCCESS;
+
+ /*
+ * Small initial size to exercise the realloc code
+ */
+ result->size = 2;
+
+ if (!(result->data = talloc_zero_array(result, uint8_t, result->size))) {
+ TALLOC_FREE(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+void dns_marshall_buffer(struct dns_buffer *buf, const uint8_t *data,
+ size_t len)
+{
+ if (!ERR_DNS_IS_OK(buf->error)) return;
+
+ if (buf->offset + len < buf->offset) {
+ /*
+ * Wraparound!
+ */
+ buf->error = ERROR_DNS_INVALID_PARAMETER;
+ return;
+ }
+
+ if ((buf->offset + len) > 0xffff) {
+ /*
+ * Only 64k possible
+ */
+ buf->error = ERROR_DNS_INVALID_PARAMETER;
+ return;
+ }
+
+ if (buf->offset + len > buf->size) {
+ size_t new_size = buf->offset + len;
+ uint8_t *new_data;
+
+ /*
+ * Don't do too many reallocs, round up to some multiple
+ */
+
+ new_size += (64 - (new_size % 64));
+
+ if (!(new_data = talloc_realloc(buf, buf->data, uint8_t,
+ new_size))) {
+ buf->error = ERROR_DNS_NO_MEMORY;
+ return;
+ }
+
+ buf->size = new_size;
+ buf->data = new_data;
+ }
+
+ if (data != NULL) {
+ memcpy(buf->data + buf->offset, data, len);
+ }
+ buf->offset += len;
+ return;
+}
+
+void dns_marshall_uint16(struct dns_buffer *buf, uint16_t val)
+{
+ uint16_t n_val = htons(val);
+ dns_marshall_buffer(buf, (uint8_t *)&n_val, sizeof(n_val));
+}
+
+void dns_marshall_uint32(struct dns_buffer *buf, uint32_t val)
+{
+ uint32_t n_val = htonl(val);
+ dns_marshall_buffer(buf, (uint8_t *)&n_val, sizeof(n_val));
+}
+
+void dns_unmarshall_buffer(struct dns_buffer *buf, uint8_t *data,
+ size_t len)
+{
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ if ((len > buf->size) || (buf->offset + len > buf->size)) {
+ buf->error = ERROR_DNS_INVALID_MESSAGE;
+ return;
+ }
+
+ memcpy((void *)data, (const void *)(buf->data + buf->offset), len);
+ buf->offset += len;
+
+ return;
+}
+
+void dns_unmarshall_uint16(struct dns_buffer *buf, uint16_t *val)
+{
+ uint16_t n_val;
+
+ dns_unmarshall_buffer(buf, (uint8_t *)&n_val, sizeof(n_val));
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ *val = ntohs(n_val);
+}
+
+void dns_unmarshall_uint32(struct dns_buffer *buf, uint32_t *val)
+{
+ uint32_t n_val;
+
+ dns_unmarshall_buffer(buf, (uint8_t *)&n_val, sizeof(n_val));
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ *val = ntohl(n_val);
+}
+
+void dns_marshall_domain_name(struct dns_buffer *buf,
+ const struct dns_domain_name *name)
+{
+ struct dns_domain_label *label;
+ char end_char = '\0';
+
+ /*
+ * TODO: Implement DNS compression
+ */
+
+ for (label = name->pLabelList; label != NULL; label = label->next) {
+ uint8_t len = label->len;
+
+ dns_marshall_buffer(buf, (uint8_t *)&len, sizeof(len));
+ if (!ERR_DNS_IS_OK(buf->error)) return;
+
+ dns_marshall_buffer(buf, (uint8_t *)label->label, len);
+ if (!ERR_DNS_IS_OK(buf->error)) return;
+ }
+
+ dns_marshall_buffer(buf, (uint8_t *)&end_char, 1);
+}
+
+static void dns_unmarshall_label(TALLOC_CTX *mem_ctx,
+ int level,
+ struct dns_buffer *buf,
+ struct dns_domain_label **plabel)
+{
+ struct dns_domain_label *label;
+ uint8_t len;
+
+ if (!ERR_DNS_IS_OK(buf->error)) return;
+
+ if (level > 128) {
+ /*
+ * Protect against recursion
+ */
+ buf->error = ERROR_DNS_INVALID_MESSAGE;
+ return;
+ }
+
+ dns_unmarshall_buffer(buf, &len, sizeof(len));
+ if (!ERR_DNS_IS_OK(buf->error)) return;
+
+ if (len == 0) {
+ *plabel = NULL;
+ return;
+ }
+
+ if ((len & 0xc0) == 0xc0) {
+ /*
+ * We've got a compressed name. Build up a new "fake" buffer
+ * and using the calculated offset.
+ */
+ struct dns_buffer new_buf;
+ uint8_t low;
+
+ dns_unmarshall_buffer(buf, &low, sizeof(low));
+ if (!ERR_DNS_IS_OK(buf->error)) return;
+
+ new_buf = *buf;
+ new_buf.offset = len & 0x3f;
+ new_buf.offset <<= 8;
+ new_buf.offset |= low;
+
+ dns_unmarshall_label(mem_ctx, level+1, &new_buf, plabel);
+ buf->error = new_buf.error;
+ return;
+ }
+
+ if ((len & 0xc0) != 0) {
+ buf->error = ERROR_DNS_INVALID_NAME;
+ return;
+ }
+
+ if (!(label = talloc_zero(mem_ctx, struct dns_domain_label))) {
+ buf->error = ERROR_DNS_NO_MEMORY;
+ return;
+ }
+
+ label->len = len;
+
+ if (!(label->label = talloc_zero_array(label, char, len+1))) {
+ buf->error = ERROR_DNS_NO_MEMORY;
+ goto error;
+ }
+
+ dns_unmarshall_buffer(buf, (uint8_t *)label->label, len);
+ if (!ERR_DNS_IS_OK(buf->error)) goto error;
+
+ dns_unmarshall_label(label, level+1, buf, &label->next);
+ if (!ERR_DNS_IS_OK(buf->error)) goto error;
+
+ *plabel = label;
+ return;
+
+ error:
+ TALLOC_FREE(label);
+ return;
+}
+
+void dns_unmarshall_domain_name(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_domain_name **pname)
+{
+ struct dns_domain_name *name;
+
+ if (!ERR_DNS_IS_OK(buf->error)) return;
+
+ if (!(name = talloc_zero(mem_ctx, struct dns_domain_name))) {
+ buf->error = ERROR_DNS_NO_MEMORY;
+ return;
+ }
+
+ dns_unmarshall_label(name, 0, buf, &name->pLabelList);
+
+ if (!ERR_DNS_IS_OK(buf->error)) {
+ return;
+ }
+
+ *pname = name;
+ return;
+}
+
+static void dns_marshall_question(struct dns_buffer *buf,
+ const struct dns_question *q)
+{
+ dns_marshall_domain_name(buf, q->name);
+ dns_marshall_uint16(buf, q->q_type);
+ dns_marshall_uint16(buf, q->q_class);
+}
+
+static void dns_unmarshall_question(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_question **pq)
+{
+ struct dns_question *q;
+
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ if (!(q = talloc_zero(mem_ctx, struct dns_question))) {
+ buf->error = ERROR_DNS_NO_MEMORY;
+ return;
+ }
+
+ dns_unmarshall_domain_name(q, buf, &q->name);
+ dns_unmarshall_uint16(buf, &q->q_type);
+ dns_unmarshall_uint16(buf, &q->q_class);
+
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ *pq = q;
+}
+
+static void dns_marshall_rr(struct dns_buffer *buf,
+ const struct dns_rrec *r)
+{
+ dns_marshall_domain_name(buf, r->name);
+ dns_marshall_uint16(buf, r->type);
+ dns_marshall_uint16(buf, r->r_class);
+ dns_marshall_uint32(buf, r->ttl);
+ dns_marshall_uint16(buf, r->data_length);
+ dns_marshall_buffer(buf, r->data, r->data_length);
+}
+
+static void dns_unmarshall_rr(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_rrec **pr)
+{
+ struct dns_rrec *r;
+
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ if (!(r = talloc_zero(mem_ctx, struct dns_rrec))) {
+ buf->error = ERROR_DNS_NO_MEMORY;
+ return;
+ }
+
+ dns_unmarshall_domain_name(r, buf, &r->name);
+ dns_unmarshall_uint16(buf, &r->type);
+ dns_unmarshall_uint16(buf, &r->r_class);
+ dns_unmarshall_uint32(buf, &r->ttl);
+ dns_unmarshall_uint16(buf, &r->data_length);
+ r->data = NULL;
+
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ if (r->data_length != 0) {
+ if (!(r->data = talloc_zero_array(r, uint8_t, r->data_length))) {
+ buf->error = ERROR_DNS_NO_MEMORY;
+ return;
+ }
+ dns_unmarshall_buffer(buf, r->data, r->data_length);
+ }
+
+ if (!(ERR_DNS_IS_OK(buf->error))) return;
+
+ *pr = r;
+}
+
+DNS_ERROR dns_marshall_request(TALLOC_CTX *mem_ctx,
+ const struct dns_request *req,
+ struct dns_buffer **pbuf)
+{
+ struct dns_buffer *buf;
+ uint16_t i;
+
+ if (!(buf = dns_create_buffer(mem_ctx))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ dns_marshall_uint16(buf, req->id);
+ dns_marshall_uint16(buf, req->flags);
+ dns_marshall_uint16(buf, req->num_questions);
+ dns_marshall_uint16(buf, req->num_answers);
+ dns_marshall_uint16(buf, req->num_auths);
+ dns_marshall_uint16(buf, req->num_additionals);
+
+ for (i=0; i<req->num_questions; i++) {
+ dns_marshall_question(buf, req->questions[i]);
+ }
+ for (i=0; i<req->num_answers; i++) {
+ dns_marshall_rr(buf, req->answers[i]);
+ }
+ for (i=0; i<req->num_auths; i++) {
+ dns_marshall_rr(buf, req->auths[i]);
+ }
+ for (i=0; i<req->num_additionals; i++) {
+ dns_marshall_rr(buf, req->additional[i]);
+ }
+
+ if (!ERR_DNS_IS_OK(buf->error)) {
+ DNS_ERROR err = buf->error;
+ TALLOC_FREE(buf);
+ return err;
+ }
+
+ *pbuf = buf;
+ return ERROR_DNS_SUCCESS;
+}
+
+DNS_ERROR dns_unmarshall_request(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_request **preq)
+{
+ struct dns_request *req;
+ uint16_t i;
+ DNS_ERROR err = ERROR_DNS_NO_MEMORY;
+
+ if (!(req = talloc_zero(mem_ctx, struct dns_request))) {
+ return err;
+ }
+
+ dns_unmarshall_uint16(buf, &req->id);
+ dns_unmarshall_uint16(buf, &req->flags);
+ dns_unmarshall_uint16(buf, &req->num_questions);
+ dns_unmarshall_uint16(buf, &req->num_answers);
+ dns_unmarshall_uint16(buf, &req->num_auths);
+ dns_unmarshall_uint16(buf, &req->num_additionals);
+
+ if (!ERR_DNS_IS_OK(buf->error)){
+ err = buf->error;
+ goto error;
+ }
+
+ err = ERROR_DNS_NO_MEMORY;
+
+ if ((req->num_questions != 0) &&
+ !(req->questions = talloc_zero_array(req, struct dns_question *,
+ req->num_questions))) {
+ goto error;
+ }
+ if ((req->num_answers != 0) &&
+ !(req->answers = talloc_zero_array(req, struct dns_rrec *,
+ req->num_answers))) {
+ goto error;
+ }
+ if ((req->num_auths != 0) &&
+ !(req->auths = talloc_zero_array(req, struct dns_rrec *,
+ req->num_auths))) {
+ goto error;
+ }
+ if ((req->num_additionals != 0) &&
+ !(req->additional = talloc_zero_array(req, struct dns_rrec *,
+ req->num_additionals))) {
+ goto error;
+ }
+
+ for (i=0; i<req->num_questions; i++) {
+ dns_unmarshall_question(req->questions, buf,
+ &req->questions[i]);
+ }
+ for (i=0; i<req->num_answers; i++) {
+ dns_unmarshall_rr(req->answers, buf,
+ &req->answers[i]);
+ }
+ for (i=0; i<req->num_auths; i++) {
+ dns_unmarshall_rr(req->auths, buf,
+ &req->auths[i]);
+ }
+ for (i=0; i<req->num_additionals; i++) {
+ dns_unmarshall_rr(req->additional, buf,
+ &req->additional[i]);
+ }
+
+ if (!ERR_DNS_IS_OK(buf->error)) {
+ err = buf->error;
+ goto error;
+ }
+
+ *preq = req;
+ return ERROR_DNS_SUCCESS;
+
+ error:
+ TALLOC_FREE(req);
+ return err;
+}
+
+struct dns_request *dns_update2request(struct dns_update_request *update)
+{
+ struct dns_request *req;
+
+ /*
+ * This is a non-specified construct that happens to work on Linux/gcc
+ * and I would expect it to work everywhere else. dns_request and
+ * dns_update_request are essentially the same structures with
+ * different names, so any difference would mean that the compiler
+ * applied two different variations of padding given the same types in
+ * the structures.
+ */
+
+ req = (struct dns_request *)(void *)update;
+
+ /*
+ * The assert statement here looks like we could do the equivalent
+ * assignments to get portable, but it would mean that we have to
+ * allocate the dns_question record for the dns_zone records. We
+ * assume that if this assert works then the same holds true for
+ * dns_zone<>dns_question as well.
+ */
+
+#ifdef DEVELOPER
+ assert((req->id == update->id) && (req->flags == update->flags) &&
+ (req->num_questions == update->num_zones) &&
+ (req->num_answers == update->num_preqs) &&
+ (req->num_auths == update->num_updates) &&
+ (req->num_additionals == update->num_additionals) &&
+ (req->questions ==
+ (struct dns_question **)(void *)update->zones) &&
+ (req->answers == update->preqs) &&
+ (req->auths == update->updates) &&
+ (req->additional == update->additional));
+#endif
+
+ return req;
+}
+
+struct dns_update_request *dns_request2update(struct dns_request *request)
+{
+ /*
+ * For portability concerns see dns_update2request;
+ */
+ return (struct dns_update_request *)(void *)request;
+}
+
+DNS_ERROR dns_marshall_update_request(TALLOC_CTX *mem_ctx,
+ struct dns_update_request *update,
+ struct dns_buffer **pbuf)
+{
+ return dns_marshall_request(mem_ctx, dns_update2request(update), pbuf);
+}
+
+DNS_ERROR dns_unmarshall_update_request(TALLOC_CTX *mem_ctx,
+ struct dns_buffer *buf,
+ struct dns_update_request **pupreq)
+{
+ /*
+ * See comments above about portability. If the above works, this will
+ * as well.
+ */
+
+ return dns_unmarshall_request(mem_ctx, buf,
+ (struct dns_request **)(void *)pupreq);
+}
+
+uint16_t dns_response_code(uint16_t flags)
+{
+ return flags & 0xF;
+}
diff --git a/lib/addns/dnsquery.c b/lib/addns/dnsquery.c
new file mode 100644
index 0000000..7590c9f
--- /dev/null
+++ b/lib/addns/dnsquery.c
@@ -0,0 +1,782 @@
+/*
+ Unix SMB/CIFS implementation.
+ DNS utility library
+ Copyright (C) Gerald (Jerry) Carter 2006.
+ Copyright (C) Jeremy Allison 2007.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/util_net.h"
+#include "lib/util/tsort.h"
+#include "librpc/gen_ndr/dns.h"
+#include "libcli/dns/dns_lookup.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "dnsquery.h"
+
+/*********************************************************************
+ Sort SRV record list based on weight and priority. See RFC 2782.
+*********************************************************************/
+
+static int dnssrvcmp( struct dns_rr_srv *a, struct dns_rr_srv *b )
+{
+ if ( a->priority == b->priority ) {
+
+ /* randomize entries with an equal weight and priority */
+ if ( a->weight == b->weight )
+ return 0;
+
+ /* higher weights should be sorted lower */
+ if ( a->weight > b->weight )
+ return -1;
+ else
+ return 1;
+ }
+
+ if ( a->priority < b->priority )
+ return -1;
+
+ return 1;
+}
+
+struct ads_dns_lookup_srv_state {
+ struct dns_rr_srv *srvs;
+ size_t num_srvs;
+};
+
+static void ads_dns_lookup_srv_done(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_lookup_srv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct ads_dns_lookup_srv_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ads_dns_lookup_srv_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = dns_lookup_send(
+ state,
+ ev,
+ NULL,
+ name,
+ DNS_QCLASS_IN,
+ DNS_QTYPE_SRV);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ads_dns_lookup_srv_done, req);
+ return req;
+}
+
+static void ads_dns_lookup_srv_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ads_dns_lookup_srv_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_srv_state);
+ int ret;
+ struct dns_name_packet *reply;
+ uint16_t i, idx;
+
+ ret = dns_lookup_recv(subreq, state, &reply);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
+ return;
+ }
+
+ for (i=0; i<reply->ancount; i++) {
+ if (reply->answers[i].rr_type == DNS_QTYPE_SRV) {
+ /* uint16_t can't wrap here. */
+ state->num_srvs += 1;
+ }
+ }
+
+ state->srvs = talloc_array(state, struct dns_rr_srv, state->num_srvs);
+ if (tevent_req_nomem(state->srvs, req)) {
+ return;
+ }
+
+ idx = 0;
+
+ for (i=0; i<reply->ancount; i++) {
+ struct dns_res_rec *an = &reply->answers[i];
+ struct dns_rr_srv *dst = &state->srvs[idx];
+ struct dns_srv_record *src;
+
+ if (an->rr_type != DNS_QTYPE_SRV) {
+ continue;
+ }
+ src = &an->rdata.srv_record;
+
+ *dst = (struct dns_rr_srv) {
+ .hostname = talloc_move(state->srvs, &src->target),
+ .priority = src->priority,
+ .weight = src->weight,
+ .port = src->port,
+ };
+ idx += 1;
+ }
+
+ for (i=0; i<reply->arcount; i++) {
+ struct dns_res_rec *ar = &reply->additional[i];
+ struct sockaddr_storage addr;
+ bool ok;
+ size_t j;
+
+ ok = dns_res_rec_get_sockaddr(ar, &addr);
+ if (!ok) {
+ continue;
+ }
+
+ for (j=0; j<state->num_srvs; j++) {
+ struct dns_rr_srv *srv = &state->srvs[j];
+ struct sockaddr_storage *tmp;
+
+ if (strcmp(srv->hostname, ar->name) != 0) {
+ continue;
+ }
+ /* uint16_t can't wrap here. */
+ tmp = talloc_realloc(
+ state->srvs,
+ srv->ss_s,
+ struct sockaddr_storage,
+ srv->num_ips+1);
+
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ srv->ss_s = tmp;
+
+ srv->ss_s[srv->num_ips] = addr;
+ srv->num_ips += 1;
+ }
+ }
+
+ TYPESAFE_QSORT(state->srvs, state->num_srvs, dnssrvcmp);
+
+ tevent_req_done(req);
+}
+
+NTSTATUS ads_dns_lookup_srv_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct dns_rr_srv **srvs,
+ size_t *num_srvs)
+{
+ struct ads_dns_lookup_srv_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_srv_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *srvs = talloc_move(mem_ctx, &state->srvs);
+ *num_srvs = state->num_srvs;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Simple wrapper for a DNS SRV query
+*********************************************************************/
+
+NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
+ const char *name,
+ struct dns_rr_srv **dclist,
+ size_t *numdcs)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ size_t num_srvs = 0;
+
+ ev = samba_tevent_context_init(ctx);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = ads_dns_lookup_srv_send(ev, ev, name);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = ads_dns_lookup_srv_recv(req, ctx, dclist, &num_srvs);
+ if (NT_STATUS_IS_OK(status)) {
+ *numdcs = num_srvs;
+ }
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+struct ads_dns_lookup_ns_state {
+ struct dns_rr_ns *nss;
+ size_t num_nss;
+};
+
+static void ads_dns_lookup_ns_done(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_lookup_ns_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct ads_dns_lookup_ns_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ads_dns_lookup_ns_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = dns_lookup_send(state, ev, NULL, name, DNS_QCLASS_IN,
+ DNS_QTYPE_NS);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ads_dns_lookup_ns_done, req);
+ return req;
+}
+
+static void ads_dns_lookup_ns_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ads_dns_lookup_ns_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_ns_state);
+ int ret;
+ struct dns_name_packet *reply;
+ uint16_t i, idx;
+
+ ret = dns_lookup_recv(subreq, state, &reply);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
+ return;
+ }
+
+ for (i=0; i<reply->ancount; i++) {
+ if (reply->answers[i].rr_type == DNS_QTYPE_NS) {
+ state->num_nss += 1;
+ }
+ }
+
+ state->nss = talloc_array(state, struct dns_rr_ns, state->num_nss);
+ if (tevent_req_nomem(state->nss, req)) {
+ return;
+ }
+
+ idx = 0;
+
+ for (i=0; i<reply->ancount; i++) {
+ struct dns_res_rec *an = &reply->answers[i];
+
+ if (an->rr_type != DNS_QTYPE_NS) {
+ continue;
+ }
+
+ state->nss[idx].hostname = talloc_move(state->nss,
+ &an->rdata.ns_record);
+ idx += 1;
+ }
+
+ for (i=0; i<reply->arcount; i++) {
+ struct dns_res_rec *ar = &reply->additional[i];
+ struct sockaddr_storage addr;
+ bool ok;
+ size_t j;
+
+ ok = dns_res_rec_get_sockaddr(ar, &addr);
+ if (!ok) {
+ continue;
+ }
+
+ for (j=0; j<state->num_nss; j++) {
+ struct dns_rr_ns *ns = &state->nss[j];
+
+ if (strcmp(ns->hostname, ar->name) == 0) {
+ ns->ss = addr;
+ }
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS ads_dns_lookup_ns_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct dns_rr_ns **nss,
+ size_t *num_nss)
+{
+ struct ads_dns_lookup_ns_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_ns_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *nss = talloc_move(mem_ctx, &state->nss);
+ *num_nss = state->num_nss;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Simple wrapper for a DNS NS query
+*********************************************************************/
+
+NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
+ const char *dnsdomain,
+ struct dns_rr_ns **nslist,
+ size_t *numns)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ size_t num_ns = 0;
+
+ ev = samba_tevent_context_init(ctx);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = ads_dns_lookup_ns_send(ev, ev, dnsdomain);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = ads_dns_lookup_ns_recv(req, ctx, nslist, &num_ns);
+ *numns = num_ns;
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+/*********************************************************************
+ Async A record lookup.
+*********************************************************************/
+
+struct ads_dns_lookup_a_state {
+ uint8_t rcode;
+ size_t num_names;
+ char **hostnames;
+ struct samba_sockaddr *addrs;
+};
+
+static void ads_dns_lookup_a_done(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_lookup_a_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct ads_dns_lookup_a_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ads_dns_lookup_a_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = dns_lookup_send(
+ state,
+ ev,
+ NULL,
+ name,
+ DNS_QCLASS_IN,
+ DNS_QTYPE_A);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ads_dns_lookup_a_done, req);
+ return req;
+}
+
+static void ads_dns_lookup_a_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ads_dns_lookup_a_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_a_state);
+ int ret;
+ struct dns_name_packet *reply = NULL;
+ uint16_t i;
+
+ ret = dns_lookup_recv(subreq, state, &reply);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
+ return;
+ }
+
+ state->rcode = (reply->operation & DNS_RCODE);
+ if (state->rcode != DNS_RCODE_OK) {
+ /* Don't bother looking for answers. */
+ tevent_req_done(req);
+ return;
+ }
+
+ /*
+ * We don't care about CNAME answers here. We're
+ * just wanting an async name -> IPv4 lookup.
+ */
+ for (i = 0; i < reply->ancount; i++) {
+ if (reply->answers[i].rr_type == DNS_QTYPE_A) {
+ state->num_names += 1;
+ }
+ }
+
+ state->hostnames = talloc_zero_array(state,
+ char *,
+ state->num_names);
+ if (tevent_req_nomem(state->hostnames, req)) {
+ return;
+ }
+ state->addrs = talloc_zero_array(state,
+ struct samba_sockaddr,
+ state->num_names);
+ if (tevent_req_nomem(state->addrs, req)) {
+ return;
+ }
+
+ state->num_names = 0;
+
+ for (i = 0; i < reply->ancount; i++) {
+ bool ok;
+ struct sockaddr_storage ss = {0};
+ struct dns_res_rec *an = &reply->answers[i];
+
+ if (an->rr_type != DNS_QTYPE_A) {
+ continue;
+ }
+ if (an->name == NULL) {
+ /* Can this happen? */
+ continue;
+ }
+ if (an->rdata.ipv4_record == NULL) {
+ /* Can this happen? */
+ continue;
+ }
+ ok = dns_res_rec_get_sockaddr(an,
+ &ss);
+ if (!ok) {
+ continue;
+ }
+ if (is_zero_addr(&ss)) {
+ continue;
+ }
+ state->addrs[state->num_names].u.ss = ss;
+ state->addrs[state->num_names].sa_socklen =
+ sizeof(struct sockaddr_in);
+ state->hostnames[state->num_names] = talloc_strdup(
+ state->hostnames,
+ an->name);
+ if (tevent_req_nomem(state->hostnames[state->num_names], req)) {
+ return;
+ }
+ state->num_names += 1;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS ads_dns_lookup_a_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *rcode_out,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out)
+{
+ struct ads_dns_lookup_a_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_a_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (rcode_out != NULL) {
+ /*
+ * If we got no names, an upper layer may
+ * want to print a debug message.
+ */
+ *rcode_out = state->rcode;
+ }
+ if (hostnames_out != NULL) {
+ *hostnames_out = talloc_move(mem_ctx,
+ &state->hostnames);
+ }
+ if (addrs_out != NULL) {
+ *addrs_out = talloc_move(mem_ctx,
+ &state->addrs);
+ }
+ *num_names_out = state->num_names;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Simple wrapper for a DNS A query
+*********************************************************************/
+
+NTSTATUS ads_dns_lookup_a(TALLOC_CTX *ctx,
+ const char *name_in,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(ctx);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = ads_dns_lookup_a_send(ev, ev, name_in);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ /*
+ * Synchronous doesn't need to care about the rcode or
+ * a copy of the name_in.
+ */
+ status = ads_dns_lookup_a_recv(req,
+ ctx,
+ NULL,
+ num_names_out,
+ hostnames_out,
+ addrs_out);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+#if defined(HAVE_IPV6)
+/*********************************************************************
+ Async AAAA record lookup.
+*********************************************************************/
+
+struct ads_dns_lookup_aaaa_state {
+ uint8_t rcode;
+ size_t num_names;
+ char **hostnames;
+ struct samba_sockaddr *addrs;
+};
+
+static void ads_dns_lookup_aaaa_done(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_lookup_aaaa_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name)
+{
+ struct tevent_req *req, *subreq = NULL;
+ struct ads_dns_lookup_aaaa_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ads_dns_lookup_aaaa_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = dns_lookup_send(
+ state,
+ ev,
+ NULL,
+ name,
+ DNS_QCLASS_IN,
+ DNS_QTYPE_AAAA);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ads_dns_lookup_aaaa_done, req);
+ return req;
+}
+
+static void ads_dns_lookup_aaaa_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ads_dns_lookup_aaaa_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_aaaa_state);
+ int ret;
+ struct dns_name_packet *reply = NULL;
+ uint16_t i;
+
+ ret = dns_lookup_recv(subreq, state, &reply);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
+ return;
+ }
+
+ state->rcode = (reply->operation & DNS_RCODE);
+ if (state->rcode != DNS_RCODE_OK) {
+ /* Don't bother looking for answers. */
+ tevent_req_done(req);
+ return;
+ }
+
+ /*
+ * We don't care about CNAME answers here. We're
+ * just wanting an async name -> IPv6 lookup.
+ */
+ for (i = 0; i < reply->ancount; i++) {
+ if (reply->answers[i].rr_type == DNS_QTYPE_AAAA) {
+ state->num_names += 1;
+ }
+ }
+
+ state->hostnames = talloc_zero_array(state,
+ char *,
+ state->num_names);
+ if (tevent_req_nomem(state->hostnames, req)) {
+ return;
+ }
+ state->addrs = talloc_zero_array(state,
+ struct samba_sockaddr,
+ state->num_names);
+ if (tevent_req_nomem(state->addrs, req)) {
+ return;
+ }
+
+ state->num_names = 0;
+
+ for (i = 0; i < reply->ancount; i++) {
+ bool ok;
+ struct sockaddr_storage ss = {0};
+ struct dns_res_rec *an = &reply->answers[i];
+
+ if (an->rr_type != DNS_QTYPE_AAAA) {
+ continue;
+ }
+ if (an->name == NULL) {
+ /* Can this happen? */
+ continue;
+ }
+ if (an->rdata.ipv6_record == NULL) {
+ /* Can this happen? */
+ continue;
+ }
+ ok = dns_res_rec_get_sockaddr(an,
+ &ss);
+ if (!ok) {
+ continue;
+ }
+ if (is_zero_addr(&ss)) {
+ continue;
+ }
+ state->addrs[state->num_names].u.ss = ss;
+ state->addrs[state->num_names].sa_socklen =
+ sizeof(struct sockaddr_in6);
+
+ state->hostnames[state->num_names] = talloc_strdup(
+ state->hostnames,
+ an->name);
+ if (tevent_req_nomem(state->hostnames[state->num_names], req)) {
+ return;
+ }
+ state->num_names += 1;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS ads_dns_lookup_aaaa_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *rcode_out,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out)
+{
+ struct ads_dns_lookup_aaaa_state *state = tevent_req_data(
+ req, struct ads_dns_lookup_aaaa_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (rcode_out != NULL) {
+ /*
+ * If we got no names, an upper layer may
+ * want to print a debug message.
+ */
+ *rcode_out = state->rcode;
+ }
+ if (hostnames_out != NULL) {
+ *hostnames_out = talloc_move(mem_ctx,
+ &state->hostnames);
+ }
+ if (addrs_out != NULL) {
+ *addrs_out = talloc_move(mem_ctx,
+ &state->addrs);
+ }
+ *num_names_out = state->num_names;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Simple wrapper for a DNS AAAA query
+*********************************************************************/
+
+NTSTATUS ads_dns_lookup_aaaa(TALLOC_CTX *ctx,
+ const char *name_in,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(ctx);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = ads_dns_lookup_aaaa_send(ev, ev, name_in);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ /*
+ * Synchronous doesn't need to care about the rcode or
+ * a copy of the name_in.
+ */
+ status = ads_dns_lookup_aaaa_recv(req,
+ ctx,
+ NULL,
+ num_names_out,
+ hostnames_out,
+ addrs_out);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+#endif
diff --git a/lib/addns/dnsquery.h b/lib/addns/dnsquery.h
new file mode 100644
index 0000000..51f6b8f
--- /dev/null
+++ b/lib/addns/dnsquery.h
@@ -0,0 +1,84 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Gerald Carter 2006.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIB_ADDNS_DNSQUERY_H__
+#define __LIB_ADDNS_DNSQUERY_H__
+
+#include "replace.h"
+#include <tevent.h>
+#include "libcli/dns/dns.h"
+#include "lib/util/util_net.h"
+#include "libcli/util/ntstatus.h"
+
+/* The following definitions come from libads/dns.c */
+
+struct tevent_req *ads_dns_lookup_srv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name);
+NTSTATUS ads_dns_lookup_srv_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct dns_rr_srv **srvs,
+ size_t *num_srvs);
+NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
+ const char *name,
+ struct dns_rr_srv **dclist,
+ size_t *numdcs);
+struct tevent_req *ads_dns_lookup_ns_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name);
+NTSTATUS ads_dns_lookup_ns_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct dns_rr_ns **nss,
+ size_t *num_nss);
+NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
+ const char *dnsdomain,
+ struct dns_rr_ns **nslist,
+ size_t *numns);
+struct tevent_req *ads_dns_lookup_a_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name);
+NTSTATUS ads_dns_lookup_a_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *rcode_out,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out);
+NTSTATUS ads_dns_lookup_a(TALLOC_CTX *ctx,
+ const char *name_in,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out);
+#if defined(HAVE_IPV6)
+struct tevent_req *ads_dns_lookup_aaaa_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name);
+NTSTATUS ads_dns_lookup_aaaa_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *rcode_out,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out);
+NTSTATUS ads_dns_lookup_aaaa(TALLOC_CTX *ctx,
+ const char *name_in,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out);
+#endif
+
+#endif /* __LIB_ADDNS_DNSQUERY_H__ */
diff --git a/lib/addns/dnsquery_srv.c b/lib/addns/dnsquery_srv.c
new file mode 100644
index 0000000..ac9e612
--- /dev/null
+++ b/lib/addns/dnsquery_srv.c
@@ -0,0 +1,560 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "dnsquery.h"
+#include "dnsquery_srv.h"
+#include "lib/util/debug.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/samba_util.h"
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/ndr/libndr.h"
+
+/*
+ * For an array of dns_rr_srv records, issue A/AAAA queries for those
+ * records where the initial reply did not return IP addresses.
+ */
+
+struct dns_rr_srv_fill_state {
+ struct dns_rr_srv *srvs;
+ size_t num_srvs;
+
+ struct tevent_req **subreqs;
+ size_t num_outstanding;
+};
+
+static void dns_rr_srv_fill_done_a(struct tevent_req *subreq);
+#if defined(HAVE_IPV6)
+static void dns_rr_srv_fill_done_aaaa(struct tevent_req *subreq);
+#endif
+static void dns_rr_srv_fill_timedout(struct tevent_req *subreq);
+
+static struct tevent_req *dns_rr_srv_fill_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dns_rr_srv *srvs,
+ size_t num_srvs,
+ uint32_t timeout)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct dns_rr_srv_fill_state *state = NULL;
+ size_t i, num_subreqs;
+
+ req = tevent_req_create(mem_ctx, &state, struct dns_rr_srv_fill_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->srvs = srvs;
+ state->num_srvs = num_srvs;
+
+ /*
+ * Without IPv6 we only use half of this, but who does not
+ * have IPv6 these days?
+ */
+ num_subreqs = num_srvs * 2;
+
+ state->subreqs = talloc_zero_array(
+ state, struct tevent_req *, num_subreqs);
+ if (tevent_req_nomem(state->subreqs, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_srvs; i++) {
+
+ if (srvs[i].hostname == NULL) {
+ continue;
+ }
+ if (srvs[i].ss_s != NULL) {
+ /* IP address returned in SRV record. */
+ continue;
+ }
+
+ subreq = ads_dns_lookup_a_send(
+ state->subreqs, ev, srvs[i].hostname);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(state->subreqs);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, dns_rr_srv_fill_done_a, req);
+
+ state->subreqs[i*2] = subreq;
+ state->num_outstanding += 1;
+
+#if defined(HAVE_IPV6)
+ subreq = ads_dns_lookup_aaaa_send(
+ state->subreqs, ev, srvs[i].hostname);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(state->subreqs);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, dns_rr_srv_fill_done_aaaa, req);
+
+ state->subreqs[i*2+1] = subreq;
+ state->num_outstanding += 1;
+#endif
+ }
+
+ if (state->num_outstanding == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = tevent_wakeup_send(
+ state->subreqs,
+ ev,
+ tevent_timeval_current_ofs(timeout, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, dns_rr_srv_fill_timedout, req);
+
+ return req;
+}
+
+static void dns_rr_srv_fill_done(
+ struct tevent_req *subreq,
+ NTSTATUS (*recv_fn)(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *rcode_out,
+ size_t *num_names_out,
+ char ***hostnames_out,
+ struct samba_sockaddr **addrs_out))
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct dns_rr_srv_fill_state *state = tevent_req_data(
+ req, struct dns_rr_srv_fill_state);
+ size_t num_subreqs = talloc_array_length(state->subreqs);
+ struct dns_rr_srv *srv = NULL;
+ size_t num_ips;
+ struct sockaddr_storage *tmp = NULL;
+ uint8_t rcode = 0;
+ char **hostnames_out = NULL;
+ struct samba_sockaddr *addrs = NULL;
+ size_t num_addrs = 0;
+ NTSTATUS status;
+ size_t i;
+ const char *ip_dbg_str = (recv_fn == ads_dns_lookup_a_recv) ?
+ "A" : "AAAA";
+
+ /*
+ * This loop walks all potential subreqs. Typical setups won't
+ * have more than a few DCs. If you have really many DCs
+ * (hundreds) and a DNS that doesn't return the DC IPs in the
+ * SRV reply, you have bigger problems than this loop linearly
+ * walking a pointer array. This is theoretically O(n^2), but
+ * probably the DNS roundtrip time outweighs this by a
+ * lot. And we have a global timeout on this whole
+ * dns_rr_srv_fill routine.
+ */
+ for (i=0; i<num_subreqs; i++) {
+ if (state->subreqs[i] == subreq) {
+ state->subreqs[i] = NULL;
+ break;
+ }
+ }
+ if (i == num_subreqs) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ srv = &state->srvs[i/2]; /* 2 subreq per srv */
+
+ status = recv_fn(
+ subreq,
+ state,
+ &rcode,
+ &num_addrs,
+ &hostnames_out,
+ &addrs);
+ TALLOC_FREE(subreq);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_INFO("async DNS %s lookup for %s returned %s\n",
+ ip_dbg_str,
+ srv->hostname,
+ nt_errstr(status));
+ num_addrs = 0;
+ goto done;
+ }
+
+ if (rcode != DNS_RCODE_OK) {
+ DBG_INFO("async DNS %s lookup for %s returned DNS code "
+ "%"PRIu8"\n",
+ ip_dbg_str,
+ srv->hostname,
+ rcode);
+ num_addrs = 0;
+ goto done;
+ }
+
+ if (num_addrs == 0) {
+ DBG_INFO("async DNS %s lookup for %s returned 0 addresses.\n",
+ ip_dbg_str,
+ srv->hostname);
+ goto done;
+ }
+
+ num_ips = talloc_array_length(srv->ss_s);
+
+ if (num_ips + num_addrs < num_addrs) {
+ /* overflow */
+ goto done;
+ }
+
+ tmp = talloc_realloc(
+ state->srvs,
+ srv->ss_s,
+ struct sockaddr_storage,
+ num_ips + num_addrs);
+ if (tmp == NULL) {
+ goto done;
+ }
+
+ for (i=0; i<num_addrs; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ DBG_INFO("async DNS %s lookup for %s [%zu] got %s -> %s\n",
+ ip_dbg_str,
+ srv->hostname,
+ i,
+ hostnames_out[i],
+ print_sockaddr(addr, sizeof(addr), &addrs[i].u.ss));
+ tmp[num_ips + i] = addrs[i].u.ss;
+ }
+ srv->ss_s = tmp;
+ srv->num_ips = num_ips + num_addrs;
+
+done:
+ state->num_outstanding -= 1;
+ if (state->num_outstanding == 0) {
+ tevent_req_done(req);
+ }
+}
+
+static void dns_rr_srv_fill_done_a(struct tevent_req *subreq)
+{
+ dns_rr_srv_fill_done(subreq, ads_dns_lookup_a_recv);
+}
+
+#if defined(HAVE_IPV6)
+static void dns_rr_srv_fill_done_aaaa(struct tevent_req *subreq)
+{
+ dns_rr_srv_fill_done(subreq, ads_dns_lookup_aaaa_recv);
+}
+#endif
+
+static void dns_rr_srv_fill_timedout(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct dns_rr_srv_fill_state *state = tevent_req_data(
+ req, struct dns_rr_srv_fill_state);
+ bool ok;
+
+ if (DEBUGLEVEL >= DBGLVL_INFO) {
+ size_t i, num_addrs = 0;
+
+ for (i=0; i<state->num_srvs; i++) {
+ /*
+ * Count for the debug. Code that fills this
+ * in ensures no wrap.
+ */
+ num_addrs += state->srvs[i].num_ips;
+ }
+
+ DBG_INFO("async DNS lookup timed out after %zu addresses "
+ "returned (not an error)\n",
+ num_addrs);
+ }
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ TALLOC_FREE(state->subreqs);
+ if (!ok) {
+ tevent_req_oom(subreq);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS dns_rr_srv_fill_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Request a SRV record and fill in the A/AAAA records if the SRV
+ * record did not carry them.
+ */
+
+struct ads_dns_query_srv_state {
+ struct tevent_context *ev;
+ uint32_t async_dns_timeout;
+ const char *query;
+
+ struct tevent_req *fill_req;
+ struct tevent_req *timeout_req;
+ struct dns_rr_srv *srvs;
+ size_t num_srvs;
+};
+
+static void ads_dns_query_srv_site_aware_done(struct tevent_req *subreq);
+static void ads_dns_query_srv_done(struct tevent_req *subreq);
+static void ads_dns_query_srv_filled(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_query_srv_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint32_t async_dns_timeout,
+ const char *sitename,
+ const char *query)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct ads_dns_query_srv_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct ads_dns_query_srv_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->async_dns_timeout = async_dns_timeout;
+ state->query = query;
+
+ if ((sitename != NULL) && (sitename[0] != '\0')) {
+ char *after_tcp = NULL;
+ char *site_aware = NULL;
+
+ /*
+ * ".<SITENAME>._sites" comes after "._tcp."
+ */
+ after_tcp = strstr(state->query, "._tcp.");
+ if (after_tcp == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ after_tcp += 6; /* strlen("._tcp.") */
+
+ site_aware = talloc_asprintf(
+ state,
+ "%.*s%s._sites.%s",
+ (int)(after_tcp - state->query),
+ state->query,
+ sitename,
+ after_tcp);
+ if (tevent_req_nomem(site_aware, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = ads_dns_lookup_srv_send(state, ev, site_aware);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, ads_dns_query_srv_site_aware_done, req);
+ return req;
+ }
+
+ subreq = ads_dns_lookup_srv_send(state, state->ev, state->query);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ads_dns_query_srv_done, req);
+ return req;
+}
+
+static void ads_dns_query_srv_site_aware_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ads_dns_query_srv_state *state = tevent_req_data(
+ req, struct ads_dns_query_srv_state);
+ NTSTATUS status;
+
+ status = ads_dns_lookup_srv_recv(
+ subreq, state, &state->srvs, &state->num_srvs);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_REFUSED)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(status) && (state->num_srvs != 0)) {
+ if (state->async_dns_timeout == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = dns_rr_srv_fill_send(
+ state,
+ state->ev,
+ state->srvs,
+ state->num_srvs,
+ state->async_dns_timeout);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(
+ subreq, ads_dns_query_srv_filled, req);
+ return;
+ }
+
+ subreq = ads_dns_lookup_srv_send(state, state->ev, state->query);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, ads_dns_query_srv_done, req);
+}
+
+static void ads_dns_query_srv_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ads_dns_query_srv_state *state = tevent_req_data(
+ req, struct ads_dns_query_srv_state);
+ NTSTATUS status;
+
+ status = ads_dns_lookup_srv_recv(
+ subreq, state, &state->srvs, &state->num_srvs);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if ((state->num_srvs == 0) || (state->async_dns_timeout == 0)) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = dns_rr_srv_fill_send(
+ state,
+ state->ev,
+ state->srvs,
+ state->num_srvs,
+ state->async_dns_timeout);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, ads_dns_query_srv_filled, req);
+}
+
+static void ads_dns_query_srv_filled(struct tevent_req *subreq)
+{
+ NTSTATUS status = dns_rr_srv_fill_recv(subreq);
+ return tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS ads_dns_query_srv_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct dns_rr_srv **srvs,
+ size_t *num_srvs)
+{
+ struct ads_dns_query_srv_state *state = tevent_req_data(
+ req, struct ads_dns_query_srv_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ if (srvs != NULL) {
+ *srvs = talloc_move(mem_ctx, &state->srvs);
+ }
+ if (num_srvs != NULL) {
+ *num_srvs = state->num_srvs;
+ }
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ads_dns_query_srv(
+ TALLOC_CTX *mem_ctx,
+ uint32_t async_dns_timeout,
+ const char *sitename,
+ const char *query,
+ struct dns_rr_srv **srvs,
+ size_t *num_srvs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = ads_dns_query_srv_send(
+ frame, ev, async_dns_timeout, sitename, query);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = ads_dns_query_srv_recv(req, mem_ctx, srvs, num_srvs);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+char *ads_dns_query_string_dcs(TALLOC_CTX *mem_ctx, const char *realm)
+{
+ char *ret = talloc_asprintf(mem_ctx, "_ldap._tcp.dc._msdcs.%s", realm);
+ return ret;
+}
+
+char *ads_dns_query_string_gcs(TALLOC_CTX *mem_ctx, const char *realm)
+{
+ char *ret = talloc_asprintf(mem_ctx, "_ldap._tcp.gc._msdcs.%s", realm);
+ return ret;
+}
+
+char *ads_dns_query_string_kdcs(TALLOC_CTX *mem_ctx, const char *realm)
+{
+ char *ret = talloc_asprintf(
+ mem_ctx, "_kerberos._tcp.dc._msdcs.%s", realm);
+ return ret;
+}
+
+char *ads_dns_query_string_pdc(TALLOC_CTX *mem_ctx, const char *realm)
+{
+ char *ret = talloc_asprintf(
+ mem_ctx, "_ldap._tcp.pdc._msdcs.%s", realm);
+ return ret;
+}
+
+char *ads_dns_query_string_dcs_guid(
+ TALLOC_CTX *mem_ctx,
+ const struct GUID *domain_guid,
+ const char *realm)
+{
+ struct GUID_txt_buf buf;
+ char *ret = NULL;
+
+ talloc_asprintf(
+ mem_ctx,
+ "_ldap._tcp.%s.domains._msdcs.%s",
+ GUID_buf_string(domain_guid, &buf),
+ realm);
+ return ret;
+}
diff --git a/lib/addns/dnsquery_srv.h b/lib/addns/dnsquery_srv.h
new file mode 100644
index 0000000..d10c74e
--- /dev/null
+++ b/lib/addns/dnsquery_srv.h
@@ -0,0 +1,54 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIB_ADDNS_DNSQUERY_SRV_H__
+#define __LIB_ADDNS_DNSQUERY_SRV_H__
+
+#include "replace.h"
+#include <tevent.h>
+#include "libcli/util/ntstatus.h"
+#include "libcli/dns/dns.h"
+
+struct tevent_req *ads_dns_query_srv_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint32_t async_dns_timeout,
+ const char *sitename,
+ const char *query);
+NTSTATUS ads_dns_query_srv_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct dns_rr_srv **srvs,
+ size_t *num_srvs);
+NTSTATUS ads_dns_query_srv(
+ TALLOC_CTX *mem_ctx,
+ uint32_t async_dns_timeout,
+ const char *sitename,
+ const char *query,
+ struct dns_rr_srv **srvs,
+ size_t *num_srvs);
+
+char *ads_dns_query_string_dcs(TALLOC_CTX *mem_ctx, const char *realm);
+char *ads_dns_query_string_gcs(TALLOC_CTX *mem_ctx, const char *realm);
+char *ads_dns_query_string_kdcs(TALLOC_CTX *mem_ctx, const char *realm);
+char *ads_dns_query_string_pdc(TALLOC_CTX *mem_ctx, const char *realm);
+
+struct GUID;
+char *ads_dns_query_string_dcs_guid(
+ TALLOC_CTX *mem_ctx,
+ const struct GUID *domain_guid,
+ const char *realm);
+
+#endif /* _ADS_DNS_H */
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;
+}
diff --git a/lib/addns/dnssock.c b/lib/addns/dnssock.c
new file mode 100644
index 0000000..11f2e00
--- /dev/null
+++ b/lib/addns/dnssock.c
@@ -0,0 +1,420 @@
+/*
+ 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 "replace.h"
+#include "dns.h"
+#include <sys/time.h>
+#include <unistd.h>
+#include "system/select.h"
+#include "../lib/util/debug.h"
+
+static int destroy_dns_connection(struct dns_connection *conn)
+{
+ return close(conn->s);
+}
+
+/********************************************************************
+********************************************************************/
+
+static DNS_ERROR dns_open_helper(const char *nameserver,
+ const char *service,
+ struct addrinfo *hints,
+ TALLOC_CTX *mem_ctx,
+ struct dns_connection **ret_conn)
+{
+ int ret;
+ struct addrinfo *rp;
+ struct addrinfo *ai_result = NULL;
+ struct dns_connection *conn = NULL;
+
+ if (!(conn = talloc(mem_ctx, struct dns_connection))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ ret = getaddrinfo(nameserver, service, hints, &ai_result);
+ if (ret != 0) {
+ DEBUG(1,("dns_tcp_open: getaddrinfo: %s\n", gai_strerror(ret)));
+ TALLOC_FREE(conn);
+ return ERROR_DNS_INVALID_NAME_SERVER;
+ }
+
+ for (rp = ai_result; rp != NULL; rp = rp->ai_next) {
+ conn->s = socket(rp->ai_family,
+ rp->ai_socktype,
+ rp->ai_protocol);
+ if (conn->s == -1) {
+ continue;
+ }
+ do {
+ ret = connect(conn->s, rp->ai_addr, rp->ai_addrlen);
+ } while ((ret == -1) && (errno == EINTR));
+ if (ret != -1) {
+ /* Successful connect */
+ break;
+ }
+ close(conn->s);
+ }
+
+ freeaddrinfo(ai_result);
+
+ if (rp == NULL) {
+ TALLOC_FREE(conn);
+ return ERROR_DNS_CONNECTION_FAILED;
+ }
+
+ talloc_set_destructor(conn, destroy_dns_connection);
+
+ *ret_conn = conn;
+ return ERROR_DNS_SUCCESS;
+}
+
+static DNS_ERROR dns_tcp_open( const char *nameserver,
+ TALLOC_CTX *mem_ctx,
+ struct dns_connection **result )
+{
+ struct addrinfo hints;
+ struct dns_connection *conn;
+ DNS_ERROR dns_ret;
+ char service[16];
+
+ snprintf(service, sizeof(service), "%d", DNS_TCP_PORT);
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ dns_ret = dns_open_helper(nameserver, service, &hints, mem_ctx, &conn);
+ if (!ERR_DNS_IS_OK(dns_ret)) {
+ return dns_ret;
+ }
+
+ conn->hType = DNS_TCP;
+ *result = conn;
+ return ERROR_DNS_SUCCESS;
+}
+
+/********************************************************************
+ * ********************************************************************/
+
+static DNS_ERROR dns_udp_open( const char *nameserver,
+ TALLOC_CTX *mem_ctx,
+ struct dns_connection **result )
+{
+ struct addrinfo hints;
+ struct sockaddr_storage RecvAddr;
+ struct dns_connection *conn = NULL;
+ DNS_ERROR dns_ret;
+ socklen_t RecvAddrLen;
+ char service[16];
+
+ snprintf(service, sizeof(service), "%d", DNS_UDP_PORT);
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = IPPROTO_UDP;
+
+ dns_ret = dns_open_helper(nameserver, service, &hints, mem_ctx, &conn);
+ if (!ERR_DNS_IS_OK(dns_ret)) {
+ TALLOC_FREE(conn);
+ return dns_ret;
+ }
+
+ /* Set up the RecvAddr structure with the IP address of
+ the receiver and the specified port number. */
+
+ RecvAddrLen = sizeof(RecvAddr);
+ if (getpeername(conn->s,
+ (struct sockaddr *)&RecvAddr,
+ &RecvAddrLen) == -1) {
+ return ERROR_DNS_CONNECTION_FAILED;
+ }
+
+ conn->hType = DNS_UDP;
+ memcpy(&conn->RecvAddr, &RecvAddr, sizeof(struct sockaddr_storage));
+
+ *result = conn;
+ return ERROR_DNS_SUCCESS;
+}
+
+/********************************************************************
+********************************************************************/
+
+DNS_ERROR dns_open_connection( const char *nameserver, int32_t dwType,
+ TALLOC_CTX *mem_ctx,
+ struct dns_connection **conn )
+{
+ switch ( dwType ) {
+ case DNS_TCP:
+ return dns_tcp_open( nameserver, mem_ctx, conn );
+ case DNS_UDP:
+ return dns_udp_open( nameserver, mem_ctx, conn );
+ }
+
+ return ERROR_DNS_INVALID_PARAMETER;
+}
+
+static DNS_ERROR write_all(int fd, uint8_t *data, size_t len)
+{
+ size_t total = 0;
+
+ while (total < len) {
+
+ ssize_t ret;
+
+ do {
+ ret = write(fd, data + total, len - total);
+ } while ((ret == -1) && (errno == EINTR));
+
+ if (ret <= 0) {
+ /*
+ * EOF or error
+ */
+ return ERROR_DNS_SOCKET_ERROR;
+ }
+
+ total += ret;
+ }
+
+ return ERROR_DNS_SUCCESS;
+}
+
+static DNS_ERROR dns_send_tcp(struct dns_connection *conn,
+ const struct dns_buffer *buf)
+{
+ uint16_t len = htons(buf->offset);
+ DNS_ERROR err;
+
+ err = write_all(conn->s, (uint8_t *)&len, sizeof(len));
+ if (!ERR_DNS_IS_OK(err)) return err;
+
+ return write_all(conn->s, buf->data, buf->offset);
+}
+
+static DNS_ERROR dns_send_udp(struct dns_connection *conn,
+ const struct dns_buffer *buf)
+{
+ ssize_t ret;
+
+ do {
+ ret = send(conn->s, buf->data, buf->offset, 0);
+ } while ((ret == -1) && (errno == EINTR));
+
+ if (ret != buf->offset) {
+ return ERROR_DNS_SOCKET_ERROR;
+ }
+
+ return ERROR_DNS_SUCCESS;
+}
+
+DNS_ERROR dns_send(struct dns_connection *conn, const struct dns_buffer *buf)
+{
+ if (conn->hType == DNS_TCP) {
+ return dns_send_tcp(conn, buf);
+ }
+
+ if (conn->hType == DNS_UDP) {
+ return dns_send_udp(conn, buf);
+ }
+
+ return ERROR_DNS_INVALID_PARAMETER;
+}
+
+static DNS_ERROR read_all(int fd, uint8_t *data, size_t len)
+{
+ size_t total = 0;
+
+ while (total < len) {
+ struct pollfd pfd;
+ ssize_t ret;
+ int fd_ready;
+
+ ZERO_STRUCT(pfd);
+ pfd.fd = fd;
+ pfd.events = POLLIN|POLLHUP;
+
+ fd_ready = poll(&pfd, 1, 10000);
+ if (fd_ready == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ return ERROR_DNS_SOCKET_ERROR;
+ }
+ if ( fd_ready == 0 ) {
+ /* read timeout */
+ return ERROR_DNS_SOCKET_ERROR;
+ }
+
+ do {
+ ret = read(fd, data + total, len - total);
+ } while ((ret == -1) && (errno == EINTR));
+
+ if (ret <= 0) {
+ /* EOF or error */
+ return ERROR_DNS_SOCKET_ERROR;
+ }
+
+ total += ret;
+ }
+
+ return ERROR_DNS_SUCCESS;
+}
+
+static DNS_ERROR dns_receive_tcp(TALLOC_CTX *mem_ctx,
+ struct dns_connection *conn,
+ struct dns_buffer **presult)
+{
+ struct dns_buffer *buf;
+ DNS_ERROR err;
+ uint16_t len;
+
+ if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = read_all(conn->s, (uint8_t *)&len, sizeof(len));
+ if (!ERR_DNS_IS_OK(err)) {
+ return err;
+ }
+
+ buf->size = ntohs(len);
+
+ if (buf->size == 0) {
+ *presult = buf;
+ return ERROR_DNS_SUCCESS;
+ }
+
+ if (!(buf->data = talloc_array(buf, uint8_t, buf->size))) {
+ TALLOC_FREE(buf);
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = read_all(conn->s, buf->data, talloc_get_size(buf->data));
+ if (!ERR_DNS_IS_OK(err)) {
+ TALLOC_FREE(buf);
+ return err;
+ }
+
+ *presult = buf;
+ return ERROR_DNS_SUCCESS;
+}
+
+static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx,
+ struct dns_connection *conn,
+ struct dns_buffer **presult)
+{
+ struct dns_buffer *buf;
+ ssize_t received;
+
+ if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ /*
+ * UDP based DNS can only be 512 bytes
+ */
+
+ if (!(buf->data = talloc_array(buf, uint8_t, 512))) {
+ TALLOC_FREE(buf);
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ do {
+ received = recv(conn->s, (void *)buf->data, 512, 0);
+ } while ((received == -1) && (errno == EINTR));
+
+ if (received == -1) {
+ TALLOC_FREE(buf);
+ return ERROR_DNS_SOCKET_ERROR;
+ }
+
+ if (received > 512) {
+ TALLOC_FREE(buf);
+ return ERROR_DNS_BAD_RESPONSE;
+ }
+
+ buf->size = received;
+ buf->offset = 0;
+
+ *presult = buf;
+ return ERROR_DNS_SUCCESS;
+}
+
+DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
+ struct dns_buffer **presult)
+{
+ if (conn->hType == DNS_TCP) {
+ return dns_receive_tcp(mem_ctx, conn, presult);
+ }
+
+ if (conn->hType == DNS_UDP) {
+ return dns_receive_udp(mem_ctx, conn, presult);
+ }
+
+ return ERROR_DNS_INVALID_PARAMETER;
+}
+
+DNS_ERROR dns_transaction(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
+ const struct dns_request *req,
+ struct dns_request **resp)
+{
+ struct dns_buffer *buf = NULL;
+ DNS_ERROR err;
+
+ err = dns_marshall_request(mem_ctx, req, &buf);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_send(conn, buf);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+ TALLOC_FREE(buf);
+
+ err = dns_receive(mem_ctx, conn, &buf);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_unmarshall_request(mem_ctx, buf, resp);
+
+ error:
+ TALLOC_FREE(buf);
+ return err;
+}
+
+DNS_ERROR dns_update_transaction(TALLOC_CTX *mem_ctx,
+ struct dns_connection *conn,
+ struct dns_update_request *up_req,
+ struct dns_update_request **up_resp)
+{
+ struct dns_request *resp;
+ DNS_ERROR err;
+
+ err = dns_transaction(mem_ctx, conn, dns_update2request(up_req),
+ &resp);
+
+ if (!ERR_DNS_IS_OK(err)) return err;
+
+ *up_resp = dns_request2update(resp);
+ return ERROR_DNS_SUCCESS;
+}
diff --git a/lib/addns/dnsutils.c b/lib/addns/dnsutils.c
new file mode 100644
index 0000000..d1a3173
--- /dev/null
+++ b/lib/addns/dnsutils.c
@@ -0,0 +1,149 @@
+/*
+ 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 "includes.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+#include "dns.h"
+#include <ctype.h>
+
+
+static DNS_ERROR LabelList( TALLOC_CTX *mem_ctx,
+ const char *name,
+ struct dns_domain_label **presult )
+{
+ struct dns_domain_label *result;
+ const char *dot;
+
+ for (dot = name; *dot != '\0'; dot += 1) {
+ char c = *dot;
+
+ if (c == '.')
+ break;
+
+ if (c == '-') continue;
+ if ((c >= 'a') && (c <= 'z')) continue;
+ if ((c >= 'A') && (c <= 'Z')) continue;
+ if ((c >= '0') && (c <= '9')) continue;
+
+ return ERROR_DNS_INVALID_NAME;
+ }
+
+ if ((dot - name) > 63) {
+ /*
+ * DNS labels can only be 63 chars long
+ */
+ return ERROR_DNS_INVALID_NAME;
+ }
+
+ if (!(result = talloc_zero(mem_ctx, struct dns_domain_label))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ if (*dot == '\0') {
+ /*
+ * No dot around, so this is the last component
+ */
+
+ if (!(result->label = talloc_strdup(result, name))) {
+ TALLOC_FREE(result);
+ return ERROR_DNS_NO_MEMORY;
+ }
+ result->len = strlen(result->label);
+ *presult = result;
+ return ERROR_DNS_SUCCESS;
+ }
+
+ if (dot[1] == '.') {
+ /*
+ * Two dots in a row, reject
+ */
+
+ TALLOC_FREE(result);
+ return ERROR_DNS_INVALID_NAME;
+ }
+
+ if (dot[1] != '\0') {
+ /*
+ * Something follows, get the rest
+ */
+
+ DNS_ERROR err = LabelList(result, dot+1, &result->next);
+
+ if (!ERR_DNS_IS_OK(err)) {
+ TALLOC_FREE(result);
+ return err;
+ }
+ }
+
+ result->len = (dot - name);
+
+ if (!(result->label = talloc_strndup(result, name, result->len))) {
+ TALLOC_FREE(result);
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ *presult = result;
+ return ERROR_DNS_SUCCESS;
+}
+
+DNS_ERROR dns_domain_name_from_string( TALLOC_CTX *mem_ctx,
+ const char *pszDomainName,
+ struct dns_domain_name **presult )
+{
+ struct dns_domain_name *result;
+ DNS_ERROR err;
+
+ if (!(result = talloc(mem_ctx, struct dns_domain_name))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = LabelList( result, pszDomainName, &result->pLabelList );
+ if (!ERR_DNS_IS_OK(err)) {
+ TALLOC_FREE(result);
+ return err;
+ }
+
+ *presult = result;
+ return ERROR_DNS_SUCCESS;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+char *dns_generate_keyname( TALLOC_CTX *mem_ctx )
+{
+ char *result = NULL;
+#if defined(HAVE_KRB5)
+
+ struct GUID guid;
+
+ guid = GUID_random();
+ result = GUID_string(mem_ctx, &guid);
+
+#endif
+
+ return result;
+}
diff --git a/lib/addns/error.c b/lib/addns/error.c
new file mode 100644
index 0000000..361388c
--- /dev/null
+++ b/lib/addns/error.c
@@ -0,0 +1,59 @@
+/*
+ Linux DNS client library implementation
+ Copyright (C) 2010 Guenther Deschner
+
+ ** 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 "dnserr.h"
+
+typedef struct {
+ const char *dns_errstr;
+ DNS_ERROR dns_errcode;
+} dns_err_code_struct;
+
+static const dns_err_code_struct dns_errs[] =
+{
+ { "ERROR_DNS_SUCCESS", ERROR_DNS_SUCCESS },
+ { "ERROR_DNS_RECORD_NOT_FOUND", ERROR_DNS_RECORD_NOT_FOUND },
+ { "ERROR_DNS_BAD_RESPONSE", ERROR_DNS_BAD_RESPONSE },
+ { "ERROR_DNS_INVALID_PARAMETER", ERROR_DNS_INVALID_PARAMETER },
+ { "ERROR_DNS_NO_MEMORY", ERROR_DNS_NO_MEMORY },
+ { "ERROR_DNS_INVALID_NAME_SERVER", ERROR_DNS_INVALID_NAME_SERVER },
+ { "ERROR_DNS_CONNECTION_FAILED", ERROR_DNS_CONNECTION_FAILED },
+ { "ERROR_DNS_GSS_ERROR", ERROR_DNS_GSS_ERROR },
+ { "ERROR_DNS_INVALID_NAME", ERROR_DNS_INVALID_NAME },
+ { "ERROR_DNS_INVALID_MESSAGE", ERROR_DNS_INVALID_MESSAGE },
+ { "ERROR_DNS_SOCKET_ERROR", ERROR_DNS_SOCKET_ERROR },
+ { "ERROR_DNS_UPDATE_FAILED", ERROR_DNS_UPDATE_FAILED },
+ { NULL, ERROR_DNS_SUCCESS },
+};
+
+const char *dns_errstr(DNS_ERROR err)
+{
+ int i;
+
+ for (i=0; dns_errs[i].dns_errstr != NULL; i++) {
+ if (ERR_DNS_EQUAL(err, dns_errs[i].dns_errcode)) {
+ return dns_errs[i].dns_errstr;
+ }
+ }
+
+ return NULL;
+}
diff --git a/lib/addns/wscript_build b/lib/addns/wscript_build
new file mode 100644
index 0000000..cc72b35
--- /dev/null
+++ b/lib/addns/wscript_build
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('addns',
+ source='''
+ dnsquery.c
+ dnsrecord.c
+ dnsutils.c
+ dnssock.c
+ dnsgss.c
+ dnsmarshall.c
+ error.c
+ dnsquery_srv.c
+ ''',
+ public_deps='samba-util gssapi ndr resolv dns_lookup',
+ private_library=True,
+ vars=locals())