2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/os/dnssrv.c
2N/A *
2N/A * Copyright 1990,2000,2001,2002,2003 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A *
2N/A * do DNS SRV RR queries
2N/A */
2N/A
2N/A#include "autoconf.h"
2N/A#ifdef KRB5_DNS_LOOKUP
2N/A
2N/A#include "dnsglue.h"
2N/A
2N/A/*
2N/A * Lookup a KDC via DNS SRV records
2N/A */
2N/A
2N/Avoid
2N/Akrb5int_free_srv_dns_data (struct srv_dns_entry *p)
2N/A{
2N/A struct srv_dns_entry *next;
2N/A while (p) {
2N/A next = p->next;
2N/A free(p->host);
2N/A free(p);
2N/A p = next;
2N/A }
2N/A}
2N/A
2N/A/* Do DNS SRV query, return results in *answers.
2N/A
2N/A Make best effort to return all the data we can. On memory or
2N/A decoding errors, just return what we've got. Always return 0,
2N/A currently. */
2N/A
2N/Akrb5_error_code
2N/Akrb5int_make_srv_query_realm(const krb5_data *realm,
2N/A const char *service,
2N/A const char *protocol,
2N/A struct srv_dns_entry **answers)
2N/A{
2N/A const unsigned char *p = NULL, *base = NULL;
2N/A char host[MAXDNAME];
2N/A int size, ret, rdlen, nlen, len;
2N/A unsigned short priority, weight, port;
2N/A struct krb5int_dns_state *ds = NULL;
2N/A struct k5buf buf;
2N/A
2N/A struct srv_dns_entry *head = NULL;
2N/A struct srv_dns_entry *srv = NULL, *entry = NULL;
2N/A
2N/A /*
2N/A * First off, build a query of the form:
2N/A *
2N/A * service.protocol.realm
2N/A *
2N/A * which will most likely be something like:
2N/A *
2N/A * _kerberos._udp.REALM
2N/A *
2N/A */
2N/A
2N/A if (memchr(realm->data, 0, realm->length))
2N/A return 0;
2N/A krb5int_buf_init_fixed(&buf, host, sizeof(host));
2N/A krb5int_buf_add_fmt(&buf, "%s.%s.", service, protocol);
2N/A krb5int_buf_add_len(&buf, realm->data, realm->length);
2N/A
2N/A /* Realm names don't (normally) end with ".", but if the query
2N/A doesn't end with "." and doesn't get an answer as is, the
2N/A resolv code will try appending the local domain. Since the
2N/A realm names are absolutes, let's stop that.
2N/A
2N/A But only if a name has been specified. If we are performing
2N/A a search on the prefix alone then the intention is to allow
2N/A the local domain or domain search lists to be expanded. */
2N/A
2N/A len = krb5int_buf_len(&buf);
2N/A if (len > 0 && host[len - 1] != '.')
2N/A krb5int_buf_add(&buf, ".");
2N/A
2N/A if (krb5int_buf_data(&buf) == NULL)
2N/A return 0;
2N/A
2N/A#ifdef TEST
2N/A fprintf (stderr, "sending DNS SRV query for %s\n", host);
2N/A#endif
2N/A
2N/A size = krb5int_dns_init(&ds, host, C_IN, T_SRV);
2N/A if (size < 0)
2N/A goto out;
2N/A
2N/A for (;;) {
2N/A ret = krb5int_dns_nextans(ds, &base, &rdlen);
2N/A if (ret < 0 || base == NULL)
2N/A goto out;
2N/A
2N/A p = base;
2N/A
2N/A SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
2N/A SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
2N/A SAFE_GETUINT16(base, rdlen, p, 2, port, out);
2N/A
2N/A /*
2N/A * RFC 2782 says the target is never compressed in the reply;
2N/A * do we believe that? We need to flatten it anyway, though.
2N/A */
2N/A nlen = krb5int_dns_expand(ds, p, host, sizeof(host));
2N/A if (nlen < 0 || !INCR_OK(base, rdlen, p, nlen))
2N/A goto out;
2N/A
2N/A /*
2N/A * We got everything! Insert it into our list, but make sure
2N/A * it's in the right order. Right now we don't do anything
2N/A * with the weight field
2N/A */
2N/A
2N/A srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
2N/A if (srv == NULL)
2N/A goto out;
2N/A
2N/A srv->priority = priority;
2N/A srv->weight = weight;
2N/A srv->port = port;
2N/A /* The returned names are fully qualified. Don't let the
2N/A local resolver code do domain search path stuff. */
2N/A if (asprintf(&srv->host, "%s.", host) < 0) {
2N/A free(srv);
2N/A goto out;
2N/A }
2N/A
2N/A if (head == NULL || head->priority > srv->priority) {
2N/A srv->next = head;
2N/A head = srv;
2N/A } else {
2N/A /*
2N/A * This is confusing. Only insert an entry into this
2N/A * spot if:
2N/A * The next person has a higher priority (lower priorities
2N/A * are preferred).
2N/A * Or
2N/A * There is no next entry (we're at the end)
2N/A */
2N/A for (entry = head; entry != NULL; entry = entry->next) {
2N/A if ((entry->next &&
2N/A entry->next->priority > srv->priority) ||
2N/A entry->next == NULL) {
2N/A srv->next = entry->next;
2N/A entry->next = srv;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A if (ds != NULL) {
2N/A krb5int_dns_fini(ds);
2N/A ds = NULL;
2N/A }
2N/A *answers = head;
2N/A return 0;
2N/A}
2N/A#endif