Blame SOURCES/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch

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