pax_global_header00006660000000000000000000000064115717720170014522gustar00rootroot0000000000000052 comment=d32f1fddbf1bee2a030ba0ba5270d0878d746831 wrapsrv-0.2/000077500000000000000000000000001157177201700130675ustar00rootroot00000000000000wrapsrv-0.2/Makefile000066400000000000000000000006131157177201700145270ustar00rootroot00000000000000CC = gcc WARN = -Wall -Wextra -Werror CFLAGS = -O2 -g $(WARN) INCLUDE = LDFLAGS = -lresolv DESTDIR = /usr/local BIN = wrapsrv DOC = wrapsrv.1 SRC = wrapsrv.c all: $(BIN) $(DOC) $(BIN): $(SRC) $(CC) $(CFLAGS) -o $@ $(SRC) $(INCLUDE) $(LDFLAGS) $(DOC): wrapsrv.docbook docbook2x-man $< clean: rm -f $(BIN) install: $(BIN) install -m 0755 $(BIN) $(DESTDIR)/bin .PHONY: all clean install wrapsrv-0.2/list.h000066400000000000000000000131551157177201700142200ustar00rootroot00000000000000/* * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef ISC_LIST_H #define ISC_LIST_H 1 #define ISC_INSIST(x) assert(x) #define ISC_LINK_INSIST(x) assert(x) #define ISC_LIST(type) struct { type *head, *tail; } #define ISC_LIST_INIT(list) \ do { (list).head = NULL; (list).tail = NULL; } while (0) #define ISC_LINK(type) struct { type *prev, *next; } #define ISC_LINK_INIT_TYPE(elt, link, type) \ do { \ (elt)->link.prev = (type *)(-1); \ (elt)->link.next = (type *)(-1); \ } while (0) #define ISC_LINK_INIT(elt, link) \ ISC_LINK_INIT_TYPE(elt, link, void) #define ISC_LINK_LINKED(elt, link) ((void *)((elt)->link.prev) != (void *)(-1)) #define ISC_LIST_HEAD(list) ((list).head) #define ISC_LIST_TAIL(list) ((list).tail) #define ISC_LIST_EMPTY(list) ((list.head) == NULL ? 1 : 0) #define __ISC_LIST_PREPENDUNSAFE(list, elt, link) \ do { \ if ((list).head != NULL) \ (list).head->link.prev = (elt); \ else \ (list).tail = (elt); \ (elt)->link.prev = NULL; \ (elt)->link.next = (list).head; \ (list).head = (elt); \ } while (0) #define ISC_LIST_PREPEND(list, elt, link) \ do { \ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ __ISC_LIST_PREPENDUNSAFE(list, elt, link); \ } while (0) #define ISC_LIST_INITANDPREPEND(list, elt, link) \ __ISC_LIST_PREPENDUNSAFE(list, elt, link) #define __ISC_LIST_APPENDUNSAFE(list, elt, link) \ do { \ if ((list).tail != NULL) \ (list).tail->link.next = (elt); \ else \ (list).head = (elt); \ (elt)->link.prev = (list).tail; \ (elt)->link.next = NULL; \ (list).tail = (elt); \ } while (0) #define ISC_LIST_APPEND(list, elt, link) \ do { \ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ __ISC_LIST_APPENDUNSAFE(list, elt, link); \ } while (0) #define ISC_LIST_INITANDAPPEND(list, elt, link) \ __ISC_LIST_APPENDUNSAFE(list, elt, link) #define __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type) \ do { \ if ((elt)->link.next != NULL) \ (elt)->link.next->link.prev = (elt)->link.prev; \ else { \ ISC_INSIST((list).tail == (elt)); \ (list).tail = (elt)->link.prev; \ } \ if ((elt)->link.prev != NULL) \ (elt)->link.prev->link.next = (elt)->link.next; \ else { \ ISC_INSIST((list).head == (elt)); \ (list).head = (elt)->link.next; \ } \ (elt)->link.prev = (type *)(-1); \ (elt)->link.next = (type *)(-1); \ } while (0) #define __ISC_LIST_UNLINKUNSAFE(list, elt, link) \ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void) #define ISC_LIST_UNLINK_TYPE(list, elt, link, type) \ do { \ ISC_LINK_INSIST(ISC_LINK_LINKED(elt, link)); \ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type); \ } while (0) #define ISC_LIST_UNLINK(list, elt, link) \ ISC_LIST_UNLINK_TYPE(list, elt, link, void) #define ISC_LIST_PREV(elt, link) ((elt)->link.prev) #define ISC_LIST_NEXT(elt, link) ((elt)->link.next) #define __ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link) \ do { \ if ((before)->link.prev == NULL) \ ISC_LIST_PREPEND(list, elt, link); \ else { \ (elt)->link.prev = (before)->link.prev; \ (before)->link.prev = (elt); \ (elt)->link.prev->link.next = (elt); \ (elt)->link.next = (before); \ } \ } while (0) #define ISC_LIST_INSERTBEFORE(list, before, elt, link) \ do { \ ISC_LINK_INSIST(ISC_LINK_LINKED(before, link)); \ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ __ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link); \ } while (0) #define __ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link) \ do { \ if ((after)->link.next == NULL) \ ISC_LIST_APPEND(list, elt, link); \ else { \ (elt)->link.next = (after)->link.next; \ (after)->link.next = (elt); \ (elt)->link.next->link.prev = (elt); \ (elt)->link.prev = (after); \ } \ } while (0) #define ISC_LIST_INSERTAFTER(list, after, elt, link) \ do { \ ISC_LINK_INSIST(ISC_LINK_LINKED(after, link)); \ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ __ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link); \ } while (0) #define ISC_LIST_APPENDLIST(list1, list2, link) \ do { \ if (ISC_LIST_EMPTY(list1)) \ (list1) = (list2); \ else if (!ISC_LIST_EMPTY(list2)) { \ (list1).tail->link.next = (list2).head; \ (list2).head->link.prev = (list1).tail; \ (list1).tail = (list2).tail; \ } \ (list2).head = NULL; \ (list2).tail = NULL; \ } while (0) #define ISC_LIST_ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link) #define __ISC_LIST_ENQUEUEUNSAFE(list, elt, link) \ __ISC_LIST_APPENDUNSAFE(list, elt, link) #define ISC_LIST_DEQUEUE(list, elt, link) \ ISC_LIST_UNLINK_TYPE(list, elt, link, void) #define ISC_LIST_DEQUEUE_TYPE(list, elt, link, type) \ ISC_LIST_UNLINK_TYPE(list, elt, link, type) #define __ISC_LIST_DEQUEUEUNSAFE(list, elt, link) \ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void) #define __ISC_LIST_DEQUEUEUNSAFE_TYPE(list, elt, link, type) \ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type) #endif /* ISC_LIST_H */ wrapsrv-0.2/wrapsrv.1000066400000000000000000000022121157177201700146520ustar00rootroot00000000000000'\" -*- coding: us-ascii -*- .if \n(.g .ds T< \\FC .if \n(.g .ds T> \\F[\n[.fam]] .de URL \\$2 \(la\\$1\(ra\\$3 .. .if \n(.g .mso www.tmac .TH wrapsrv 1 "28 May 2011" "" "" .SH NAME wrapsrv \- DNS SRV record command line wrapper .SH SYNOPSIS 'nh .fi .ad l \fBwrapsrv\fR \kx .if (\nx>(\n(.l/2)) .nr x (\n(.l/5) 'in \n(.iu+\nxu [\fISRVNAME\fR] [\fICOMMAND\fR] [\fIOPTION\fR]\&... 'in \n(.iu-\nxu .ad b 'hy .SH DESCRIPTION \fBwrapsrv\fR adds support for connecting to a network service based on DNS SRV record lookups to commands that do not support the DNS SRV record. \fBwrapsrv\fR implements the weighted priority client connection algorithm in RFC 2782. The specified command line will be invoked one or more times with \fI%h\fR and \fI%p\fR sequences in the command line substituted for the hostname and port elements of the selected SRV record. The command line invoked must exit and return 0 after a successful connection or exit and return non-zero if the connection fails. \fBwrapsrv\fR will itself exit with the return code of the last command line invocation, or it may exit with a non-zero return code if the DNS SRV record lookup fails for any reason. wrapsrv-0.2/wrapsrv.c000066400000000000000000000200111157177201700147310ustar00rootroot00000000000000/* * Copyright (c) 2009, 2011 by Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Import. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "list.h" #ifndef NS_MAXMSG #define NS_MAXMSG 65535 #endif /* Data types. */ struct srv { ISC_LINK(struct srv) link; char *tname; uint16_t weight; uint16_t port; }; struct srv_prio { ISC_LINK(struct srv_prio) link; ISC_LIST(struct srv) srv_list; uint16_t prio; }; typedef ISC_LIST(struct srv) srv_list; typedef ISC_LIST(struct srv_prio) srv_prio_list; /* Globals. */ static srv_prio_list prio_list; /* Forward. */ static char *subst_cmd(struct srv *, const char *); static char *target_name(const unsigned char *); static int do_cmd(struct srv *, int, char **); static struct srv *next_tuple(void); static void free_tuples(void); static void insert_tuple(char *, uint16_t, uint16_t, uint16_t); static void parse_answer_section(ns_msg *); static void usage(void); #ifdef DEBUG static void print_tuples(void); #endif /* Functions. */ static struct srv * next_tuple(void) { struct srv_prio *pe; struct srv *se; uint16_t rnd; unsigned csum = 0; unsigned wsum = 0; pe = ISC_LIST_HEAD(prio_list); if (pe == NULL) return (NULL); for (se = ISC_LIST_HEAD(pe->srv_list); se != NULL; se = ISC_LIST_NEXT(se, link)) { wsum += se->weight; } rnd = random() % (wsum + 1); for (se = ISC_LIST_HEAD(pe->srv_list); se != NULL; se = ISC_LIST_NEXT(se, link)) { csum += se->weight; if (csum >= rnd) { ISC_LIST_UNLINK(pe->srv_list, se, link); break; } } if (se == NULL) { ISC_LIST_UNLINK(prio_list, pe, link); free(pe); return (next_tuple()); } #ifdef DEBUG fprintf(stderr, "rnd=%hu -> prio=%hu weight=%hu port=%hu tname=%s\n", rnd, pe->prio, se->weight, se->port, se->tname); #endif return (se); } static void free_tuples(void) { struct srv_prio *pe, *pe_next; struct srv *se, *se_next; pe = ISC_LIST_HEAD(prio_list); while (pe != NULL) { pe_next = ISC_LIST_NEXT(pe, link); ISC_LIST_UNLINK(prio_list, pe, link); se = ISC_LIST_HEAD(pe->srv_list); while (se != NULL) { se_next = ISC_LIST_NEXT(se, link); ISC_LIST_UNLINK(pe->srv_list, se, link); free(se->tname); free(se); se = se_next; } free(pe); pe = pe_next; } } static void insert_tuple(char *tname, uint16_t prio, uint16_t weight, uint16_t port) { struct srv_prio *pe; struct srv *se; for (pe = ISC_LIST_HEAD(prio_list); pe != NULL; pe = ISC_LIST_NEXT(pe, link)) { if (pe->prio == prio) break; } if (pe == NULL) { struct srv_prio *piter; pe = malloc(sizeof(*pe)); assert(pe != NULL); ISC_LINK_INIT(pe, link); ISC_LIST_INIT(pe->srv_list); pe->prio = prio; for (piter = ISC_LIST_HEAD(prio_list); piter != NULL; piter = ISC_LIST_NEXT(piter, link)) { assert(piter->prio != prio); if (piter->prio > prio) { ISC_LIST_INSERTBEFORE(prio_list, piter, pe, link); break; } } if (piter == NULL) ISC_LIST_APPEND(prio_list, pe, link); } se = malloc(sizeof(*se)); assert(se != NULL); ISC_LINK_INIT(se, link); se->tname = tname; se->weight = weight; se->port = port; ISC_LIST_APPEND(pe->srv_list, se, link); } #ifdef DEBUG static void print_tuples(void) { struct srv_prio *pe; struct srv *se; for (pe = ISC_LIST_HEAD(prio_list); pe != NULL; pe = ISC_LIST_NEXT(pe, link)) { fprintf(stderr, "prio=%hu\n", pe->prio); for (se = ISC_LIST_HEAD(pe->srv_list); se != NULL; se = ISC_LIST_NEXT(se, link)) { fprintf(stderr, "\tweight=%hu port=%hu tname=%s\n", se->weight, se->port, se->tname); } } } #endif static char * target_name(const unsigned char *target) { char buf[NS_MAXDNAME]; if (ns_name_ntop(target, buf, sizeof buf) == -1) { perror("ns_name_ntop"); exit(EXIT_FAILURE); } return (strdup(buf)); } static void parse_answer_section(ns_msg *msg) { int rrnum, rrmax; ns_rr rr; uint16_t prio, weight, port, len; const unsigned char *rdata; char *tname; rrmax = ns_msg_count(*msg, ns_s_an); for (rrnum = 0; rrnum < rrmax; rrnum++) { if (ns_parserr(msg, ns_s_an, rrnum, &rr)) { perror("ns_parserr"); exit(EXIT_FAILURE); } if (ns_rr_type(rr) == ns_t_srv) { len = ns_rr_rdlen(rr); rdata = ns_rr_rdata(rr); if (len > 3U * NS_INT16SZ) { NS_GET16(prio, rdata); NS_GET16(weight, rdata); NS_GET16(port, rdata); len -= 3U * NS_INT16SZ; tname = target_name(rdata); insert_tuple(tname, prio, weight, port); } } } } static char * subst_cmd(struct srv *se, const char *cmd) { char *q, *str; const char *p = cmd; int ch; int n_host = 0; int n_port = 0; size_t bufsz; size_t len_tname; len_tname = strlen(se->tname); while ((ch = *p++) != '\0') { if (ch == '%' && *p == 'h') n_host++; if (ch == '%' && *p == 'p') n_port++; } bufsz = strlen(cmd) + 1; bufsz -= 2 * (n_host + n_port); /* '%h' and '%p' */ bufsz += 5 * n_port; /* '%h' -> uint16_t */ bufsz += (strlen(se->tname) + 1) * n_host; str = calloc(1, bufsz); assert(str != NULL); p = cmd; q = str; while ((ch = *p++) != '\0') { if (ch == '%' && *p == 'h') { strcpy(q, se->tname); q += len_tname; p++; } else if (ch == '%' && *p == 'p') { q += sprintf(q, "%hu", se->port); p++; } else { *q++ = ch; } } return (str); } static int do_cmd(struct srv *se, int argc, char **argv) { char *cmd, *scmd, *p; int i, rc; size_t bufsz = 2; for (i = 2; i < argc; i++) { bufsz += 1; bufsz += strlen(argv[i]); } p = cmd = malloc(bufsz); assert(cmd != NULL); for (i = 2; i < argc; i++) { strcpy(p, argv[i]); p += strlen(argv[i]); if (i != argc - 1) { *p++ = ' '; } } scmd = subst_cmd(se, cmd); free(cmd); #ifdef DEBUG fprintf(stderr, "scmd='%s'\n", scmd); #endif rc = system(scmd); rc = WEXITSTATUS(rc); free(scmd); free(se->tname); free(se); #ifdef DEBUG fprintf(stderr, "rc=%i\n", rc); #endif return (rc); } static void usage(void) { fprintf(stderr, "Usage: wrapsrv [OPTION]...\n"); fprintf(stderr, "%%h and %%p sequences will be converted to " "hostname and port.\n"); exit(EXIT_FAILURE); } int main(int argc, char **argv) { char *qname; ns_msg msg; int len, rc = 0; unsigned char answer[NS_MAXMSG]; unsigned rcode; struct srv *se; if (argc < 3) usage(); ISC_LIST_INIT(prio_list); srandom(time(NULL)); res_init(); qname = argv[1]; len = res_query(qname, ns_c_in, ns_t_srv, answer, sizeof answer); if (len < 0) { herror("res_query"); return (EXIT_FAILURE); } if (ns_initparse(answer, len, &msg) < 0) { perror("ns_initparse"); return (EXIT_FAILURE); } rcode = ns_msg_getflag(msg, ns_f_rcode); if (rcode != ns_r_noerror) { fprintf(stderr, "wrapsrv: query for %s returned rcode %u\n", qname, rcode); return (EXIT_FAILURE); } if (ns_msg_count(msg, ns_s_an) == 0) { fprintf(stderr, "wrapsrv: query for %s returned no answers\n", qname); return (EXIT_FAILURE); } parse_answer_section(&msg); #ifdef DEBUG print_tuples(); fprintf(stderr, "\n"); #endif while ((se = next_tuple()) != NULL) { if ((rc = do_cmd(se, argc, argv)) == 0) break; #ifdef DEBUG fprintf(stderr, "\n"); #endif } free_tuples(); return (rc); } wrapsrv-0.2/wrapsrv.docbook000066400000000000000000000032721157177201700161410ustar00rootroot00000000000000 wrapsrv 1 wrapsrv DNS SRV record command line wrapper wrapsrv SRVNAME COMMAND OPTION DESCRIPTION wrapsrv adds support for connecting to a network service based on DNS SRV record lookups to commands that do not support the DNS SRV record. wrapsrv implements the weighted priority client connection algorithm in RFC 2782. The specified command line will be invoked one or more times with %h and %p sequences in the command line substituted for the hostname and port elements of the selected SRV record. The command line invoked must exit and return 0 after a successful connection or exit and return non-zero if the connection fails. wrapsrv will itself exit with the return code of the last command line invocation, or it may exit with a non-zero return code if the DNS SRV record lookup fails for any reason.