Working on libcldc's functionality requires moving all cldc_host searching and manipulation out from chunkd/tabled and into libcldc proper. Part of this involves changing the integration points related to SRV record lookups, so as to accomodate input from a file during testing (thus avoiding the need for a DNS server w/ SRV records during testsuite runs). While working on that, I updated a lot of zaitcev's code in cld/lib/cldc-dns.c into a libc-like function called getsrvinfo(). getsrvinfo() is intended as an analogue to getaddrinfo(3), for services based on SRV records (ours, many LDAP/Active Directory services, several VoIP/SIP services, exim and SMTP, ...). Maybe this will stay within Project Hail, maybe it will move into POSIX in ten years or so. But it was a fun mini-project within the scope of my libcldc work, so I am posting it here, even if hail-devel is perhaps not the best forum. Jeff #ifndef __GETSRVINFO_H__ #define __GETSRVINFO_H__ #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> struct srvinfo; enum srvinfo_error_codes { ESI_NONE = 0, /* no error */ ESI_CORRUPT = 1, /* server returned bad data */ ESI_FAIL = 2, /* server returned permanent failure */ ESI_AGAIN = 3, /* server returned temporary failure; * try again later.*/ ESI_OOM = 4, /* internal memory alloc failed */ ESI_INVAL = 5, /* invalid argument(s) */ }; enum srvinfo_flags { FSI_NO_ADDR = (1U << 0), /* skip host->addrs lookup */ }; struct srvinfo { unsigned int si_prio; /* SRV priority */ unsigned int si_weight; /* SRV weight */ char *si_target; /* SRV target domainname */ unsigned short si_port; /* SRV port */ struct addrinfo *si_addr; /* addresses returned * from getaddrinfo(3) lookup * on si_target */ struct srvinfo *si_next; /* next srvinfo in result list */ }; extern int getsrvinfo(const char *service_name, const char *domain_name, const struct addrinfo *hints, unsigned int flags, struct srvinfo **res); extern void freesrvinfo(struct srvinfo *res); extern const char *gsi_strerror(int errcode); #endif /* __GETSRVINFO_H__ */ /* * getsrvinfo.c * * Copyright 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <arpa/nameser.h> #include <netdb.h> #include <resolv.h> #include "getsrvinfo.h" #define __must_be_array(a) \ (__builtin_types_compatible_p(typeof(a), typeof(&a[0]))) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) enum { gsi_max_dom_name_sz = 64, gsi_dns_buf_sz = 1024, }; static int fill_srvinfo(struct srvinfo *si, const struct addrinfo *hints, unsigned int gsi_flags, unsigned int priority, unsigned int weight, unsigned int port, unsigned int nlen, const char *name) { char portstr[11]; char *hostname; struct addrinfo *res0 = NULL; int rc = 0; sprintf(portstr, "%u", port); hostname = malloc(nlen + 1); if (!hostname) { rc = -ENOMEM; goto err_name; } memcpy(hostname, name, nlen); hostname[nlen] = 0; if (!(gsi_flags & FSI_NO_ADDR)) { rc = getaddrinfo(hostname, portstr, hints, &res0); if (rc) { rc = -EINVAL; goto err_addr; } } si->si_prio = priority; si->si_weight = weight; si->si_target = hostname; si->si_port = port; si->si_addr = res0; si->si_next = NULL; return 0; err_addr: free(hostname); err_name: return rc; } void freesrvinfo(struct srvinfo *res) { while (res) { struct srvinfo *tmp; tmp = res; res = res->si_next; if (tmp->si_addr) freeaddrinfo(tmp->si_addr); free(tmp->si_target); free(tmp); } } int getsrvinfo(const char *service_name, const char *domain_name, const struct addrinfo *hints, unsigned int gsi_flags, struct srvinfo **res_out) { unsigned char resp[gsi_dns_buf_sz]; int rlen; ns_msg nsb; ns_rr rrb; int rrlen; char hostb[gsi_max_dom_name_sz]; struct srvinfo *si, *res = NULL, *res_last = NULL; const unsigned char *p; int rc, gsi_rc, i; if (!domain_name || !res_out) return ESI_INVAL; *res_out = NULL; /* we concatencate service_name and domain_name as a helpful * service for the caller, because it is very common * that service_name is either completely static, or at least * stored in a separate variable from domain_name. */ if (service_name) { char *name; size_t name_len; int has_dot; has_dot = (service_name[strlen(service_name) - 1] == '.'); name_len = strlen(service_name) + strlen(domain_name) + (has_dot ? 0 : 1) + 1; name = malloc(name_len); if (!name) return ESI_OOM; snprintf(name, name_len, "%s%s%s", service_name, has_dot ? "" : ".", domain_name); rc = res_search(name, ns_c_in, ns_t_srv, resp, sizeof(resp)); free(name); } else { rc = res_search(domain_name, ns_c_in, ns_t_srv, resp, sizeof(resp)); } /* parse resolver return value */ if (rc < 0) { switch (h_errno) { case TRY_AGAIN: return ESI_AGAIN; case HOST_NOT_FOUND: case NO_DATA: case NO_RECOVERY: default: return ESI_FAIL; } } rlen = rc; if (rlen == 0) return ESI_FAIL; /* set up DNS result parse */ if (ns_initparse(resp, rlen, &nsb) < 0) return ESI_CORRUPT; /* iterate through each answer. Because DNS packets may * be truncated, we do not signal an error on * short-length faults found during packet parsing */ for (i = 0; i < ns_msg_count(nsb, ns_s_an); i++) { rc = ns_parserr(&nsb, ns_s_an, i, &rrb); if (rc < 0) continue; if (ns_rr_class(rrb) != ns_c_in) continue; switch (ns_rr_type(rrb)) { case ns_t_srv: rrlen = ns_rr_rdlen(rrb); if (rrlen < 8) { /* 2+2+2 and 2 for host */ break; } p = ns_rr_rdata(rrb); rc = dn_expand(resp, resp+rlen, p+6, hostb, gsi_max_dom_name_sz); if (rc < 0) { break; } if (rc < 2) { break; } si = malloc(sizeof(*si)); if (!si) { gsi_rc = ESI_OOM; goto err_out; } if (fill_srvinfo(si, hints, gsi_flags, ns_get16(p+0), ns_get16(p+2), ns_get16(p+4), rc, hostb)) { free(si); gsi_rc = ESI_OOM; goto err_out; } /* if first item, set BOL-ptr */ if (!res) res = si; /* append to EOL */ if (res_last) res_last->si_next = si; res_last = si; break; case ns_t_cname: /* impossible, but ... ? */ default: break; } } *res_out = res; return ESI_NONE; err_out: freesrvinfo(res); return gsi_rc; } static const char *gsi_error_str[] = { [ESI_NONE] = "no error", [ESI_CORRUPT] = "server returned bad data", [ESI_FAIL] = "server returned permanent failure", [ESI_AGAIN] = "server returned temporary failure", [ESI_OOM] = "internal memory alloc failed", [ESI_INVAL] = "invalid argument(s)", }; const char *gsi_strerror(int errcode) { if (errcode < 0 || errcode >= ARRAY_SIZE(gsi_error_str)) return NULL; return gsi_error_str[errcode]; } -- To unsubscribe from this list: send the line "unsubscribe hail-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html