diff options
Diffstat (limited to 'lib/addns')
-rw-r--r-- | lib/addns/dns.h | 367 | ||||
-rw-r--r-- | lib/addns/dnserr.h | 87 | ||||
-rw-r--r-- | lib/addns/dnsgss.c | 344 | ||||
-rw-r--r-- | lib/addns/dnsmarshall.c | 532 | ||||
-rw-r--r-- | lib/addns/dnsquery.c | 782 | ||||
-rw-r--r-- | lib/addns/dnsquery.h | 84 | ||||
-rw-r--r-- | lib/addns/dnsquery_srv.c | 560 | ||||
-rw-r--r-- | lib/addns/dnsquery_srv.h | 54 | ||||
-rw-r--r-- | lib/addns/dnsrecord.c | 475 | ||||
-rw-r--r-- | lib/addns/dnssock.c | 420 | ||||
-rw-r--r-- | lib/addns/dnsutils.c | 149 | ||||
-rw-r--r-- | lib/addns/error.c | 59 | ||||
-rw-r--r-- | lib/addns/wscript_build | 16 |
13 files changed, 3929 insertions, 0 deletions
diff --git a/lib/addns/dns.h b/lib/addns/dns.h new file mode 100644 index 0000000..de1897b --- /dev/null +++ b/lib/addns/dns.h @@ -0,0 +1,367 @@ +/* + 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 **additionals; +}; + +/* + * 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 **additionals; +}; + +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_add_rrec(TALLOC_CTX *mem_ctx, struct dns_rrec *rec, + uint16_t *num_records, struct dns_rrec ***records); +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, + 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..a446da6 --- /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->additionals); + } + + 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->additionals); + + 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..a07ed78 --- /dev/null +++ b/lib/addns/dnsmarshall.c @@ -0,0 +1,532 @@ +/* + 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; + } + + 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->additionals[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->additionals = 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->additionals, buf, + &req->additionals[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->additionals == update->additionals)); +#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..869d459 --- /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; + } + /* + * Sychronous 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; + } + /* + * Sychronous 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..6cba22f --- /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 outweights 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..e6e205e --- /dev/null +++ b/lib/addns/dnsrecord.c @@ -0,0 +1,475 @@ +/* + 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, + 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, 3600, &ss_addrs[i], &rec); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + err = dns_create_aaaa_record(req, hostname, 3600, &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()) |