📄 krbhst.c
字号:
/* * Copyright (c) 2001 - 2003 Kungliga Tekniska H鰃skolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include "krb5_locl.h"#include <resolve.h>#include "locate_plugin.h"RCSID("$Id: krbhst.c 21457 2007-07-10 12:53:25Z lha $");static intstring_to_proto(const char *string){ if(strcasecmp(string, "udp") == 0) return KRB5_KRBHST_UDP; else if(strcasecmp(string, "tcp") == 0) return KRB5_KRBHST_TCP; else if(strcasecmp(string, "http") == 0) return KRB5_KRBHST_HTTP; return -1;}/* * set `res' and `count' to the result of looking up SRV RR in DNS for * `proto', `proto', `realm' using `dns_type'. * if `port' != 0, force that port number */static krb5_error_codesrv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count, const char *realm, const char *dns_type, const char *proto, const char *service, int port){ char domain[1024]; struct dns_reply *r; struct resource_record *rr; int num_srv; int proto_num; int def_port; *res = NULL; *count = 0; proto_num = string_to_proto(proto); if(proto_num < 0) { krb5_set_error_string(context, "unknown protocol `%s'", proto); return EINVAL; } if(proto_num == KRB5_KRBHST_HTTP) def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); else if(port == 0) def_port = ntohs(krb5_getportbyname (context, service, proto, 88)); else def_port = port; snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm); r = dns_lookup(domain, dns_type); if(r == NULL) return KRB5_KDC_UNREACH; for(num_srv = 0, rr = r->head; rr; rr = rr->next) if(rr->type == T_SRV) num_srv++; *res = malloc(num_srv * sizeof(**res)); if(*res == NULL) { dns_free_data(r); krb5_set_error_string(context, "malloc: out of memory"); return ENOMEM; } dns_srv_order(r); for(num_srv = 0, rr = r->head; rr; rr = rr->next) if(rr->type == T_SRV) { krb5_krbhst_info *hi; size_t len = strlen(rr->u.srv->target); hi = calloc(1, sizeof(*hi) + len); if(hi == NULL) { dns_free_data(r); while(--num_srv >= 0) free((*res)[num_srv]); free(*res); *res = NULL; return ENOMEM; } (*res)[num_srv++] = hi; hi->proto = proto_num; hi->def_port = def_port; if (port != 0) hi->port = port; else hi->port = rr->u.srv->port; strlcpy(hi->hostname, rr->u.srv->target, len + 1); } *count = num_srv; dns_free_data(r); return 0;}struct krb5_krbhst_data { char *realm; unsigned int flags; int def_port; int port; /* hardwired port number if != 0 */#define KD_CONFIG 1#define KD_SRV_UDP 2#define KD_SRV_TCP 4#define KD_SRV_HTTP 8#define KD_FALLBACK 16#define KD_CONFIG_EXISTS 32#define KD_LARGE_MSG 64#define KD_PLUGIN 128 krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *, krb5_krbhst_info**); unsigned int fallback_count; struct krb5_krbhst_info *hosts, **index, **end;};static krb5_booleankrbhst_empty(const struct krb5_krbhst_data *kd){ return kd->index == &kd->hosts;}/* * Return the default protocol for the `kd' (either TCP or UDP) */static intkrbhst_get_default_proto(struct krb5_krbhst_data *kd){ if (kd->flags & KD_LARGE_MSG) return KRB5_KRBHST_TCP; return KRB5_KRBHST_UDP;}/* * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port' * and forcing it to `port' if port != 0 */static struct krb5_krbhst_info*parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd, const char *spec, int def_port, int port){ const char *p = spec; struct krb5_krbhst_info *hi; hi = calloc(1, sizeof(*hi) + strlen(spec)); if(hi == NULL) return NULL; hi->proto = krbhst_get_default_proto(kd); if(strncmp(p, "http://", 7) == 0){ hi->proto = KRB5_KRBHST_HTTP; p += 7; } else if(strncmp(p, "http/", 5) == 0) { hi->proto = KRB5_KRBHST_HTTP; p += 5; def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); }else if(strncmp(p, "tcp/", 4) == 0){ hi->proto = KRB5_KRBHST_TCP; p += 4; } else if(strncmp(p, "udp/", 4) == 0) { p += 4; } if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) { free(hi); return NULL; } /* get rid of trailing /, and convert to lower case */ hi->hostname[strcspn(hi->hostname, "/")] = '\0'; strlwr(hi->hostname); hi->port = hi->def_port = def_port; if(p != NULL) { char *end; hi->port = strtol(p, &end, 0); if(end == p) { free(hi); return NULL; } } if (port) hi->port = port; return hi;}void_krb5_free_krbhst_info(krb5_krbhst_info *hi){ if (hi->ai != NULL) freeaddrinfo(hi->ai); free(hi);}krb5_error_code_krb5_krbhost_info_move(krb5_context context, krb5_krbhst_info *from, krb5_krbhst_info **to){ size_t hostnamelen = strlen(from->hostname); /* trailing NUL is included in structure */ *to = calloc(1, sizeof(**to) + hostnamelen); if(*to == NULL) { krb5_set_error_string(context, "malloc - out of memory"); return ENOMEM; } (*to)->proto = from->proto; (*to)->port = from->port; (*to)->def_port = from->def_port; (*to)->ai = from->ai; from->ai = NULL; (*to)->next = NULL; memcpy((*to)->hostname, from->hostname, hostnamelen + 1); return 0;}static voidappend_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host){ struct krb5_krbhst_info *h; for(h = kd->hosts; h; h = h->next) if(h->proto == host->proto && h->port == host->port && strcmp(h->hostname, host->hostname) == 0) { _krb5_free_krbhst_info(host); return; } *kd->end = host; kd->end = &host->next;}static krb5_error_codeappend_host_string(krb5_context context, struct krb5_krbhst_data *kd, const char *host, int def_port, int port){ struct krb5_krbhst_info *hi; hi = parse_hostspec(context, kd, host, def_port, port); if(hi == NULL) return ENOMEM; append_host_hostinfo(kd, hi); return 0;}/* * return a readable representation of `host' in `hostname, hostlen' */krb5_error_code KRB5_LIB_FUNCTIONkrb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host, char *hostname, size_t hostlen){ const char *proto = ""; char portstr[7] = ""; if(host->proto == KRB5_KRBHST_TCP) proto = "tcp/"; else if(host->proto == KRB5_KRBHST_HTTP) proto = "http://"; if(host->port != host->def_port) snprintf(portstr, sizeof(portstr), ":%d", host->port); snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr); return 0;}/* * create a getaddrinfo `hints' based on `proto' */static voidmake_hints(struct addrinfo *hints, int proto){ memset(hints, 0, sizeof(*hints)); hints->ai_family = AF_UNSPEC; switch(proto) { case KRB5_KRBHST_UDP : hints->ai_socktype = SOCK_DGRAM; break; case KRB5_KRBHST_HTTP : case KRB5_KRBHST_TCP : hints->ai_socktype = SOCK_STREAM; break; }}/* * return an `struct addrinfo *' in `ai' corresponding to the information * in `host'. free:ing is handled by krb5_krbhst_free. */krb5_error_code KRB5_LIB_FUNCTIONkrb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host, struct addrinfo **ai){ struct addrinfo hints; char portstr[NI_MAXSERV]; int ret; if (host->ai == NULL) { make_hints(&hints, host->proto); snprintf (portstr, sizeof(portstr), "%d", host->port); ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai); if (ret) return krb5_eai_to_heim_errno(ret, errno); } *ai = host->ai; return 0;}static krb5_booleanget_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host){ struct krb5_krbhst_info *hi = *kd->index; if(hi != NULL) { *host = hi; kd->index = &(*kd->index)->next; return TRUE; } return FALSE;}static voidsrv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, const char *proto, const char *service){ krb5_krbhst_info **res; int count, i; if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service, kd->port)) return; for(i = 0; i < count; i++) append_host_hostinfo(kd, res[i]); free(res);}/* * read the configuration for `conf_string', defaulting to kd->def_port and * forcing it to `kd->port' if kd->port != 0 */static voidconfig_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, const char *conf_string){ int i; char **hostlist; hostlist = krb5_config_get_strings(context, NULL, "realms", kd->realm, conf_string, NULL); if(hostlist == NULL) return; kd->flags |= KD_CONFIG_EXISTS; for(i = 0; hostlist && hostlist[i] != NULL; i++) append_host_string(context, kd, hostlist[i], kd->def_port, kd->port); krb5_config_free_strings(hostlist);}/* * as a fallback, look for `serv_string.kd->realm' (typically * kerberos.REALM, kerberos-1.REALM, ... * `port' is the default port for the service, and `proto' the * protocol */static krb5_error_codefallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, const char *serv_string, int port, int proto){ char *host; int ret; struct addrinfo *ai; struct addrinfo hints; char portstr[NI_MAXSERV]; /* * Don't try forever in case the DNS server keep returning us * entries (like wildcard entries or the .nu TLD) */ if(kd->fallback_count >= 5) { kd->flags |= KD_FALLBACK; return 0; } if(kd->fallback_count == 0) asprintf(&host, "%s.%s.", serv_string, kd->realm); else asprintf(&host, "%s-%d.%s.", serv_string, kd->fallback_count, kd->realm); if (host == NULL) return ENOMEM; make_hints(&hints, proto); snprintf(portstr, sizeof(portstr), "%d", port); ret = getaddrinfo(host, portstr, &hints, &ai); if (ret) { /* no more hosts, so we're done here */ free(host); kd->flags |= KD_FALLBACK; } else { struct krb5_krbhst_info *hi; size_t hostlen = strlen(host); hi = calloc(1, sizeof(*hi) + hostlen); if(hi == NULL) { free(host); return ENOMEM; } hi->proto = proto; hi->port = hi->def_port = port; hi->ai = ai; memmove(hi->hostname, host, hostlen); hi->hostname[hostlen] = '\0'; free(host); append_host_hostinfo(kd, hi); kd->fallback_count++; } return 0;}/* * Fetch hosts from plugin */static krb5_error_code add_locate(void *ctx, int type, struct sockaddr *addr){ struct krb5_krbhst_info *hi; struct krb5_krbhst_data *kd = ctx; char host[NI_MAXHOST], port[NI_MAXSERV]; struct addrinfo hints, *ai; socklen_t socklen; size_t hostlen; int ret; socklen = socket_sockaddr_size(addr); ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV); if (ret != 0) return 0; make_hints(&hints, krbhst_get_default_proto(kd)); ret = getaddrinfo(host, port, &hints, &ai); if (ret) return 0; hostlen = strlen(host); hi = calloc(1, sizeof(*hi) + hostlen); if(hi == NULL) return ENOMEM;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -