summaryrefslogtreecommitdiffstats
path: root/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch
diff options
context:
space:
mode:
Diffstat (limited to '0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch')
-rw-r--r--0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch913
1 files changed, 913 insertions, 0 deletions
diff --git a/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch b/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch
new file mode 100644
index 0000000..ba3f91c
--- /dev/null
+++ b/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch
@@ -0,0 +1,913 @@
+From 9c6be00daca0b80aed94ec9680724f95e6be92e1 Mon Sep 17 00:00:00 2001
+From: "Robbie Harwood (frozencemetery)" <rharwood@club.cc.cmu.edu>
+Date: Thu, 15 Aug 2013 15:55:52 -0400
+Subject: [PATCH 03/13] Use k5_transport(_strategy) enums for k5_sendto
+
+In k5_sendto and k5_locate_server, replace "socktype" parameters with
+a new enumerator k5_transport, so that we can add new transports which
+are not in the socket type namespace. Control the order in which we
+make connections of different types using a new k5_transport_strategy
+enumerator, to simplify the logic for adding new transports later.
+Control the result of k5_locate_server with a no_udp boolean rather
+than a socket type.
+
+[ghudson@mit.edu: renamed type to k5_transport; k5_locate_server
+ no_udp change; clarified commit message; fix for Solaris getaddrinfo]
+[kaduk@mit.edu: name variables of type k5_transport 'transport']
+[nalin@redhat.com: use transport rather than sock_type in more places,
+ add and use k5_transport_strategy, update the test program]
+
+ticket: 7929
+---
+ src/lib/krb5/os/changepw.c | 31 +++++-----
+ src/lib/krb5/os/hostrealm_domain.c | 2 +-
+ src/lib/krb5/os/locate_kdc.c | 75 ++++++++++++----------
+ src/lib/krb5/os/os-proto.h | 27 +++++---
+ src/lib/krb5/os/sendto_kdc.c | 123 +++++++++++++++++++++++--------------
+ src/lib/krb5/os/t_locate_kdc.c | 24 ++++----
+ src/lib/krb5/os/t_std_conf.c | 2 +-
+ src/lib/krb5/os/t_trace.c | 6 +-
+ src/lib/krb5/os/t_trace.ref | 4 +-
+ src/lib/krb5/os/trace.c | 6 +-
+ 10 files changed, 178 insertions(+), 122 deletions(-)
+
+diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
+index 4d8abd9..a1c9885 100644
+--- a/src/lib/krb5/os/changepw.c
++++ b/src/lib/krb5/os/changepw.c
+@@ -59,25 +59,25 @@ struct sendto_callback_context {
+
+ static krb5_error_code
+ locate_kpasswd(krb5_context context, const krb5_data *realm,
+- struct serverlist *serverlist, int socktype)
++ struct serverlist *serverlist, krb5_boolean no_udp)
+ {
+ krb5_error_code code;
+
+ code = k5_locate_server(context, realm, serverlist, locate_service_kpasswd,
+- socktype);
++ no_udp);
+
+ if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) {
+ code = k5_locate_server(context, realm, serverlist,
+- locate_service_kadmin, SOCK_STREAM);
++ locate_service_kadmin, TRUE);
+ if (!code) {
+- /* Success with admin_server but now we need to change the
+- port number to use DEFAULT_KPASSWD_PORT and the socktype. */
++ /* Success with admin_server but now we need to change the port
++ * number to use DEFAULT_KPASSWD_PORT and the transport. */
+ size_t i;
+ for (i = 0; i < serverlist->nservers; i++) {
+ struct server_entry *s = &serverlist->servers[i];
+ krb5_ui_2 kpasswd_port = htons(DEFAULT_KPASSWD_PORT);
+- if (socktype != SOCK_STREAM)
+- s->socktype = socktype;
++ if (!no_udp && s->transport == TCP)
++ s->transport = TCP_OR_UDP;
+ if (s->hostname != NULL)
+ s->port = kpasswd_port;
+ else if (s->family == AF_INET)
+@@ -214,7 +214,7 @@ change_set_password(krb5_context context,
+ krb5_data *result_string)
+ {
+ krb5_data chpw_rep;
+- krb5_boolean use_tcp = 0;
++ krb5_boolean no_udp = FALSE;
+ GETSOCKNAME_ARG3_TYPE addrlen;
+ krb5_error_code code = 0;
+ char *code_string;
+@@ -247,9 +247,10 @@ change_set_password(krb5_context context,
+ callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number;
+
+ do {
+- int socktype = (use_tcp ? SOCK_STREAM : SOCK_DGRAM);
++ k5_transport_strategy strategy = no_udp ? NO_UDP : UDP_FIRST;
++
+ code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl,
+- socktype);
++ no_udp);
+ if (code)
+ break;
+
+@@ -260,7 +261,7 @@ change_set_password(krb5_context context,
+ callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
+ krb5_free_data_contents(callback_ctx.context, &chpw_rep);
+
+- code = k5_sendto(callback_ctx.context, NULL, &sl, socktype, 0,
++ code = k5_sendto(callback_ctx.context, NULL, &sl, strategy,
+ &callback_info, &chpw_rep, ss2sa(&remote_addr),
+ &addrlen, NULL, NULL, NULL);
+ if (code) {
+@@ -277,9 +278,9 @@ change_set_password(krb5_context context,
+ result_string);
+
+ if (code) {
+- if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
++ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !no_udp) {
+ k5_free_serverlist(&sl);
+- use_tcp = 1;
++ no_udp = 1;
+ continue;
+ }
+
+@@ -305,9 +306,9 @@ change_set_password(krb5_context context,
+ strncpy(result_code_string->data, code_string, result_code_string->length);
+ }
+
+- if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
++ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !no_udp) {
+ k5_free_serverlist(&sl);
+- use_tcp = 1;
++ no_udp = 1;
+ } else {
+ break;
+ }
+diff --git a/src/lib/krb5/os/hostrealm_domain.c b/src/lib/krb5/os/hostrealm_domain.c
+index dc9cc59..2228df0 100644
+--- a/src/lib/krb5/os/hostrealm_domain.c
++++ b/src/lib/krb5/os/hostrealm_domain.c
+@@ -85,7 +85,7 @@ domain_fallback_realm(krb5_context context, krb5_hostrealm_moddata data,
+ suffix = uhost;
+ while (limit-- >= 0 && (dot = strchr(suffix, '.')) != NULL) {
+ drealm = string2data((char *)suffix);
+- if (k5_locate_kdc(context, &drealm, &slist, FALSE, SOCK_DGRAM) == 0) {
++ if (k5_locate_kdc(context, &drealm, &slist, FALSE, FALSE) == 0) {
+ k5_free_serverlist(&slist);
+ ret = k5_make_realmlist(suffix, realms_out);
+ goto cleanup;
+diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
+index 4479465..4c8aead 100644
+--- a/src/lib/krb5/os/locate_kdc.c
++++ b/src/lib/krb5/os/locate_kdc.c
+@@ -129,7 +129,7 @@ new_server_entry(struct serverlist *list)
+
+ /* Add an address entry to list. */
+ static int
+-add_addr_to_list(struct serverlist *list, int socktype, int family,
++add_addr_to_list(struct serverlist *list, k5_transport transport, int family,
+ size_t addrlen, struct sockaddr *addr)
+ {
+ struct server_entry *entry;
+@@ -137,7 +137,7 @@ add_addr_to_list(struct serverlist *list, int socktype, int family,
+ entry = new_server_entry(list);
+ if (entry == NULL)
+ return ENOMEM;
+- entry->socktype = socktype;
++ entry->transport = transport;
+ entry->family = family;
+ entry->hostname = NULL;
+ entry->addrlen = addrlen;
+@@ -149,14 +149,14 @@ add_addr_to_list(struct serverlist *list, int socktype, int family,
+ /* Add a hostname entry to list. */
+ static int
+ add_host_to_list(struct serverlist *list, const char *hostname, int port,
+- int socktype, int family)
++ k5_transport transport, int family)
+ {
+ struct server_entry *entry;
+
+ entry = new_server_entry(list);
+ if (entry == NULL)
+ return ENOMEM;
+- entry->socktype = socktype;
++ entry->transport = transport;
+ entry->family = family;
+ entry->hostname = strdup(hostname);
+ if (entry->hostname == NULL)
+@@ -187,7 +187,7 @@ server_list_contains(struct serverlist *list, struct server_entry *server)
+ static krb5_error_code
+ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+ const char * name, struct serverlist *serverlist,
+- int socktype, int udpport, int sec_udpport)
++ k5_transport transport, int udpport, int sec_udpport)
+ {
+ const char *realm_srv_names[4];
+ char **hostlist, *host, *port, *cp;
+@@ -255,12 +255,12 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+ *cp = '\0';
+ }
+
+- code = add_host_to_list(serverlist, host, p1, socktype, AF_UNSPEC);
++ code = add_host_to_list(serverlist, host, p1, transport, AF_UNSPEC);
+ /* Second port is for IPv4 UDP only, and should possibly go away as
+ * it was originally a krb4 compatibility measure. */
+ if (code == 0 && p2 != 0 &&
+- (socktype == 0 || socktype == SOCK_DGRAM))
+- code = add_host_to_list(serverlist, host, p2, SOCK_DGRAM, AF_INET);
++ (transport == TCP_OR_UDP || transport == UDP))
++ code = add_host_to_list(serverlist, host, p2, UDP, AF_INET);
+ if (code)
+ goto cleanup;
+ }
+@@ -278,7 +278,8 @@ krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
+ {
+ krb5_error_code ret;
+
+- ret = locate_srv_conf_1(context, realm, name, al, 0, udpport, sec_udpport);
++ ret = locate_srv_conf_1(context, realm, name, al, TCP_OR_UDP, udpport,
++ sec_udpport);
+ if (ret)
+ return ret;
+ if (al->nservers == 0) /* Couldn't resolve any KDC names */
+@@ -294,7 +295,7 @@ locate_srv_dns_1(const krb5_data *realm, const char *service,
+ {
+ struct srv_dns_entry *head = NULL, *entry = NULL;
+ krb5_error_code code = 0;
+- int socktype;
++ k5_transport transport;
+
+ code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
+ if (code)
+@@ -310,9 +311,9 @@ locate_srv_dns_1(const krb5_data *realm, const char *service,
+ }
+
+ for (entry = head; entry != NULL; entry = entry->next) {
+- socktype = (strcmp(protocol, "_tcp") == 0) ? SOCK_STREAM : SOCK_DGRAM;
++ transport = (strcmp(protocol, "_tcp") == 0) ? TCP : UDP;
+ code = add_host_to_list(serverlist, entry->host, htons(entry->port),
+- socktype, AF_UNSPEC);
++ transport, AF_UNSPEC);
+ if (code)
+ goto cleanup;
+ }
+@@ -341,6 +342,7 @@ module_callback(void *cbdata, int socktype, struct sockaddr *sa)
+ {
+ struct module_callback_data *d = cbdata;
+ size_t addrlen;
++ k5_transport transport;
+
+ if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
+ return 0;
+@@ -350,7 +352,8 @@ module_callback(void *cbdata, int socktype, struct sockaddr *sa)
+ addrlen = sizeof(struct sockaddr_in6);
+ else
+ return 0;
+- if (add_addr_to_list(d->list, socktype, sa->sa_family, addrlen,
++ transport = (socktype == SOCK_STREAM) ? TCP : UDP;
++ if (add_addr_to_list(d->list, transport, sa->sa_family, addrlen,
+ sa) != 0) {
+ /* Assumes only error is ENOMEM. */
+ d->out_of_mem = 1;
+@@ -362,14 +365,14 @@ module_callback(void *cbdata, int socktype, struct sockaddr *sa)
+ static krb5_error_code
+ module_locate_server(krb5_context ctx, const krb5_data *realm,
+ struct serverlist *serverlist,
+- enum locate_service_type svc, int socktype)
++ enum locate_service_type svc, k5_transport transport)
+ {
+ struct krb5plugin_service_locate_result *res = NULL;
+ krb5_error_code code;
+ struct krb5plugin_service_locate_ftable *vtbl = NULL;
+ void **ptrs;
+ char *realmz; /* NUL-terminated realm */
+- int i;
++ int socktype, i;
+ struct module_callback_data cbdata = { 0, };
+ const char *msg;
+
+@@ -413,11 +416,11 @@ module_locate_server(krb5_context ctx, const krb5_data *realm,
+ if (code)
+ continue;
+
+- code = vtbl->lookup(blob, svc, realmz,
+- (socktype != 0) ? socktype : SOCK_DGRAM, AF_UNSPEC,
++ socktype = (transport == TCP) ? SOCK_STREAM : SOCK_DGRAM;
++ code = vtbl->lookup(blob, svc, realmz, socktype, AF_UNSPEC,
+ module_callback, &cbdata);
+ /* Also ask for TCP addresses if we got UDP addresses and want both. */
+- if (code == 0 && socktype == 0) {
++ if (code == 0 && transport == TCP_OR_UDP) {
+ code = vtbl->lookup(blob, svc, realmz, SOCK_STREAM, AF_UNSPEC,
+ module_callback, &cbdata);
+ if (code == KRB5_PLUGIN_NO_HANDLE)
+@@ -459,7 +462,7 @@ module_locate_server(krb5_context ctx, const krb5_data *realm,
+ static krb5_error_code
+ prof_locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ k5_transport transport)
+ {
+ const char *profname;
+ int dflport1, dflport2 = 0;
+@@ -495,7 +498,7 @@ prof_locate_server(krb5_context context, const krb5_data *realm,
+ return EBUSY; /* XXX */
+ }
+
+- return locate_srv_conf_1(context, realm, profname, serverlist, socktype,
++ return locate_srv_conf_1(context, realm, profname, serverlist, transport,
+ dflport1, dflport2);
+ }
+
+@@ -503,7 +506,7 @@ prof_locate_server(krb5_context context, const krb5_data *realm,
+ static krb5_error_code
+ dns_locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ k5_transport transport)
+ {
+ const char *dnsname;
+ int use_dns = _krb5_use_dns_kdc(context);
+@@ -533,12 +536,12 @@ dns_locate_server(krb5_context context, const krb5_data *realm,
+ }
+
+ code = 0;
+- if (socktype == SOCK_DGRAM || socktype == 0) {
++ if (transport == UDP || transport == TCP_OR_UDP) {
+ code = locate_srv_dns_1(realm, dnsname, "_udp", serverlist);
+ if (code)
+ Tprintf("dns udp lookup returned error %d\n", code);
+ }
+- if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
++ if ((transport == TCP || transport == TCP_OR_UDP) && code == 0) {
+ code = locate_srv_dns_1(realm, dnsname, "_tcp", serverlist);
+ if (code)
+ Tprintf("dns tcp lookup returned error %d\n", code);
+@@ -547,10 +550,16 @@ dns_locate_server(krb5_context context, const krb5_data *realm,
+ }
+ #endif /* KRB5_DNS_LOOKUP */
+
++/*
++ * Try all of the server location methods in sequence. transport must be
++ * TCP_OR_UDP, TCP, or UDP. It is applied to hostname entries in the profile
++ * and affects whether we query modules or DNS for UDP or TCP or both, but does
++ * not restrict a method from returning entries of other transports.
++ */
+ static krb5_error_code
+ locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ k5_transport transport)
+ {
+ krb5_error_code ret;
+ struct serverlist list = SERVERLIST_INIT;
+@@ -559,18 +568,18 @@ locate_server(krb5_context context, const krb5_data *realm,
+
+ /* Try modules. If a module returns 0 but leaves the list empty, return an
+ * empty list. */
+- ret = module_locate_server(context, realm, &list, svc, socktype);
++ ret = module_locate_server(context, realm, &list, svc, transport);
+ if (ret != KRB5_PLUGIN_NO_HANDLE)
+ goto done;
+
+ /* Try the profile. Fall back to DNS if it returns an empty list. */
+- ret = prof_locate_server(context, realm, &list, svc, socktype);
++ ret = prof_locate_server(context, realm, &list, svc, transport);
+ if (ret)
+ goto done;
+
+ #ifdef KRB5_DNS_LOOKUP
+ if (list.nservers == 0)
+- ret = dns_locate_server(context, realm, &list, svc, socktype);
++ ret = dns_locate_server(context, realm, &list, svc, transport);
+ #endif
+
+ done:
+@@ -589,9 +598,10 @@ done:
+ krb5_error_code
+ k5_locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ krb5_boolean no_udp)
+ {
+ krb5_error_code ret;
++ k5_transport transport = no_udp ? TCP : TCP_OR_UDP;
+
+ memset(serverlist, 0, sizeof(*serverlist));
+ if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
+@@ -600,7 +610,7 @@ k5_locate_server(krb5_context context, const krb5_data *realm,
+ return KRB5_REALM_CANT_RESOLVE;
+ }
+
+- ret = locate_server(context, realm, serverlist, svc, socktype);
++ ret = locate_server(context, realm, serverlist, svc, transport);
+ if (ret)
+ return ret;
+
+@@ -616,12 +626,13 @@ k5_locate_server(krb5_context context, const krb5_data *realm,
+
+ krb5_error_code
+ k5_locate_kdc(krb5_context context, const krb5_data *realm,
+- struct serverlist *serverlist, int get_masters, int socktype)
++ struct serverlist *serverlist, krb5_boolean get_masters,
++ krb5_boolean no_udp)
+ {
+ enum locate_service_type stype;
+
+ stype = get_masters ? locate_service_master_kdc : locate_service_kdc;
+- return k5_locate_server(context, realm, serverlist, stype, socktype);
++ return k5_locate_server(context, realm, serverlist, stype, no_udp);
+ }
+
+ krb5_boolean
+@@ -632,7 +643,7 @@ k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+ krb5_boolean found;
+
+ if (locate_server(context, realm, &list, locate_service_master_kdc,
+- server->socktype) != 0)
++ server->transport) != 0)
+ return FALSE;
+ found = server_list_contains(&list, server);
+ k5_free_serverlist(&list);
+diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
+index 9125ba0..3196bca 100644
+--- a/src/lib/krb5/os/os-proto.h
++++ b/src/lib/krb5/os/os-proto.h
+@@ -38,11 +38,23 @@
+
+ #include <krb5/locate_plugin.h>
+
++typedef enum {
++ TCP_OR_UDP = 0,
++ TCP,
++ UDP,
++} k5_transport;
++
++typedef enum {
++ UDP_FIRST = 0,
++ UDP_LAST,
++ NO_UDP,
++} k5_transport_strategy;
++
+ /* A single server hostname or address. */
+ struct server_entry {
+ char *hostname; /* NULL -> use addrlen/addr instead */
+ int port; /* Used only if hostname set */
+- int socktype; /* May be 0 for UDP/TCP if hostname set */
++ k5_transport transport; /* May be 0 for UDP/TCP if hostname set */
+ int family; /* May be 0 (aka AF_UNSPEC) if hostname set */
+ size_t addrlen;
+ struct sockaddr_storage addr;
+@@ -56,8 +68,8 @@ struct serverlist {
+ #define SERVERLIST_INIT { NULL, 0 }
+
+ struct remote_address {
++ k5_transport transport;
+ int family;
+- int type;
+ socklen_t len;
+ struct sockaddr_storage saddr;
+ };
+@@ -69,12 +81,13 @@ struct sendto_callback_info {
+ };
+
+ krb5_error_code k5_locate_server(krb5_context, const krb5_data *realm,
+- struct serverlist *,
+- enum locate_service_type svc, int socktype);
++ struct serverlist *serverlist,
++ enum locate_service_type svc,
++ krb5_boolean no_udp);
+
+ krb5_error_code k5_locate_kdc(krb5_context context, const krb5_data *realm,
+- struct serverlist *serverlist, int get_masters,
+- int socktype);
++ struct serverlist *serverlist,
++ krb5_boolean get_masters, krb5_boolean no_udp);
+
+ krb5_boolean k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+ struct server_entry *server);
+@@ -103,7 +116,7 @@ int _krb5_conf_boolean (const char *);
+
+ krb5_error_code k5_sendto(krb5_context context, const krb5_data *message,
+ const struct serverlist *addrs,
+- int socktype1, int socktype2,
++ k5_transport_strategy strategy,
+ struct sendto_callback_info *callback_info,
+ krb5_data *reply, struct sockaddr *remoteaddr,
+ socklen_t *remoteaddrlen, int *server_used,
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index e3855a3..3f99ce8 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -104,6 +104,7 @@ struct conn_state {
+ size_t server_index;
+ struct conn_state *next;
+ time_ms endtime;
++ krb5_boolean defer;
+ };
+
+ /* Get current time in milliseconds. */
+@@ -293,6 +294,19 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
+ }
+
+ static int
++socktype_for_transport(k5_transport transport)
++{
++ switch (transport) {
++ case UDP:
++ return SOCK_DGRAM;
++ case TCP:
++ return SOCK_STREAM;
++ default:
++ return 0;
++ }
++}
++
++static int
+ check_for_svc_unavailable (krb5_context context,
+ const krb5_data *reply,
+ void *msg_handler_data)
+@@ -330,11 +344,12 @@ check_for_svc_unavailable (krb5_context context,
+ krb5_error_code
+ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ const krb5_data *realm, krb5_data *reply, int *use_master,
+- int tcp_only)
++ int no_udp)
+ {
+ krb5_error_code retval, err;
+ struct serverlist servers;
+- int socktype1 = 0, socktype2 = 0, server_used;
++ int server_used;
++ k5_transport_strategy strategy;
+
+ /*
+ * find KDC location(s) for realm
+@@ -349,9 +364,9 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ * should probably be returned as well.
+ */
+
+- TRACE_SENDTO_KDC(context, message->length, realm, *use_master, tcp_only);
++ TRACE_SENDTO_KDC(context, message->length, realm, *use_master, no_udp);
+
+- if (!tcp_only && context->udp_pref_limit < 0) {
++ if (!no_udp && context->udp_pref_limit < 0) {
+ int tmp;
+ retval = profile_get_integer(context->profile,
+ KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0,
+@@ -368,22 +383,21 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ context->udp_pref_limit = tmp;
+ }
+
+- if (tcp_only)
+- socktype1 = SOCK_STREAM, socktype2 = 0;
++ if (no_udp)
++ strategy = NO_UDP;
+ else if (message->length <= (unsigned int) context->udp_pref_limit)
+- socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
++ strategy = UDP_FIRST;
+ else
+- socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
++ strategy = UDP_LAST;
+
+- retval = k5_locate_kdc(context, realm, &servers, *use_master,
+- tcp_only ? SOCK_STREAM : 0);
++ retval = k5_locate_kdc(context, realm, &servers, *use_master, no_udp);
+ if (retval)
+ return retval;
+
+ err = 0;
+- retval = k5_sendto(context, message, &servers, socktype1, socktype2,
+- NULL, reply, NULL, NULL, &server_used,
+- check_for_svc_unavailable, &err);
++ retval = k5_sendto(context, message, &servers, strategy, NULL, reply,
++ NULL, NULL, &server_used, check_for_svc_unavailable,
++ &err);
+ if (retval == KRB5_KDC_UNREACH) {
+ if (err == KDC_ERR_SVC_UNAVAILABLE) {
+ retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
+@@ -444,7 +458,7 @@ set_transport_message(struct conn_state *state, const krb5_data *message)
+ if (message == NULL || message->length == 0)
+ return;
+
+- if (state->addr.type == SOCK_STREAM) {
++ if (state->addr.transport == TCP) {
+ store_32_be(message->length, out->msg_len_buf);
+ SG_SET(&out->sgbuf[0], out->msg_len_buf, 4);
+ SG_SET(&out->sgbuf[1], message->data, message->length);
+@@ -457,8 +471,9 @@ set_transport_message(struct conn_state *state, const krb5_data *message)
+ }
+
+ static krb5_error_code
+-add_connection(struct conn_state **conns, struct addrinfo *ai,
+- size_t server_index, char **udpbufp)
++add_connection(struct conn_state **conns, k5_transport transport,
++ krb5_boolean defer, struct addrinfo *ai, size_t server_index,
++ char **udpbufp)
+ {
+ struct conn_state *state, **tailptr;
+
+@@ -467,14 +482,15 @@ add_connection(struct conn_state **conns, struct addrinfo *ai,
+ return ENOMEM;
+ state->state = INITIALIZING;
+ state->out.sgp = state->out.sgbuf;
+- state->addr.type = ai->ai_socktype;
++ state->addr.transport = transport;
+ state->addr.family = ai->ai_family;
+ state->addr.len = ai->ai_addrlen;
+ memcpy(&state->addr.saddr, ai->ai_addr, ai->ai_addrlen);
++ state->defer = defer;
+ state->fd = INVALID_SOCKET;
+ state->server_index = server_index;
+ SG_SET(&state->out.sgbuf[1], NULL, 0);
+- if (ai->ai_socktype == SOCK_STREAM) {
++ if (transport == TCP) {
+ state->service = service_tcp_fd;
+ } else {
+ state->service = service_udp_fd;
+@@ -549,32 +565,41 @@ translate_ai_error (int err)
+ */
+ static krb5_error_code
+ resolve_server(krb5_context context, const struct serverlist *servers,
+- size_t ind, int socktype1, int socktype2,
++ size_t ind, k5_transport_strategy strategy,
+ const krb5_data *message, char **udpbufp,
+ struct conn_state **conns)
+ {
+ krb5_error_code retval;
+ struct server_entry *entry = &servers->servers[ind];
++ k5_transport transport;
+ struct addrinfo *addrs, *a, hint, ai;
++ krb5_boolean defer;
+ int err, result;
+ char portbuf[64];
+
+- /* Skip any stray entries of socktypes we don't want. */
+- if (entry->socktype != 0 && entry->socktype != socktype1 &&
+- entry->socktype != socktype2)
++ /* Skip UDP entries if we don't want UDP. */
++ if (strategy == NO_UDP && entry->transport == UDP)
+ return 0;
+
++ transport = (strategy == UDP_FIRST) ? UDP : TCP;
+ if (entry->hostname == NULL) {
+- ai.ai_socktype = entry->socktype;
++ /* Added by a module, so transport is either TCP or UDP. */
++ ai.ai_socktype = socktype_for_transport(entry->transport);
+ ai.ai_family = entry->family;
+ ai.ai_addrlen = entry->addrlen;
+ ai.ai_addr = (struct sockaddr *)&entry->addr;
+- return add_connection(conns, &ai, ind, udpbufp);
++ defer = (entry->transport != transport);
++ return add_connection(conns, entry->transport, defer, &ai, ind,
++ udpbufp);
+ }
+
++ /* If the entry has a specified transport, use it. */
++ if (entry->transport != TCP_OR_UDP)
++ transport = entry->transport;
++
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_family = entry->family;
+- hint.ai_socktype = (entry->socktype != 0) ? entry->socktype : socktype1;
++ hint.ai_socktype = socktype_for_transport(transport);
+ hint.ai_flags = AI_ADDRCONFIG;
+ #ifdef AI_NUMERICSERV
+ hint.ai_flags |= AI_NUMERICSERV;
+@@ -586,15 +611,19 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+ err = getaddrinfo(entry->hostname, portbuf, &hint, &addrs);
+ if (err)
+ return translate_ai_error(err);
+- /* Add each address with the preferred socktype. */
++
++ /* Add each address with the specified or preferred transport. */
+ retval = 0;
+ for (a = addrs; a != 0 && retval == 0; a = a->ai_next)
+- retval = add_connection(conns, a, ind, udpbufp);
+- if (retval == 0 && entry->socktype == 0 && socktype2 != 0) {
+- /* Add each address again with the non-preferred socktype. */
++ retval = add_connection(conns, transport, FALSE, a, ind, udpbufp);
++
++ /* For TCP_OR_UDP entries, add each address again with the non-preferred
++ * transport, unless we are avoiding UDP. Flag these as deferred. */
++ if (retval == 0 && entry->transport == TCP_OR_UDP && strategy != NO_UDP) {
++ transport = (strategy == UDP_FIRST) ? TCP : UDP;
+ for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
+- a->ai_socktype = socktype2;
+- retval = add_connection(conns, a, ind, udpbufp);
++ a->ai_socktype = socktype_for_transport(transport);
++ retval = add_connection(conns, transport, TRUE, a, ind, udpbufp);
+ }
+ }
+ freeaddrinfo(addrs);
+@@ -606,17 +635,18 @@ start_connection(krb5_context context, struct conn_state *state,
+ const krb5_data *message, struct select_state *selstate,
+ struct sendto_callback_info *callback_info)
+ {
+- int fd, e;
++ int fd, e, type;
+ static const int one = 1;
+ static const struct linger lopt = { 0, 0 };
+
+- fd = socket(state->addr.family, state->addr.type, 0);
++ type = socktype_for_transport(state->addr.transport);
++ fd = socket(state->addr.family, type, 0);
+ if (fd == INVALID_SOCKET)
+ return -1; /* try other hosts */
+ set_cloexec_fd(fd);
+ /* Make it non-blocking. */
+ ioctlsocket(fd, FIONBIO, (const void *) &one);
+- if (state->addr.type == SOCK_STREAM) {
++ if (state->addr.transport == TCP) {
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));
+ TRACE_SENDTO_KDC_TCP_CONNECT(context, &state->addr);
+ }
+@@ -665,7 +695,7 @@ start_connection(krb5_context context, struct conn_state *state,
+ }
+ set_transport_message(state, message);
+
+- if (state->addr.type == SOCK_DGRAM) {
++ if (state->addr.transport == UDP) {
+ /* Send it now. */
+ ssize_t ret;
+ sg_buf *sg = &state->out.sgbuf[0];
+@@ -720,7 +750,7 @@ maybe_send(krb5_context context, struct conn_state *conn,
+ return -1;
+ }
+
+- if (conn->addr.type == SOCK_STREAM) {
++ if (conn->addr.transport != UDP) {
+ /* The select callback will handle flushing any data we
+ haven't written yet, and we only write it once. */
+ return -1;
+@@ -910,7 +940,7 @@ get_endtime(time_ms endtime, struct conn_state *conns)
+ struct conn_state *state;
+
+ for (state = conns; state != NULL; state = state->next) {
+- if (state->addr.type == SOCK_STREAM &&
++ if (state->addr.transport == TCP &&
+ (state->state == READING || state->state == WRITING) &&
+ state->endtime > endtime)
+ endtime = state->endtime;
+@@ -1008,7 +1038,7 @@ service_fds(krb5_context context, struct select_state *selstate,
+
+ krb5_error_code
+ k5_sendto(krb5_context context, const krb5_data *message,
+- const struct serverlist *servers, int socktype1, int socktype2,
++ const struct serverlist *servers, k5_transport_strategy strategy,
+ struct sendto_callback_info* callback_info, krb5_data *reply,
+ struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
+ int *server_used,
+@@ -1038,17 +1068,18 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ cm_init_selstate(sel_state);
+
+ /* First pass: resolve server hosts, communicate with resulting addresses
+- * of the preferred socktype, and wait 1s for an answer from each. */
++ * of the preferred transport, and wait 1s for an answer from each. */
+ for (s = 0; s < servers->nservers && !done; s++) {
+ /* Find the current tail pointer. */
+ for (tailptr = &conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
+- retval = resolve_server(context, servers, s, socktype1, socktype2,
+- message, &udpbuf, &conns);
++ retval = resolve_server(context, servers, s, strategy, message,
++ &udpbuf, &conns);
+ if (retval)
+ goto cleanup;
+ for (state = *tailptr; state != NULL && !done; state = state->next) {
+- /* Contact each new connection whose socktype matches socktype1. */
+- if (state->addr.type != socktype1)
++ /* Contact each new connection, deferring those which use the
++ * non-preferred RFC 4120 transport. */
++ if (state->defer)
+ continue;
+ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+@@ -1057,10 +1088,10 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ }
+ }
+
+- /* Complete the first pass by contacting servers of the non-preferred
+- * socktype (if given), waiting 1s for an answer from each. */
++ /* Complete the first pass by contacting servers of the non-preferred RFC
++ * 4120 transport (if given), waiting 1s for an answer from each. */
+ for (state = conns; state != NULL && !done; state = state->next) {
+- if (state->addr.type != socktype2)
++ if (!state->defer)
+ continue;
+ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+diff --git a/src/lib/krb5/os/t_locate_kdc.c b/src/lib/krb5/os/t_locate_kdc.c
+index 5453a4c..300aa71 100644
+--- a/src/lib/krb5/os/t_locate_kdc.c
++++ b/src/lib/krb5/os/t_locate_kdc.c
+@@ -29,18 +29,18 @@ kfatal (krb5_error_code err)
+ }
+
+ static const char *
+-stypename (int stype)
++ttypename (k5_transport ttype)
+ {
+ static char buf[20];
+- switch (stype) {
+- case SOCK_STREAM:
+- return "stream";
+- case SOCK_DGRAM:
+- return "dgram";
+- case SOCK_RAW:
+- return "raw";
++ switch (ttype) {
++ case TCP_OR_UDP:
++ return "tcp or udp";
++ case TCP:
++ return "tcp";
++ case UDP:
++ return "udp";
+ default:
+- snprintf(buf, sizeof(buf), "?%d", stype);
++ snprintf(buf, sizeof(buf), "?%d", ttype);
+ return buf;
+ }
+ }
+@@ -58,7 +58,7 @@ print_addrs (void)
+
+ if (entry->hostname != NULL) {
+ printf("%2d: host %s\t%s\tport %d\n", (int)i, entry->hostname,
+- stypename(entry->socktype), ntohs(entry->port));
++ ttypename(entry->transport), ntohs(entry->port));
+ continue;
+ }
+ err = getnameinfo((struct sockaddr *)&entry->addr, entry->addrlen,
+@@ -69,7 +69,7 @@ print_addrs (void)
+ gai_strerror(err));
+ } else {
+ printf("%2d: address %s\t%s\tport %s\n", (int)i, hostbuf,
+- stypename(entry->socktype), srvbuf);
++ ttypename(entry->transport), srvbuf);
+ }
+ }
+ }
+@@ -129,7 +129,7 @@ main (int argc, char *argv[])
+ break;
+
+ case LOOKUP_WHATEVER:
+- err = k5_locate_kdc(ctx, &realm, &sl, master, 0);
++ err = k5_locate_kdc(ctx, &realm, &sl, master, FALSE);
+ break;
+ }
+ if (err) kfatal (err);
+diff --git a/src/lib/krb5/os/t_std_conf.c b/src/lib/krb5/os/t_std_conf.c
+index e2ff572..6ee54d5 100644
+--- a/src/lib/krb5/os/t_std_conf.c
++++ b/src/lib/krb5/os/t_std_conf.c
+@@ -82,7 +82,7 @@ test_locate_kdc(krb5_context ctx, char *realm)
+
+ rlm.data = realm;
+ rlm.length = strlen(realm);
+- retval = k5_locate_kdc(ctx, &rlm, &servers, get_masters, 0);
++ retval = k5_locate_kdc(ctx, &rlm, &servers, get_masters, FALSE);
+ if (retval) {
+ com_err("krb5_locate_kdc", retval, 0);
+ return;
+diff --git a/src/lib/krb5/os/t_trace.c b/src/lib/krb5/os/t_trace.c
+index 36044f5..4cb2bd0 100644
+--- a/src/lib/krb5/os/t_trace.c
++++ b/src/lib/krb5/os/t_trace.c
+@@ -112,7 +112,7 @@ main (int argc, char *argv[])
+ TRACE(ctx, "size_t and const char *, as four-character hex hash: "
+ "{hashlenstr}", 1, NULL);
+
+- ra.type = SOCK_STREAM;
++ ra.transport = TCP;
+ addr_in = (struct sockaddr_in *)&ra.saddr;
+ addr_in->sin_family = AF_INET;
+ addr_in->sin_addr.s_addr = INADDR_ANY;
+@@ -121,10 +121,10 @@ main (int argc, char *argv[])
+ ra.family = AF_INET;
+ TRACE(ctx, "struct remote_address *, show socket type, address, port: "
+ "{raddr}", &ra);
+- ra.type = SOCK_DGRAM;
++ ra.transport = UDP;
+ TRACE(ctx, "struct remote_address *, show socket type, address, port: "
+ "{raddr}", &ra);
+- ra.type = 1234;
++ ra.transport = 1234;
+ addr_in->sin_family = AF_UNSPEC;
+ ra.family = AF_UNSPEC;
+ TRACE(ctx, "struct remote_address *, show socket type, address, port: "
+diff --git a/src/lib/krb5/os/t_trace.ref b/src/lib/krb5/os/t_trace.ref
+index 749d9c9..ca5818a 100644
+--- a/src/lib/krb5/os/t_trace.ref
++++ b/src/lib/krb5/os/t_trace.ref
+@@ -10,8 +10,8 @@ size_t and const char *, as four-character hex hash: 7B9A
+ size_t and const char *, as four-character hex hash: (null)
+ struct remote_address *, show socket type, address, port: stream 0.0.0.0:88
+ struct remote_address *, show socket type, address, port: dgram 0.0.0.0:88
+-struct remote_address *, show socket type, address, port: socktype1234 AF_UNSPEC
+-struct remote_address *, show socket type, address, port: socktype1234 af5678
++struct remote_address *, show socket type, address, port: transport1234 AF_UNSPEC
++struct remote_address *, show socket type, address, port: transport1234 af5678
+ krb5_data *, display as counted string: example.data
+ krb5_data *, display as counted string: (null)
+ krb5_data *, display as hex bytes: 6578616D706C652E64617461
+diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
+index 525742c..8319a86 100644
+--- a/src/lib/krb5/os/trace.c
++++ b/src/lib/krb5/os/trace.c
+@@ -197,12 +197,12 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
+ }
+ } else if (strcmp(tmpbuf, "raddr") == 0) {
+ ra = va_arg(ap, struct remote_address *);
+- if (ra->type == SOCK_DGRAM)
++ if (ra->transport == UDP)
+ k5_buf_add(&buf, "dgram");
+- else if (ra->type == SOCK_STREAM)
++ else if (ra->transport == TCP)
+ k5_buf_add(&buf, "stream");
+ else
+- k5_buf_add_fmt(&buf, "socktype%d", ra->type);
++ k5_buf_add_fmt(&buf, "transport%d", ra->transport);
+
+ if (getnameinfo((struct sockaddr *)&ra->saddr, ra->len,
+ addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf),
+--
+2.1.0
+