#include "autoconf.h"
#ifdef KRB5_DNS_LOOKUP
#include "k5-int.h"
#include "os-proto.h"
void
krb5int_free_srv_dns_data (struct srv_dns_entry *p)
{
struct srv_dns_entry *next;
while (p) {
next = p->next;
free(p->host);
free(p);
p = next;
}
}
static char *
make_lookup_name(const krb5_data *realm, const char *service,
const char *protocol, const char *sitename)
{
struct k5buf buf;
if (memchr(realm->data, 0, realm->length))
return NULL;
k5_buf_init_dynamic(&buf);
k5_buf_add_fmt(&buf, "%s.", service);
if (protocol != NULL)
k5_buf_add_fmt(&buf, "%s.", protocol);
if (sitename != NULL)
k5_buf_add_fmt(&buf, "%s._sites.", sitename);
k5_buf_add_len(&buf, realm->data, realm->length);
if (buf.len > 0 && ((char *)buf.data)[buf.len - 1] != '.')
k5_buf_add(&buf, ".");
return k5_buf_cstring(&buf);
}
static void
place_srv_entry(struct srv_dns_entry **head, struct srv_dns_entry *new)
{
struct srv_dns_entry *entry;
if (*head == NULL || (*head)->priority > new->priority) {
new->next = *head;
*head = new;
return;
}
for (entry = *head; entry != NULL; entry = entry->next) {
if (entry->next == NULL || entry->next->priority > new->priority) {
new->next = entry->next;
entry->next = new;
break;
}
}
}
#ifdef _WIN32
#include <windns.h>
krb5_error_code
k5_make_uri_query(krb5_context context, const krb5_data *realm,
const char *service, const char *sitename,
struct srv_dns_entry **answers)
{
*answers = NULL;
return 0;
}
krb5_error_code
krb5int_make_srv_query_realm(krb5_context context, const krb5_data *realm,
const char *service, const char *protocol,
const char *sitename,
struct srv_dns_entry **answers)
{
char *name = NULL;
DNS_STATUS st;
PDNS_RECORD records, rr;
struct srv_dns_entry *head = NULL, *srv = NULL;
*answers = NULL;
name = make_lookup_name(realm, service, protocol, sitename);
if (name == NULL)
return 0;
TRACE_DNS_SRV_SEND(context, name);
st = DnsQuery_UTF8(name, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &records,
NULL);
if (st != ERROR_SUCCESS && sitename != NULL) {
free(name);
return krb5int_make_srv_query_realm(context, realm, service, protocol,
NULL, answers);
}
if (st != ERROR_SUCCESS)
return 0;
for (rr = records; rr != NULL; rr = rr->pNext) {
if (rr->wType != DNS_TYPE_SRV)
continue;
srv = malloc(sizeof(struct srv_dns_entry));
if (srv == NULL)
goto cleanup;
srv->priority = rr->Data.SRV.wPriority;
srv->weight = rr->Data.SRV.wWeight;
srv->port = rr->Data.SRV.wPort;
if (asprintf(&srv->host, "%s.", rr->Data.SRV.pNameTarget) < 0) {
free(srv);
goto cleanup;
}
TRACE_DNS_SRV_ANS(context, srv->host, srv->port, srv->priority,
srv->weight);
place_srv_entry(&head, srv);
}
cleanup:
free(name);
if (records != NULL)
DnsRecordListFree(records, DnsFreeRecordList);
*answers = head;
return 0;
}
#else
#include "dnsglue.h"
krb5_error_code
k5_make_uri_query(krb5_context context, const krb5_data *realm,
const char *service, const char *sitename,
struct srv_dns_entry **answers)
{
const unsigned char *p = NULL, *base = NULL;
char *name = NULL;
int size, ret, rdlen;
unsigned short priority, weight;
struct krb5int_dns_state *ds = NULL;
struct srv_dns_entry *head = NULL, *uri = NULL;
*answers = NULL;
name = make_lookup_name(realm, service, NULL, sitename);
if (name == NULL)
return 0;
TRACE_DNS_URI_SEND(context, name);
size = krb5int_dns_init(&ds, name, C_IN, T_URI);
if (size < 0 && sitename != NULL) {
free(name);
return k5_make_uri_query(context, realm, service, NULL, answers);
}
if (size < 0)
goto out;
for (;;) {
ret = krb5int_dns_nextans(ds, &base, &rdlen);
if (ret < 0 || base == NULL)
goto out;
p = base;
SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
uri = k5alloc(sizeof(*uri), &ret);
if (uri == NULL)
goto out;
uri->priority = priority;
uri->weight = weight;
uri->host = k5memdup0(p, rdlen - 4, &ret);
if (uri->host == NULL) {
free(uri);
goto out;
}
TRACE_DNS_URI_ANS(context, uri->host, uri->priority, uri->weight);
place_srv_entry(&head, uri);
}
out:
krb5int_dns_fini(ds);
free(name);
*answers = head;
return 0;
}
krb5_error_code
krb5int_make_srv_query_realm(krb5_context context, const krb5_data *realm,
const char *service, const char *protocol,
const char *sitename,
struct srv_dns_entry **answers)
{
const unsigned char *p = NULL, *base = NULL;
char *name = NULL, host[MAXDNAME];
int size, ret, rdlen, nlen;
unsigned short priority, weight, port;
struct krb5int_dns_state *ds = NULL;
struct srv_dns_entry *head = NULL, *srv = NULL;
name = make_lookup_name(realm, service, protocol, sitename);
if (name == NULL)
return 0;
TRACE_DNS_SRV_SEND(context, name);
size = krb5int_dns_init(&ds, name, C_IN, T_SRV);
if (size < 0 && sitename) {
free(name);
return krb5int_make_srv_query_realm(context, realm, service, protocol,
NULL, answers);
}
if (size < 0)
goto out;
for (;;) {
ret = krb5int_dns_nextans(ds, &base, &rdlen);
if (ret < 0 || base == NULL)
goto out;
p = base;
SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
SAFE_GETUINT16(base, rdlen, p, 2, port, out);
nlen = krb5int_dns_expand(ds, p, host, sizeof(host));
if (nlen < 0 || !INCR_OK(base, rdlen, p, nlen))
goto out;
srv = malloc(sizeof(struct srv_dns_entry));
if (srv == NULL)
goto out;
srv->priority = priority;
srv->weight = weight;
srv->port = port;
if (asprintf(&srv->host, "%s.", host) < 0) {
free(srv);
goto out;
}
TRACE_DNS_SRV_ANS(context, srv->host, srv->port, srv->priority,
srv->weight);
place_srv_entry(&head, srv);
}
out:
krb5int_dns_fini(ds);
free(name);
*answers = head;
return 0;
}
#endif
#endif