├── NEWS ├── AUTHORS ├── ChangeLog ├── INSTALL ├── m4 └── check_compiler_flag.m4 ├── Makefile.am ├── COPYING ├── configure.ac ├── plugin.c ├── function.c ├── mod_txtrec.c ├── mod_arec.c ├── mod_mangler.c ├── fixed.c ├── chaos.c ├── testtcp.c ├── README ├── oas112d.c ├── mod_myip.c ├── evldns.h ├── as112d.c ├── network.c └── evldns.c /NEWS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Lead Author 2 | 3 | Ray Bellis, Internet Systems Consortium, Inc. 4 | 5 | The original implementation was written whilst at Nominet UK. 6 | 7 | Contributors 8 | 9 | Wouter Wijngaards, NLnet Labs 10 | Jakob Schlyter, Kirei.se 11 | Robert Edmonds, Farsight Security, Inc. 12 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | Changelog: $Id$ 2 | 3 | 2009/08/07 - improved IPv6 support 4 | NB: MacOS X requires that sin6_addr.sin6_family is correct. 5 | 6 | 2009/08/05 - license text in all files and autoconf updates from Jakob 7 | 8 | 2009/08/05 - split out separate server contexts each of which 9 | can handle multiple sockets with the same callback 10 | list 11 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Pre-requisites 2 | -------------- 3 | 4 | ldns 1.5.0 or later 5 | http://www.nlnetlabs.nl/projects/ldns/ 6 | 7 | libevent 2.0.0 or later 8 | http://monkey.org/~provos/libevent/ 9 | 10 | Building 11 | -------- 12 | 13 | If building from SVN: 14 | 15 | $ autoreconf --install 16 | 17 | then: 18 | 19 | $ ./configure --with-ldns= --with-libevent= 20 | $ make 21 | -------------------------------------------------------------------------------- /m4/check_compiler_flag.m4: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | 3 | # routine to help check for compiler flags. 4 | AC_DEFUN([CHECK_COMPILER_FLAG],[ 5 | AC_REQUIRE([AC_PROG_CC]) 6 | AC_MSG_CHECKING(whether $CC supports -$1) 7 | cache=`echo $1 | sed 'y% .=/+-%____p_%'` 8 | AC_CACHE_VAL(cv_prog_cc_flag_$cache, 9 | [ 10 | echo 'void f(){}' >conftest.c 11 | if test -z "`$CC -$1 -c conftest.c 2>&1`"; then 12 | eval "cv_prog_cc_flag_$cache=yes" 13 | else 14 | eval "cv_prog_cc_flag_$cache=no" 15 | fi 16 | rm -f conftest* 17 | ]) 18 | if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then 19 | AC_MSG_RESULT(yes) 20 | : 21 | $2 22 | else 23 | AC_MSG_RESULT(no) 24 | : 25 | $3 26 | fi 27 | ]) 28 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | 3 | ACLOCAL_AMFLAGS = -I m4 4 | 5 | MAINTAINERCLEANFILES = \ 6 | config.log config.status \ 7 | $(srcdir)/Makefile.in \ 8 | $(srcdir)/config.h.in $(srcdir)/config.h.in~ \ 9 | $(srcdir)/configure \ 10 | $(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \ 11 | $(srcdir)/depcomp $(srcdir)/aclocal.m4 $(srcdir)/compile \ 12 | $(srcdir)/config.guess $(srcdir)/config.sub 13 | 14 | include_HEADERS = evldns.h 15 | 16 | bin_PROGRAMS = chaos as112d oas112d fixed 17 | 18 | chaos_SOURCES = chaos.c 19 | chaos_LDFLAGS = -rdynamic -levldns -levent -lldns 20 | 21 | as112d_SOURCES = as112d.c 22 | as112d_LDFLAGS = -rdynamic -levldns -levent -lldns 23 | 24 | oas112d_SOURCES = oas112d.c 25 | oas112d_LDFLAGS = -rdynamic -levldns -levent -lldns 26 | 27 | fixed_SOURCES = fixed.c 28 | fixed_LDFLAGS = -rdynamic -levldns -levent -lldns 29 | 30 | lib_LTLIBRARIES = libevldns.la mod_mangler.la mod_txtrec.la mod_arec.la mod_myip.la 31 | 32 | libevldns_la_SOURCES = evldns.c plugin.c function.c network.c 33 | 34 | mod_mangler_la_LDFLAGS = -module 35 | mod_txtrec_la_LDFLAGS = -module 36 | mod_arec_la_LDFLAGS = -module 37 | mod_myip_la_LDFLAGS = -module 38 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Nominet UK 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of Nominet UK nor the names of its contributors may 12 | be used to endorse or promote products derived from this software 13 | without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 16 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | 3 | # -*- Autoconf -*- 4 | # Process this file with autoconf to produce a configure script. 5 | 6 | AC_PREREQ(2.59) 7 | AC_INIT([evldns],[0.2],[ray@bellis.me.uk]) 8 | AC_CONFIG_MACRO_DIR([m4]) 9 | AM_INIT_AUTOMAKE(foreign) 10 | 11 | AC_CONFIG_HEADERS([config.h]) 12 | AC_CONFIG_FILES([Makefile]) 13 | 14 | CHECK_COMPILER_FLAG([Wall], [CFLAGS="-Wall $CFLAGS"]) 15 | 16 | # Checks for programs. 17 | AC_PROG_CC 18 | AC_PROG_CXX 19 | AC_PROG_LIBTOOL 20 | AC_PROG_LN_S 21 | 22 | # Checks for libraries. 23 | AC_ARG_WITH(ldns, 24 | [ --with-ldns=DIR LDNS install directory], [ 25 | AM_CPPFLAGS="$AM_CPPFLAGS -I$withval/include" 26 | AM_LDFLAGS="$AM_LDFLAGS -L$withval/lib" 27 | ]) 28 | 29 | AC_ARG_WITH(libevent, 30 | [ --with-libevent=DIR libevent install directory], [ 31 | AM_CPPFLAGS="$AM_CPPFLAGS -I$withval/include" 32 | AM_LDFLAGS="$AM_LDFLAGS -L$withval/lib" 33 | ]) 34 | 35 | # save user flags 36 | OLDCFLAGS="$CFLAGS" 37 | OLDCPPFLAGS="$CPPFLAGS" 38 | OLDLDFLAGS="$LDFLAGS" 39 | tmp_LIBS="$LIBS" 40 | 41 | # check for libraries using supplied flags 42 | CPPFLAGS="$AM_CPPFLAGS $CPPFLAGS" 43 | LDFLAGS="$AM_LDFLAGS $LDFLAGS" 44 | 45 | AC_CHECK_LIB([ldns], [ldns_dname_match_wildcard], [], 46 | AC_MSG_ERROR([libdns unuseable]) 47 | ) 48 | AC_CHECK_LIB([event], [event_base_new]) 49 | 50 | # restore user flags 51 | CPPFLAGS="$OLDCPPFLAGS" 52 | CFLAGS="$OLDCFLAGS" 53 | LDFLAGS="$OLDLDFLAGS" 54 | LIBS="$tmp_LIBS" 55 | 56 | # Checks for header files. 57 | AC_CHECK_HEADERS([stdlib.h string.h unistd.h]) 58 | AC_CHECK_HEADERS([sys/socket.h netdb.h]) 59 | AC_CHECK_HEADERS([ldns/ldns.h event.h]) 60 | AC_HEADER_STDBOOL 61 | 62 | # Checks for typedefs, structures, and compiler characteristics. 63 | AC_C_CONST 64 | AC_TYPE_SIZE_T 65 | 66 | # Checks for library functions. 67 | AC_FUNC_MALLOC 68 | AC_SEARCH_LIBS([dlopen], [dl]) 69 | AC_CHECK_FUNCS([socket memset strdup]) 70 | AC_CHECK_FUNCS([getaddrinfo getnameinfo]) 71 | AC_SUBST(AM_CPPFLAGS) 72 | AC_SUBST(AM_CFLAGS) 73 | AC_SUBST(AM_LDFLAGS) 74 | AC_OUTPUT 75 | -------------------------------------------------------------------------------- /plugin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | int evldns_load_plugin(struct evldns_server *server, const char *plugin) 35 | { 36 | char *err; 37 | dlerror(); 38 | void *handle = dlopen(plugin, RTLD_NOW); 39 | if ((err = dlerror()) != NULL) { 40 | fprintf(stderr, "dlopen: %s\n", err); 41 | return -1; 42 | } 43 | 44 | evldns_plugin_init init = dlsym(handle, "init"); 45 | if ((err = dlerror()) != NULL) { 46 | fprintf(stderr, "dlopen: %s\n", err); 47 | return -1; 48 | } 49 | 50 | return init(server); 51 | } 52 | -------------------------------------------------------------------------------- /function.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | static TAILQ_HEAD(fbq, fb_function) funcs; 37 | 38 | struct fb_function { 39 | TAILQ_ENTRY(fb_function) next; 40 | const char *name; 41 | evldns_callback func; 42 | }; 43 | typedef struct fb_function fb_function; 44 | 45 | void evldns_init(void) 46 | { 47 | TAILQ_INIT(&funcs); 48 | } 49 | 50 | void evldns_add_function(const char *name, evldns_callback func) 51 | { 52 | fb_function *f = (fb_function *)malloc(sizeof(fb_function)); 53 | memset(f, 0, sizeof(fb_function)); 54 | f->name = strdup(name); 55 | f->func = func; 56 | TAILQ_INSERT_TAIL(&funcs, f, next); 57 | } 58 | 59 | evldns_callback evldns_get_function(const char *name) 60 | { 61 | fb_function *func; 62 | TAILQ_FOREACH(func, &funcs, next) { 63 | if (strcmp(func->name, name) == 0) { 64 | return func->func; 65 | } 66 | } 67 | return NULL; 68 | } 69 | -------------------------------------------------------------------------------- /mod_txtrec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | 33 | /* 34 | * This callback functions just returns a TXT record containing 35 | * whatever string value was passed in the 'user_data' parameter 36 | * when the callback was added. 37 | */ 38 | static void txt_callback(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 39 | { 40 | ldns_pkt *req = srq->request; 41 | ldns_pkt *resp = evldns_response(req, LDNS_RCODE_NOERROR); 42 | ldns_rr *question = ldns_rr_list_rr(ldns_pkt_question(req), 0); 43 | ldns_rr *rr = ldns_rr_clone(question); 44 | 45 | ldns_rr_set_ttl(rr, 0L); 46 | ldns_rr_push_rdf(rr, ldns_rdf_new_frm_str(LDNS_RDF_TYPE_STR, user_data)); 47 | ldns_rr_list_push_rr(ldns_pkt_answer(resp), rr); 48 | ldns_pkt_set_ancount(resp, 1); 49 | 50 | srq->response = resp; 51 | } 52 | 53 | int init(struct evldns_server *p) 54 | { 55 | evldns_add_function("txt", txt_callback); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /mod_arec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: $ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | 33 | /* 34 | * This callback functions just returns an A record containing 35 | * the IP address that was passed in the 'user_data' parameter 36 | * (as a string) when the callback was added. 37 | */ 38 | static void a_callback(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 39 | { 40 | ldns_pkt *req = srq->request; 41 | ldns_pkt *resp = evldns_response(req, LDNS_RCODE_NOERROR); 42 | ldns_rr *question = ldns_rr_list_rr(ldns_pkt_question(req), 0); 43 | ldns_rr *rr = ldns_rr_clone(question); 44 | 45 | ldns_rr_set_ttl(rr, 3600L); 46 | ldns_rr_push_rdf(rr, ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, user_data)); 47 | ldns_rr_list_push_rr(ldns_pkt_answer(resp), rr); 48 | ldns_pkt_set_ancount(resp, 1); 49 | 50 | srq->response = resp; 51 | } 52 | 53 | int init(struct evldns_server *p) 54 | { 55 | evldns_add_function("a", a_callback); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /mod_mangler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | /* 35 | * This function is not actually a callback, although it has 36 | * the same parameter signature as a callback 37 | * 38 | * Given an 'evldns_server_request' that has _already_ been 39 | * populated, it randomly flips bits in the output buffer 40 | * based on the value supplied in 'user_data' 41 | * 42 | * NB: user_data should be passed directly as an integer, 43 | * not as a pointer to an integer 44 | */ 45 | static void bitflip(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 46 | { 47 | int n_bits = (int)(long)user_data; 48 | int i; 49 | 50 | /* can't mangle an empty packet */ 51 | if (!srq->response && !srq->wire_response) { 52 | return; 53 | } 54 | 55 | /* convert LDNS packet to wire format if necessary */ 56 | if (!srq->wire_response) { 57 | ldns_status status = ldns_pkt2wire(&srq->wire_response, 58 | srq->response, &srq->wire_resplen); 59 | if (status != LDNS_STATUS_OK) { 60 | return; 61 | } 62 | } 63 | 64 | if (n_bits < 1) { 65 | n_bits = 1; 66 | } 67 | 68 | /* randomly flip n_bits bits */ 69 | for (i = 0; i < n_bits; ++i) { 70 | int offset = random() % srq->wire_resplen; 71 | int bit = random() % 8; 72 | srq->wire_response[offset] ^= (1 << bit); 73 | } 74 | } 75 | 76 | int init(struct evldns_server *p) 77 | { 78 | evldns_add_function("bitflip", bitflip); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /fixed.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: $ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | /* rejects packets that arrive with OPCODE != QUERY, or QDCOUNT != 1 */ 36 | void query_only(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 37 | { 38 | ldns_pkt *req = srq->request; 39 | 40 | if (ldns_pkt_get_opcode(req) != LDNS_PACKET_QUERY) { 41 | srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL); 42 | } 43 | 44 | if (ldns_pkt_qdcount(req) != 1) { 45 | srq->response = evldns_response(req, LDNS_RCODE_FORMERR); 46 | } 47 | } 48 | 49 | int main(int argc, char *argv[]) 50 | { 51 | struct event_base *base; 52 | struct evldns_server *p; 53 | evldns_callback arec; 54 | 55 | base = event_base_new(); 56 | 57 | /* create an evldns server context */ 58 | evldns_init(); 59 | p = evldns_add_server(base); 60 | 61 | /* create sockets and add them to the context */ 62 | evldns_add_server_ports(p, bind_to_all(NULL, "5053", 10)); 63 | 64 | /* load a couple of plugins */ 65 | evldns_load_plugin(p, ".libs/mod_arec.so"); 66 | 67 | /* get plugin defined functions */ 68 | arec = evldns_get_function("a"); 69 | 70 | /* register a list of callbacks */ 71 | evldns_add_callback(p, NULL, LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, query_only, NULL); 72 | evldns_add_callback(p, "*", LDNS_RR_CLASS_IN, LDNS_RR_TYPE_A, arec, "192.168.1.1"); 73 | 74 | /* and set it running */ 75 | event_base_dispatch(base); 76 | 77 | return EXIT_SUCCESS; 78 | } 79 | -------------------------------------------------------------------------------- /chaos.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | void nxdomain(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 36 | { 37 | ldns_pkt *req = srq->request; 38 | srq->response = evldns_response(req, LDNS_RCODE_NXDOMAIN); 39 | } 40 | 41 | /* rejects packets that arrive with OPCODE != QUERY, or QDCOUNT != 1 */ 42 | void query_only(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 43 | { 44 | ldns_pkt *req = srq->request; 45 | 46 | if (ldns_pkt_get_opcode(req) != LDNS_PACKET_QUERY) { 47 | srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL); 48 | } 49 | 50 | if (ldns_pkt_qdcount(req) != 1) { 51 | srq->response = evldns_response(req, LDNS_RCODE_FORMERR); 52 | } 53 | } 54 | 55 | int main(int argc, char *argv[]) 56 | { 57 | struct event_base *base; 58 | struct evldns_server *p; 59 | evldns_callback myip, txt; 60 | 61 | base = event_base_new(); 62 | evldns_init(); 63 | 64 | /* create an evldns server context */ 65 | p = evldns_add_server(base); 66 | 67 | /* create sockets and add them to the context */ 68 | evldns_add_server_port(p, bind_to_udp4_port(5053)); 69 | evldns_add_server_port(p, bind_to_udp6_port(5053)); 70 | 71 | /* load a couple of plugins */ 72 | evldns_load_plugin(p, ".libs/mod_myip.so"); 73 | evldns_load_plugin(p, ".libs/mod_txtrec.so"); 74 | 75 | /* get plugin defined functions */ 76 | myip = evldns_get_function("myip"); 77 | txt = evldns_get_function("txt"); 78 | 79 | /* register a list of callbacks */ 80 | evldns_add_callback(p, NULL, LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, query_only, NULL); 81 | evldns_add_callback(p, "client.bind", LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, myip, NULL); 82 | evldns_add_callback(p, "version.bind", LDNS_RR_CLASS_CH, LDNS_RR_TYPE_TXT, txt, "evldns-0.2"); 83 | evldns_add_callback(p, "author.bind", LDNS_RR_CLASS_CH, LDNS_RR_TYPE_TXT, txt, "Ray Bellis, R&D Nominet UK"); 84 | evldns_add_callback(p, "*", LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, nxdomain, NULL); 85 | 86 | /* and set it running */ 87 | event_base_dispatch(base); 88 | 89 | return EXIT_SUCCESS; 90 | } 91 | -------------------------------------------------------------------------------- /testtcp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void usage() 12 | { 13 | fprintf(stderr, "usage: testtcp \n"); 14 | exit(EXIT_FAILURE); 15 | } 16 | 17 | int test(struct sockaddr_in *addr, void *out, size_t outlen, void *in, size_t *inlen) 18 | { 19 | int reuse = 1; 20 | int done = 0; 21 | uint16_t msglen = htons(outlen); 22 | #if 0 23 | int delay = 1; 24 | struct iovec vec[2] = { 25 | { &msglen, sizeof (msglen) }, 26 | { out, outlen } 27 | }; 28 | #endif 29 | 30 | int s = socket(PF_INET, SOCK_STREAM, 0); 31 | if (s < 0) { 32 | perror("socket"); 33 | return -1; 34 | } 35 | 36 | /* allow socket re-use */ 37 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { 38 | perror("setsockopt"); 39 | } 40 | 41 | if (connect(s, (struct sockaddr *)addr, sizeof(*addr)) < 0) { 42 | perror("connect"); 43 | return -1; 44 | } 45 | 46 | #if 0 47 | /* write packet length and output buffer */ 48 | if (writev(s, vec, 2) < 0) { 49 | perror("writev"); 50 | return -1; 51 | } 52 | 53 | /* flush socket */ 54 | if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &delay, sizeof(delay))) { 55 | perror("setsockopt"); 56 | } 57 | #else 58 | if (write(s, &msglen, sizeof(msglen)) < 0) { 59 | perror("write"); 60 | return -1; 61 | } 62 | 63 | done = 0; 64 | while (done < outlen) { 65 | int r = write(s, out + done, outlen - done); 66 | if (r < 0) { 67 | perror("write"); 68 | return -1; 69 | } 70 | done += r; 71 | } 72 | #endif 73 | 74 | /* read returned packet length */ 75 | if (read(s, &msglen, sizeof(msglen)) < 0) { 76 | perror("read"); 77 | return -1; 78 | } 79 | 80 | /* read the rest of the packet */ 81 | *inlen = ntohs(msglen); 82 | done = 0; 83 | while (done < *inlen) { 84 | int r = read(s, in + done, *inlen - done); 85 | if (r < 0) { 86 | perror("read"); 87 | return -1; 88 | } 89 | done += r; 90 | } 91 | 92 | shutdown(s, SHUT_RDWR); 93 | close(s); 94 | 95 | return 0; 96 | } 97 | 98 | void loop(struct sockaddr_in *addr) 99 | { 100 | int i = 1; 101 | size_t outlen, inlen; 102 | uint8_t *outbuf; 103 | uint8_t inbuf[LDNS_MAX_PACKETLEN]; 104 | struct timeval tv1, tv2; 105 | ldns_pkt *outpkt; 106 | 107 | gettimeofday(&tv1, NULL); 108 | 109 | ldns_pkt_query_new_frm_str(&outpkt, "www.google.co.uk", 110 | LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, 0); 111 | 112 | while (1) { 113 | 114 | ldns_pkt_set_id(outpkt, rand()); 115 | ldns_pkt2wire(&outbuf, outpkt, &outlen); 116 | 117 | if (test(addr, outbuf, outlen, inbuf, &inlen) < 0) { 118 | return ; 119 | } 120 | i++; 121 | 122 | free(outbuf); 123 | 124 | gettimeofday(&tv2, NULL); 125 | if (tv2.tv_sec - tv1.tv_sec == 5 && 126 | tv2.tv_usec > tv1.tv_usec) { 127 | break; 128 | } 129 | } 130 | 131 | ldns_pkt_free(outpkt); 132 | 133 | fprintf(stdout, "%5d packets handled\n", i++); 134 | fprintf(stdout, "rate = %d\n", i / 5); 135 | } 136 | 137 | int main(int argc, char *argv[]) 138 | { 139 | char *host; 140 | int port; 141 | struct hostent *hostent; 142 | struct sockaddr_in addr; 143 | 144 | if (argc != 3) { 145 | usage(); 146 | } 147 | 148 | host = argv[1]; 149 | port = atoi(argv[2]); 150 | 151 | if ((hostent = gethostbyname(host)) == NULL) { 152 | herror("gethostbyname"); 153 | return EXIT_FAILURE; 154 | } 155 | 156 | memset(&addr, 0, sizeof(addr)); 157 | memcpy(&addr.sin_addr, hostent->h_addr, sizeof(addr.sin_addr)); 158 | addr.sin_family = AF_INET; 159 | addr.sin_port = htons(port); 160 | 161 | loop(&addr); 162 | 163 | return EXIT_SUCCESS; 164 | } 165 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ABOUT 2 | ----- 3 | 4 | evldns is a library and framework designed to ease the creation of 5 | small and fast DNS servers, especially where the responses from those 6 | servers are dynamically synthesised. 7 | 8 | It is a mashup of libevent and ldns. Specifically it's derived from 9 | the server-side half of libevent's "evdns" module re-written to use 10 | the ldns package handling APIs. 11 | 12 | APIs 13 | ---- 14 | 15 | evldns works using callback functions. A list of packet matching patterns 16 | may be registered, along with a pointer to the function that will be 17 | invoked when each pattern is matched. 18 | 19 | The packet match works on the usual DNS triple of (QNAME, QCLASS, QTYPE) 20 | where QNAME may be an exact match or a wildcard, and QCLASS or QTYPE may 21 | be "ANY". 22 | 23 | The callback function is passed two parameters: 24 | 25 | void callback(struct evldns_server_request *req, void *data) 26 | 27 | The "req" parameter contains the complete received DNS request as an 28 | "ldns_pkt". The callback should create a response packet and populate 29 | "req" with that response, which may either be in raw wire format 30 | (req->wire_response and req->wire_len) or in ldns format (req->response). 31 | 32 | If the callback function fails to populate either of the response fields 33 | then the evldns system will pass the received packet onto the next 34 | matching callback. 35 | 36 | Should no callback match then evldns will automatically generate and 37 | return a packet with RCODE = 5 (Refused). 38 | 39 | The "data" parameter is used to pass an additional parameter supplied when 40 | the callback function was registered. See "mod_txtrec.c" for an example 41 | of how "data" may be used to pass expected response data into a callback. 42 | 43 | A complete evldns application requires just a few extra lines of code: 44 | 45 | event_init(); /* initialise libevent */ 46 | evldns_init(); /* initialise evldns */ 47 | 48 | /* create an evldns server context */ 49 | struct evldns_server *server = evldns_add_server(); 50 | 51 | /* register the socket with evldns */ 52 | evldns_add_server_port(server, bind_to_udp4_port(53)); 53 | 54 | /* register callbacks here */ 55 | evldns_add_callback(server, qname, qclass, qtype, callback, data); 56 | ... 57 | 58 | /* and set libevent running */ 59 | event_dispatch(); 60 | 61 | and that's it! 62 | 63 | DEMOS 64 | ----- 65 | 66 | - as112d 67 | 68 | This application shows how evldns may be used to construct an extremely 69 | fast and lightweight server for negative responses for RFC 1918 IP addresses, 70 | as implemented by the AS112 anycast cloud. 71 | 72 | On a single core of an HP DL385 2.0 GHz Opteron server this application has 73 | been benchmarked at over 60,000 queries per second. 74 | 75 | - chaos 76 | 77 | Demonstrates use of plugin modules and callback data to handle queries 78 | for BIND-style "version.bind CH TXT" and "author.bind CH TXT". 79 | 80 | It also includes support for "client.bind CH TXT" and "client.bind IN A/TXT" 81 | which returns the IP(v4) address of the host from which it received the query. 82 | 83 | PLUGINS 84 | ------- 85 | 86 | evldns supports dynamic (shared) modules, and run-time binding to named 87 | callback functions. 88 | 89 | However the implementation of these is quite likely to change. 90 | 91 | LICENSING 92 | --------- 93 | 94 | This code is open source with a BSD license. 95 | 96 | Please see "COPYING" for the full license details. 97 | 98 | TODO 99 | ---- 100 | 101 | - Support for multiple response packets 102 | 103 | 1. Needed for AXFR 104 | 105 | 2. Also for OARC style varying response length responsders, 106 | where multiple packets of differing lengths are sent back 107 | in response to a single inbound request 108 | 109 | - Support for deferred responses 110 | 111 | So that queries can be intentionally delayed, or if for whatever 112 | reason the framework is used in a system where response packets 113 | cannot be immediately synthesised. 114 | -------------------------------------------------------------------------------- /oas112d.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: as112d.c 29 2010-01-15 11:54:55Z ray.bellis $ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | static char *t_soa = "@ SOA a.as112.net. hostmaster.as112.net. 1 604800 2592000 0604800 604800"; 37 | static char *t_ns1 = "@ NS b.as112.net."; 38 | static char *t_ns2 = "@ NS c.as112.net."; 39 | 40 | /* rejects packets that arrive with OPCODE != QUERY, or QDCOUNT != 1 */ 41 | void query_only(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 42 | { 43 | ldns_pkt *req = srq->request; 44 | 45 | if (ldns_pkt_get_opcode(req) != LDNS_PACKET_QUERY) { 46 | srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL); 47 | } 48 | 49 | if (ldns_pkt_qdcount(req) != 1) { 50 | srq->response = evldns_response(req, LDNS_RCODE_FORMERR); 51 | } 52 | } 53 | 54 | void as112_callback(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 55 | { 56 | /* the default response packet */ 57 | ldns_pkt *resp = srq->response = evldns_response(srq->request, LDNS_RCODE_NOERROR); 58 | 59 | /* misc local variables */ 60 | ldns_rr_list *answer, *authority; 61 | ldns_rr *soa, *ns1, *ns2; 62 | int ancount = 0; 63 | 64 | /* we do not support zone transfers */ 65 | if (qtype == LDNS_RR_TYPE_AXFR || qtype == LDNS_RR_TYPE_IXFR) { 66 | ldns_pkt_set_rcode(resp, LDNS_RCODE_NOTIMPL); 67 | return; 68 | } 69 | 70 | /* we need references to the response's sections */ 71 | answer = ldns_pkt_answer(resp); 72 | authority = ldns_pkt_authority(resp); 73 | 74 | /* SOA */ 75 | if (qtype == LDNS_RR_TYPE_ANY || qtype == LDNS_RR_TYPE_SOA) { 76 | ldns_rr_new_frm_str(&soa, t_soa, 300, qname, NULL); 77 | ldns_rr_list_push_rr(answer, soa); 78 | } 79 | 80 | /* NS */ 81 | if (qtype == LDNS_RR_TYPE_ANY || qtype == LDNS_RR_TYPE_NS) { 82 | ldns_rr_new_frm_str(&ns1, t_ns1, 300, qname, NULL); 83 | ldns_rr_new_frm_str(&ns2, t_ns2, 300, qname, NULL); 84 | ldns_rr_list_push_rr(answer, ns1); 85 | ldns_rr_list_push_rr(answer, ns2); 86 | } 87 | 88 | /* if NODATA fill authority section with an SOA */ 89 | ancount = ldns_rr_list_rr_count(answer); 90 | ldns_pkt_set_ancount(resp, ancount); 91 | if (!ancount) { 92 | ldns_rr_new_frm_str(&soa, t_soa, 300, qname, NULL); 93 | ldns_rr_list_push_rr(authority, soa); 94 | ldns_pkt_set_nscount(resp, 1); 95 | } 96 | 97 | /* update packet header */ 98 | ldns_pkt_set_aa(resp, 1); 99 | } 100 | 101 | int main(int argc, char *argv[]) 102 | { 103 | struct evldns_server *p; 104 | struct event_base *base; 105 | 106 | base = event_base_new(); 107 | p = evldns_add_server(base); 108 | evldns_add_server_port(p, bind_to_udp4_port(5053)); 109 | evldns_add_server_port(p, bind_to_tcp4_port(5053, 10)); 110 | evldns_add_callback(p, NULL, LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, query_only, NULL); 111 | evldns_add_callback(p, NULL, LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, as112_callback, NULL); 112 | event_base_dispatch(base); 113 | 114 | return EXIT_SUCCESS; 115 | } 116 | -------------------------------------------------------------------------------- /mod_myip.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | /* 38 | * this callback function returns the IP(v4) address of 39 | * the DNS client that sent the request 40 | * 41 | * If the question is "qname IN TXT" or "qname CH TXT" 42 | * then a TXT record containing the address is returned. 43 | * 44 | * If the question is "qname IN A" then an A record is 45 | * returned instead. 46 | * 47 | * If the question is "qname IN ANY" then both the TXT 48 | * and A records are returned. 49 | */ 50 | static void myip_callback(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 51 | { 52 | ldns_pkt *req = srq->request; 53 | ldns_pkt *resp = evldns_response(req, LDNS_RCODE_NOERROR); 54 | ldns_rr *question = ldns_rr_list_rr(ldns_pkt_question(req), 0); 55 | ldns_rr_list *answer = ldns_pkt_answer(resp); 56 | 57 | /* generate TXT records for client address */ 58 | if ((qclass == LDNS_RR_CLASS_IN || qclass == LDNS_RR_CLASS_CH) && 59 | (qtype == LDNS_RR_TYPE_TXT || qtype == LDNS_RR_TYPE_ANY)) 60 | { 61 | char nbuf[NI_MAXHOST]; 62 | 63 | if (getnameinfo((struct sockaddr *)&srq->addr, srq->addrlen, 64 | nbuf, sizeof(nbuf), NULL, 0, 65 | NI_NUMERICHOST) == 0) 66 | { 67 | ldns_rr *rr = ldns_rr_clone(question); 68 | ldns_rr_push_rdf(rr, ldns_rdf_new_frm_str(LDNS_RDF_TYPE_STR, nbuf)); 69 | ldns_rr_set_type(rr, LDNS_RR_TYPE_TXT); 70 | ldns_rr_set_ttl(rr, 0L); 71 | ldns_rr_list_push_rr(answer, rr); 72 | } 73 | } 74 | 75 | /* generate A records for client address, if the query arrived on IPv4 */ 76 | if (qclass == LDNS_RR_CLASS_IN && srq->addr.ss_family == AF_INET && 77 | (qtype == LDNS_RR_TYPE_A || qtype == LDNS_RR_TYPE_ANY)) { 78 | struct sockaddr_in *p = (struct sockaddr_in *)&srq->addr; 79 | ldns_rr *rr = ldns_rr_clone(question); 80 | ldns_rdf *rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_A, 4, 81 | &p->sin_addr.s_addr); 82 | ldns_rr_push_rdf(rr, rdf); 83 | ldns_rr_set_type(rr, LDNS_RR_TYPE_A); 84 | ldns_rr_set_ttl(rr, 0L); 85 | ldns_rr_list_push_rr(answer, rr); 86 | } 87 | 88 | /* generate AAAA records for client address, if the query arrived on IPv6 */ 89 | if (qclass == LDNS_RR_CLASS_IN && srq->addr.ss_family == AF_INET6 && 90 | (qtype == LDNS_RR_TYPE_AAAA || qtype == LDNS_RR_TYPE_ANY)) { 91 | struct sockaddr_in6 *p = (struct sockaddr_in6 *)&srq->addr; 92 | ldns_rr *rr = ldns_rr_clone(question); 93 | ldns_rdf *rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_AAAA, 16, 94 | &p->sin6_addr.s6_addr); 95 | ldns_rr_push_rdf(rr, rdf); 96 | ldns_rr_set_type(rr, LDNS_RR_TYPE_AAAA); 97 | ldns_rr_set_ttl(rr, 0L); 98 | ldns_rr_list_push_rr(answer, rr); 99 | } 100 | 101 | /* update packet header */ 102 | ldns_pkt_set_ancount(resp, ldns_rr_list_rr_count(answer)); 103 | srq->response = resp; 104 | } 105 | 106 | int init(struct evldns_server *p) 107 | { 108 | evldns_add_function("myip", myip_callback); 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /evldns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #ifndef EVLDNS_H 32 | #define EVLDNS_H 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | /* forward declarations */ 43 | struct evldns_server; 44 | struct evldns_server_port; 45 | struct evldns_server_request; 46 | 47 | /* type declarations */ 48 | 49 | struct evldns_server_request { 50 | 51 | /* the parent server */ 52 | struct evldns_server_port *port; 53 | 54 | /* current socket and (optional) event object */ 55 | int socket; 56 | struct event *event; 57 | 58 | /* the client's address */ 59 | struct sockaddr_storage addr; 60 | socklen_t addrlen; 61 | 62 | /* formatted DNS packets */ 63 | ldns_pkt *request; 64 | ldns_pkt *response; 65 | 66 | /* unformatted request data */ 67 | uint8_t *wire_request; 68 | uint16_t wire_reqlen; 69 | uint16_t wire_reqdone; 70 | 71 | /* unformatted response data */ 72 | uint8_t *wire_response; 73 | size_t wire_resplen; 74 | size_t wire_respdone; 75 | 76 | /* misc flags */ 77 | uint8_t wire_resphead:2; 78 | uint8_t is_tcp:1; 79 | uint8_t blackhole:1; 80 | 81 | /* pending requests for UDP mode */ 82 | TAILQ_ENTRY(evldns_server_request) next; 83 | }; 84 | typedef struct evldns_server_request evldns_server_request; 85 | 86 | typedef void (*evldns_callback)(evldns_server_request *request, void *data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass); 87 | typedef int (*evldns_plugin_init)(struct evldns_server *p); 88 | 89 | /* 90 | * exported functions 91 | */ 92 | 93 | /* core evdns sort-of-clone functions */ 94 | struct evldns_server *evldns_add_server(struct event_base *); 95 | struct evldns_server_port *evldns_add_server_port(struct evldns_server *, int socket); 96 | void evldns_server_close(struct evldns_server_port *port); 97 | void evldns_add_callback(struct evldns_server *server, const char *dname, ldns_rr_class rr_class, ldns_rr_type rr_type, evldns_callback callback, void *data); 98 | ldns_pkt *evldns_response(const ldns_pkt *request, ldns_pkt_rcode rcode); 99 | 100 | /* not-core network function - binds to a list of fds */ 101 | void evldns_add_server_ports(struct evldns_server *, const int *sockets); 102 | 103 | /* plugin and function handling functions */ 104 | extern void evldns_init(void); 105 | extern int evldns_load_plugin(struct evldns_server *server, const char *plugin); 106 | extern void evldns_add_function(const char *name, evldns_callback func); 107 | extern evldns_callback evldns_get_function(const char *name); 108 | 109 | /* miscellaneous utility functions */ 110 | extern int bind_to_sockaddr(struct sockaddr *addr, socklen_t addrlen, int type, int backlog); 111 | extern int bind_to_address(const char *addr, const char *port, int type, int backlog); 112 | extern int bind_to_udp_address(const char *addr, const char *port); 113 | extern int bind_to_tcp_address(const char *addr, const char *port, int backlog); 114 | extern int bind_to_port(int port, int family, int type, int backlog); 115 | extern int bind_to_udp4_port(int port); 116 | extern int bind_to_udp6_port(int port); 117 | extern int bind_to_tcp4_port(int port, int backlog); 118 | extern int bind_to_tcp6_port(int port, int backlog); 119 | extern int *bind_to_all(const char *addr, const char *port, int backlog); 120 | extern int socket_is_tcp(int fd); 121 | 122 | #ifdef __cplusplus 123 | } 124 | #endif 125 | 126 | #endif /* EVLDNS_H */ 127 | -------------------------------------------------------------------------------- /as112d.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | static char *t_soa = "@ SOA prisoner.iana.org. hostmaster.root-servers.org. 2002040800 1800 900 0604800 604800"; 37 | static char *t_ns1 = "@ NS blackhole-1.iana.org."; 38 | static char *t_ns2 = "@ NS blackhole-2.iana.org."; 39 | 40 | typedef struct as112_zone { 41 | ldns_rdf *origin; 42 | ldns_rr *soa; 43 | ldns_rr *ns1; 44 | ldns_rr *ns2; 45 | } as112_zone; 46 | 47 | as112_zone zones[19]; 48 | 49 | /* 50 | * following two functions pre-create a set of standard RRs 51 | * (SOA and NS) for each of the recognised zones 52 | */ 53 | void create_zone(const char *origin, as112_zone *zone) 54 | { 55 | zone->origin = ldns_dname_new_frm_str(origin); 56 | ldns_rr_new_frm_str(&zone->soa, t_soa, 300, zone->origin, NULL); 57 | ldns_rr_new_frm_str(&zone->ns1, t_ns1, 300, zone->origin, NULL); 58 | ldns_rr_new_frm_str(&zone->ns2, t_ns2, 300, zone->origin, NULL); 59 | } 60 | 61 | void create_zones() 62 | { 63 | as112_zone *p = &zones[0]; 64 | create_zone("10.in-addr.arpa", p++); 65 | create_zone("254.169.in-addr.arpa", p++); 66 | create_zone("168.192.in-addr.arpa", p++); 67 | create_zone("16.172.in-addr.arpa", p++); 68 | create_zone("17.172.in-addr.arpa", p++); 69 | create_zone("18.172.in-addr.arpa", p++); 70 | create_zone("19.172.in-addr.arpa", p++); 71 | create_zone("20.172.in-addr.arpa", p++); 72 | create_zone("21.172.in-addr.arpa", p++); 73 | create_zone("22.172.in-addr.arpa", p++); 74 | create_zone("23.172.in-addr.arpa", p++); 75 | create_zone("24.172.in-addr.arpa", p++); 76 | create_zone("25.172.in-addr.arpa", p++); 77 | create_zone("26.172.in-addr.arpa", p++); 78 | create_zone("27.172.in-addr.arpa", p++); 79 | create_zone("28.172.in-addr.arpa", p++); 80 | create_zone("29.172.in-addr.arpa", p++); 81 | create_zone("30.172.in-addr.arpa", p++); 82 | create_zone("31.172.in-addr.arpa", p++); 83 | } 84 | 85 | /* 86 | * given an query, strips it to the relevant portion of the 87 | * in-addr.arpa namespace and uses hard-coded logic to figure 88 | * out which of the pre-created elements of the 'zones' array 89 | * contains the RRs for it 90 | */ 91 | as112_zone *search_zones(ldns_rdf *qname, int *count) 92 | { 93 | int n = 0, slash8 = -1; 94 | *count = ldns_dname_label_count(qname); 95 | while (*count > 0 && n < 4) { 96 | int octet = -1; 97 | 98 | (*count)--; 99 | if (n < 2) { 100 | /* do nothing for in-addr. and arpa. */ 101 | } else { 102 | /* attempt to parse the numeric labels */ 103 | ldns_rdf *label = ldns_dname_label(qname, *count); 104 | uint8_t *data = ldns_rdf_data(label); 105 | char *str = (char*)data + 1, *ptr; 106 | uint8_t c1 = *str, c2; 107 | octet = strtol(str, &ptr, 10); 108 | c2 = *ptr; 109 | ldns_rdf_deep_free(label); 110 | if (!isdigit(c1) || c2 != '\0') return NULL; 111 | if (n == 2) { 112 | slash8 = octet; 113 | if (slash8 == 10) { /* shortcut here for 10.0.0.0/8 */ 114 | return &zones[0]; 115 | } 116 | } 117 | } 118 | 119 | /* if it wasn't 10.0.0.0/8 then check at the /16 boundary */ 120 | if (n == 3) { 121 | if (slash8 == 169 && octet == 254) { 122 | return &zones[1]; 123 | } else if (slash8 == 192 && octet == 168) { 124 | return &zones[2]; 125 | } else if (slash8 == 172 && (octet >= 16 && octet < 32)) { 126 | return &zones[3 + octet - 16]; 127 | } else { 128 | return NULL; 129 | } 130 | } 131 | n++; 132 | } 133 | return NULL; 134 | } 135 | 136 | /* rejects packets that arrive with OPCODE != QUERY, or QDCOUNT != 1 */ 137 | void query_only(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 138 | { 139 | ldns_pkt *req = srq->request; 140 | 141 | if (ldns_pkt_get_opcode(req) != LDNS_PACKET_QUERY) { 142 | srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL); 143 | } 144 | 145 | if (ldns_pkt_qdcount(req) != 1) { 146 | srq->response = evldns_response(req, LDNS_RCODE_FORMERR); 147 | } 148 | } 149 | 150 | void as112_callback(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass) 151 | { 152 | /* copy the question and determine qtype and qname */ 153 | ldns_pkt *req = srq->request; 154 | ldns_pkt *resp = srq->response = evldns_response(req, LDNS_RCODE_REFUSED); 155 | 156 | /* misc local variables */ 157 | ldns_rr_list *answer = ldns_pkt_answer(resp); 158 | int lcount; 159 | 160 | /* figure out what zone we're handling */ 161 | as112_zone *zone = search_zones(qname, &lcount); 162 | if (!zone) { 163 | return; 164 | } 165 | 166 | if (lcount == 0) { /* no more sub-domain labels found */ 167 | /* SOA */ 168 | if (qtype == LDNS_RR_TYPE_ANY || qtype == LDNS_RR_TYPE_SOA) { 169 | ldns_rr_list_push_rr(answer, ldns_rr_clone(zone->soa)); 170 | } 171 | 172 | /* NS */ 173 | if (qtype == LDNS_RR_TYPE_ANY || qtype == LDNS_RR_TYPE_NS) { 174 | ldns_rr_list_push_rr(answer, ldns_rr_clone(zone->ns1)); 175 | ldns_rr_list_push_rr(answer, ldns_rr_clone(zone->ns2)); 176 | } 177 | ldns_pkt_set_rcode(resp, LDNS_RCODE_NOERROR); 178 | } else { /* more labels, left - no good */ 179 | ldns_pkt_set_rcode(resp, LDNS_RCODE_NXDOMAIN); 180 | } 181 | 182 | /* fill authority section if NODATA */ 183 | ldns_pkt_set_ancount(resp, ldns_rr_list_rr_count(answer)); 184 | if (!ldns_rr_list_rr_count(answer)) { 185 | ldns_rr_list_push_rr(ldns_pkt_authority(resp), ldns_rr_clone(zone->soa)); 186 | ldns_pkt_set_nscount(resp, 1); 187 | } 188 | 189 | /* update packet header */ 190 | ldns_pkt_set_aa(resp, 1); 191 | } 192 | int main(int argc, char *argv[]) 193 | { 194 | struct event_base *base; 195 | struct evldns_server *p; 196 | 197 | create_zones(); 198 | base = event_base_new(); 199 | p = evldns_add_server(base); 200 | evldns_add_server_port(p, bind_to_udp4_port(5053)); 201 | evldns_add_server_port(p, bind_to_tcp4_port(5053, 10)); 202 | evldns_add_callback(p, NULL, LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, query_only, NULL); 203 | evldns_add_callback(p, "*.in-addr.arpa.", LDNS_RR_CLASS_ANY, LDNS_RR_TYPE_ANY, as112_callback, NULL); 204 | event_base_dispatch(base); 205 | 206 | return EXIT_SUCCESS; 207 | } 208 | -------------------------------------------------------------------------------- /network.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009-2014, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | /*--------------------------------------------------------------------*/ 44 | 45 | int bind_to_sockaddr(struct sockaddr* addr, socklen_t addrlen, int type, int backlog) 46 | { 47 | int r, s; 48 | int reuse = 1; 49 | char nbuf[NI_MAXHOST]; 50 | 51 | /* make the actual socket */ 52 | s = socket(addr->sa_family, type, 0); 53 | if (s < 0) { 54 | perror("socket"); 55 | return s; 56 | } 57 | 58 | /* disable automatic IPv4 mapped dual-stack */ 59 | #ifdef IPV6_V6ONLY 60 | if (addr->sa_family == AF_INET6) { 61 | int v6only = 1; 62 | if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only))) { 63 | perror("setsockopt(IPV6_ONLY)"); 64 | } 65 | } 66 | #endif 67 | 68 | /* allow socket re-use */ 69 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { 70 | perror("setsockopt(SO_REUSEADDR)"); 71 | } 72 | 73 | /* bind to that local address */ 74 | if ((r = bind(s, addr, addrlen)) < 0) { 75 | perror("bind"); 76 | close(s); 77 | return r; 78 | } 79 | 80 | /* if it's TCP, listen */ 81 | if (type == SOCK_STREAM) { 82 | if ((r = listen(s, backlog)) < 0) { 83 | perror("listen"); 84 | close(s); 85 | return r; 86 | } 87 | } 88 | 89 | /* make the socket non-blocking */ 90 | if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { 91 | perror("fcntl"); 92 | } 93 | 94 | /* log the result of the 'bind' call (non-fatal) */ 95 | if ((r = getnameinfo(addr, addrlen, nbuf, sizeof(nbuf), NULL, 0, NI_NUMERICHOST)) == 0) { 96 | syslog(LOG_INFO, "bound %s fd#%d to %s", (type == SOCK_STREAM) ? "TCP" : "UDP", s, nbuf); 97 | } else { 98 | fprintf(stderr, "getnameinfo: %s\n", gai_strerror(r)); 99 | } 100 | 101 | return s; 102 | } 103 | 104 | int bind_to_port(int port, int family, int type, int backlog) 105 | { 106 | /* set up the local address (protocol specific) */ 107 | if (family == AF_INET) { 108 | struct sockaddr_in addr; 109 | memset(&addr, 0, sizeof(addr)); 110 | 111 | addr.sin_family = family; 112 | addr.sin_addr.s_addr = INADDR_ANY; 113 | addr.sin_port = htons(port); 114 | return bind_to_sockaddr((struct sockaddr *)&addr, sizeof(addr), type, backlog); 115 | } else if (family == AF_INET6) { 116 | struct sockaddr_in6 addr; 117 | memset(&addr, 0, sizeof(addr)); 118 | 119 | addr.sin6_family = AF_INET6; 120 | addr.sin6_addr = in6addr_any; 121 | addr.sin6_port = htons(port); 122 | return bind_to_sockaddr((struct sockaddr *)&addr, sizeof(addr), type, backlog); 123 | } else { 124 | fprintf(stderr, "address family %d not recognized\n", family); 125 | return -1; 126 | } 127 | } 128 | 129 | int bind_to_address(const char *ipaddr, const char *port, int type, int backlog) 130 | { 131 | struct sockaddr_storage addr; 132 | int addrlen; 133 | struct addrinfo hints, *ai; 134 | 135 | memset(&hints, 0, sizeof(hints)); 136 | hints.ai_family = PF_UNSPEC; 137 | hints.ai_flags = AI_PASSIVE; 138 | 139 | int res = getaddrinfo(ipaddr, port, &hints, &ai); 140 | if (res) { 141 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res)); 142 | return -1; 143 | } 144 | 145 | memset(&addr, 0, sizeof(addr)); 146 | addrlen = ai->ai_addrlen; 147 | memcpy(&addr, ai->ai_addr, addrlen); 148 | freeaddrinfo(ai); 149 | 150 | return bind_to_sockaddr((struct sockaddr *)&addr, addrlen, type, backlog); 151 | } 152 | 153 | /*--------------------------------------------------------------------*/ 154 | 155 | int bind_to_udp_address(const char *ipaddr, const char *port) 156 | { 157 | return bind_to_address(ipaddr, port, SOCK_DGRAM, 0); 158 | } 159 | 160 | int bind_to_tcp_address(const char *ipaddr, const char *port, int backlog) 161 | { 162 | return bind_to_address(ipaddr, port, SOCK_STREAM, backlog); 163 | } 164 | 165 | /*--------------------------------------------------------------------*/ 166 | 167 | int bind_to_udp4_port(int port) 168 | { 169 | return bind_to_port(port, AF_INET, SOCK_DGRAM, 0); 170 | } 171 | 172 | int bind_to_tcp4_port(int port, int backlog) 173 | { 174 | return bind_to_port(port, AF_INET, SOCK_STREAM, backlog); 175 | } 176 | 177 | /*--------------------------------------------------------------------*/ 178 | 179 | int bind_to_udp6_port(int port) 180 | { 181 | return bind_to_port(port, AF_INET6, SOCK_DGRAM, 0); 182 | } 183 | 184 | int bind_to_tcp6_port(int port, int backlog) 185 | { 186 | return bind_to_port(port, AF_INET6, SOCK_STREAM, backlog); 187 | } 188 | 189 | /*--------------------------------------------------------------------*/ 190 | 191 | int *bind_to_all(const char *ipaddr, const char *port, int backlog) 192 | { 193 | struct sockaddr_storage addr; 194 | struct addrinfo hints, *ai, *ai0; 195 | int *result = 0; 196 | 197 | memset(&hints, 0, sizeof(hints)); 198 | hints.ai_family = PF_UNSPEC; 199 | hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; 200 | 201 | int res = getaddrinfo(ipaddr, port, &hints, &ai); 202 | if (res) { 203 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res)); 204 | return NULL; 205 | } 206 | ai0 = ai; 207 | 208 | /* count the addrinfo objects */ 209 | int count = 0; 210 | while (ai) { 211 | ++count; 212 | ai = ai->ai_next; 213 | } 214 | 215 | /* make some memory for FDs */ 216 | result = (int *)calloc(count + 1, sizeof(int)); 217 | 218 | int current = 0; 219 | for (ai = ai0 ; ai; ai = ai->ai_next) { 220 | if (ai->ai_socktype != SOCK_DGRAM && ai->ai_socktype != SOCK_STREAM) continue; 221 | 222 | int addrlen = ai->ai_addrlen; 223 | memset(&addr, 0, sizeof(addr)); 224 | memcpy(&addr, ai->ai_addr, addrlen); 225 | 226 | int fd = bind_to_sockaddr((struct sockaddr *)&addr, addrlen, ai->ai_socktype, backlog); 227 | if (fd >= 0) { 228 | result[current++] = fd; 229 | } 230 | } 231 | 232 | /* clean up and terminate */ 233 | freeaddrinfo(ai0); 234 | result[current++] = -1; 235 | 236 | return result; 237 | } 238 | 239 | /*--------------------------------------------------------------------*/ 240 | 241 | int socket_is_tcp(int fd) 242 | { 243 | int type; 244 | socklen_t typelen = sizeof(type); 245 | 246 | getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &typelen); 247 | 248 | return (type == SOCK_STREAM); 249 | } 250 | -------------------------------------------------------------------------------- /evldns.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2009, Nominet UK. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Nominet UK nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY Nominet UK ''AS IS'' AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL Nominet UK BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | * This source file is derived from 'evdns.c' from libevent, originally 30 | * developed by Adam Langley 31 | * 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | 42 | struct evldns_server { 43 | struct event_base *base; 44 | TAILQ_HEAD(evldnscbq, evldns_cb) callbacks; 45 | }; 46 | typedef struct evldns_server evldns_server; 47 | 48 | struct evldns_server_port { 49 | TAILQ_ENTRY(evldns_server_port) next; 50 | evldns_server *server; 51 | int socket; 52 | int refcnt; 53 | struct event *event; 54 | TAILQ_HEAD(evldnssrq, evldns_server_request) pending; 55 | unsigned int is_tcp:1; 56 | unsigned int closing:1; 57 | }; 58 | typedef struct evldns_server_port evldns_server_port; 59 | 60 | struct evldns_cb { 61 | TAILQ_ENTRY(evldns_cb) next; 62 | ldns_rdf *rdf; 63 | ldns_rr_type rr_type; 64 | ldns_rr_class rr_class; 65 | evldns_callback callback; 66 | void *data; 67 | }; 68 | typedef struct evldns_cb evldns_cb; 69 | 70 | /* forward declarations */ 71 | static void evldns_tcp_accept_callback(int fd, short events, void *arg); 72 | static void evldns_tcp_read_callback(int fd, short events, void *arg); 73 | static void evldns_tcp_write_callback(int fd, short events, void *arg); 74 | 75 | static void evldns_udp_callback(int fd, short events, void *arg); 76 | static void evldns_udp_read_callback(evldns_server_port *port); 77 | static void evldns_udp_write_callback(evldns_server_port *port); 78 | 79 | static void server_port_free(evldns_server_port *port); 80 | static int server_request_free(evldns_server_request *req); 81 | static int server_process_packet(evldns_server_request *req); 82 | 83 | /* exported function */ 84 | struct evldns_server *evldns_add_server(struct event_base *base) 85 | { 86 | evldns_server *server; 87 | if (!(server = calloc(1, sizeof(*server)))) { 88 | return NULL; 89 | } 90 | server->base = base; 91 | TAILQ_INIT(&server->callbacks); 92 | 93 | return server; 94 | } 95 | 96 | struct evldns_server_port * 97 | evldns_add_server_port(struct evldns_server *server, int socket) 98 | { 99 | evldns_server_port *port; 100 | void (*callback)(int, short, void *); 101 | 102 | /* don't add bad sockets */ 103 | if (socket < 0) return NULL; 104 | 105 | /* create the evldns_server_port structure */ 106 | if (!(port = calloc(1, sizeof(*port)))) { 107 | return NULL; 108 | } 109 | 110 | /* and populate it */ 111 | port->server = server; 112 | port->socket = socket; 113 | port->refcnt = 1; 114 | port->is_tcp = socket_is_tcp(socket); 115 | 116 | /* and set it up for libevent */ 117 | if (port->is_tcp) { 118 | callback = evldns_tcp_accept_callback; 119 | } else { 120 | callback = evldns_udp_callback; 121 | TAILQ_INIT(&port->pending); // only needed for UDP 122 | } 123 | 124 | port->event = event_new(port->server->base, port->socket, 125 | EV_READ | EV_PERSIST, callback, port); 126 | event_add(port->event, NULL); 127 | 128 | return port; 129 | } 130 | 131 | void 132 | evldns_add_server_ports(struct evldns_server *server, const int *sockets) 133 | { 134 | while (*sockets >= 0) { 135 | (void)evldns_add_server_port(server, *sockets++); 136 | } 137 | } 138 | 139 | void 140 | evldns_close_server_port(evldns_server_port *port) 141 | { 142 | if (--port->refcnt == 0) { 143 | server_port_free(port); 144 | } else { 145 | port->closing = 1; 146 | } 147 | } 148 | 149 | /*-------------------------------------------------------------------*/ 150 | 151 | static void 152 | evldns_tcp_accept_callback(int fd, short events, void *arg) 153 | { 154 | struct timeval tv = { 120, 0 }; 155 | evldns_server_port *port = (evldns_server_port *)arg; 156 | evldns_server_request *req = calloc(1, sizeof(evldns_server_request)); 157 | if (!req) { 158 | perror("calloc"); 159 | return; 160 | } 161 | 162 | req->port = port; 163 | req->addrlen = sizeof(struct sockaddr_storage); 164 | req->socket = accept(fd, (struct sockaddr *)&req->addr, &req->addrlen); 165 | req->is_tcp = 1; 166 | 167 | /* create event on new socket and register that event */ 168 | req->event = event_new(req->port->server->base, req->socket, EV_READ | EV_PERSIST, 169 | evldns_tcp_read_callback, req); 170 | event_add(req->event, &tv); 171 | } 172 | 173 | /*-------------------------------------------------------------------*/ 174 | 175 | static void evldns_tcp_cleanup(evldns_server_request *req) 176 | { 177 | event_del(req->event); 178 | shutdown(req->socket, SHUT_RDWR); 179 | close(req->socket); 180 | server_request_free(req); 181 | } 182 | 183 | static int 184 | evldns_tcp_write_packet(evldns_server_request *req) 185 | { 186 | int r; 187 | 188 | /* 189 | * send the two byte header coalesced with data if possible 190 | */ 191 | if (req->wire_resphead < 2) { 192 | struct iovec iov[2]; 193 | uint16_t len = htons(req->wire_resplen); 194 | 195 | iov[0].iov_base = &len + req->wire_resphead; 196 | iov[0].iov_len = sizeof(len) - req->wire_resphead; 197 | 198 | iov[1].iov_base = req->wire_response; 199 | iov[1].iov_len = req->wire_resplen; 200 | 201 | r = writev(req->socket, &iov[0], 2); 202 | if (r < 0) { 203 | if (errno == EAGAIN || errno == EINTR) { 204 | return 0; 205 | } else { 206 | perror("writev"); 207 | return -1; 208 | } 209 | } else if (r == 0) { 210 | return 0; 211 | } else if (r == 1) { 212 | req->wire_resphead = 1; 213 | } else if (r >= 2) { 214 | req->wire_resphead = 2; 215 | req->wire_respdone = req->wire_resplen - r - 2; 216 | } 217 | } 218 | 219 | /* 220 | * send as much of the rest of the packet as possible 221 | */ 222 | while (req->wire_respdone < req->wire_resplen) { 223 | r = write(req->socket, req->wire_response + req->wire_respdone, 224 | req->wire_resplen - req->wire_respdone); 225 | if (r < 0) { 226 | if (errno == EAGAIN || errno == EINTR) { 227 | return 0; 228 | } else { 229 | perror("write"); 230 | return -1; 231 | } 232 | } else if (r == 0) { 233 | return 0; 234 | } else { 235 | req->wire_respdone += r; 236 | } 237 | } 238 | 239 | /* 240 | * if the whole packet has been sent without any errors set the 241 | * socket back to read-only mode so that more requests can be 242 | * received 243 | */ 244 | if (req->wire_respdone >= req->wire_resplen) { 245 | 246 | /* 247 | * set up the request object reader to receive another 248 | * request - without this it'll loop 249 | */ 250 | ldns_pkt_free(req->response); 251 | if (req->wire_response) { 252 | free(req->wire_response); 253 | } 254 | req->response = 0; 255 | req->wire_response = 0; 256 | req->wire_reqdone = 0; 257 | req->wire_reqlen = 0; 258 | 259 | struct timeval tv = { 120, 0 }; 260 | (void)event_del(req->event); 261 | (void)event_assign(req->event, req->port->server->base, req->socket, 262 | EV_READ | EV_PERSIST, evldns_tcp_read_callback, req); 263 | if (event_add(req->event, &tv) < 0) { 264 | // TODO: warn 265 | } 266 | 267 | return 1; 268 | } else { 269 | return 0; 270 | } 271 | } 272 | 273 | static void 274 | evldns_tcp_write_queue(evldns_server_request *req) 275 | { 276 | int r; 277 | 278 | /* 279 | * first time we've seen this packet, wire_resphead == 0 280 | * indicates that the two byte header needs to be written 281 | */ 282 | req->wire_resphead = 0; 283 | req->wire_respdone = 0; 284 | 285 | /* 286 | * send the packet, and leave libevent expecting more write events 287 | * if the whole packet wasn't sent 288 | */ 289 | r = evldns_tcp_write_packet(req); 290 | if (r == 0) { 291 | struct timeval tv = { 120, 0 }; 292 | (void)event_del(req->event); 293 | (void)event_assign(req->event, req->port->server->base, req->socket, 294 | EV_WRITE | EV_PERSIST, evldns_tcp_write_callback, req); 295 | if (event_add(req->event, &tv) < 0) { 296 | // TODO: warn 297 | } 298 | } else if (r < 0) { 299 | evldns_tcp_cleanup(req); 300 | } 301 | } 302 | 303 | static int 304 | evldns_tcp_read_packet(evldns_server_request *req) 305 | { 306 | int r; 307 | 308 | /* 309 | * if this is a new message - read the two byte message header 310 | */ 311 | if (!req->wire_reqlen) { 312 | uint16_t len = 0; 313 | 314 | r = recv(req->socket, &len, sizeof(len), 0); 315 | if (r < 0) { 316 | if (errno == EAGAIN || errno == EINTR) { 317 | return 0; 318 | } else { 319 | perror("recv"); 320 | return -1; 321 | } 322 | } else if (r == 0) { 323 | return -1; 324 | } else { 325 | if (!len) return 1; /* zero-length request */ 326 | /* get rid of any previous buffer */ 327 | free(req->wire_request); 328 | 329 | /* set up the new buffers */ 330 | req->wire_reqlen = len = ntohs(len); 331 | req->wire_reqdone = 0; 332 | req->wire_request = malloc(len); 333 | if (!req->wire_request) { 334 | perror("malloc"); 335 | return -1; 336 | } 337 | } 338 | } 339 | 340 | /* 341 | * the rest of the message might be available 342 | */ 343 | while (req->wire_reqdone < req->wire_reqlen) { 344 | r = recv(req->socket, req->wire_request + req->wire_reqdone, 345 | req->wire_reqlen - req->wire_reqdone, 0); 346 | if (r < 0) { 347 | if (errno == EAGAIN || errno == EINTR) { 348 | return 0; 349 | } else { 350 | perror("recv"); 351 | return -1; 352 | } 353 | } else if (r == 0) { 354 | return -1; 355 | } else { 356 | req->wire_reqdone += r; 357 | } 358 | } 359 | 360 | /* 361 | * see if we've got the whole request now 362 | */ 363 | return (req->wire_reqdone >= req->wire_reqlen) ? 1 : 0; 364 | } 365 | 366 | static void 367 | evldns_tcp_read_callback(int fd, short events, void *arg) 368 | { 369 | evldns_server_request *req = (evldns_server_request *)arg; 370 | if (events == EV_TIMEOUT) { 371 | evldns_tcp_cleanup(req); 372 | } else if (events & EV_READ) { 373 | int r = evldns_tcp_read_packet(req); 374 | if (r < 0) { 375 | evldns_tcp_cleanup(req); 376 | } else if (r == 1) { 377 | int ret = server_process_packet(req); 378 | if (ret >= 0) { 379 | evldns_tcp_write_queue(req); 380 | } else if (ret == -2) { 381 | /* 382 | * a callback requested blackholing the request 383 | */ 384 | evldns_tcp_cleanup(req); 385 | } 386 | } 387 | } 388 | } 389 | 390 | static void 391 | evldns_tcp_write_callback(int fd, short events, void *arg) 392 | { 393 | evldns_server_request *req = (evldns_server_request *)arg; 394 | if (events == EV_TIMEOUT) { 395 | evldns_tcp_cleanup(req); 396 | } else if (events & EV_WRITE) { 397 | if (evldns_tcp_write_packet(req) < 0) { 398 | evldns_tcp_cleanup(req); 399 | } 400 | } 401 | } 402 | 403 | /*-------------------------------------------------------------------*/ 404 | 405 | static void 406 | evldns_udp_callback(int fd, short events, void *arg) 407 | { 408 | evldns_server_port *port = (evldns_server_port *)arg; 409 | 410 | if (events & EV_READ) { 411 | evldns_udp_read_callback(port); 412 | } 413 | if (events & EV_WRITE) { 414 | evldns_udp_write_callback(port); 415 | } 416 | } 417 | 418 | static int 419 | evldns_server_udp_write_queue(evldns_server_request *req) 420 | { 421 | evldns_server_port *port = req->port; 422 | int r; 423 | 424 | /* 425 | * try and send the datagram immediately 426 | */ 427 | r = sendto(req->socket, req->wire_response, req->wire_resplen, 0, 428 | (struct sockaddr *) &req->addr, req->addrlen); 429 | 430 | /* 431 | * if it failed, queue it for later 432 | */ 433 | if (r < 0) { 434 | if (errno != EAGAIN) { 435 | perror("sendto"); 436 | return -1; 437 | } 438 | 439 | TAILQ_INSERT_TAIL(&port->pending, req, next); 440 | if (TAILQ_FIRST(&port->pending) == req) { 441 | (void)event_del(port->event); 442 | (void)event_assign(port->event, port->server->base, port->socket, 443 | (port->closing ? 0 : EV_READ) | EV_WRITE | EV_PERSIST, 444 | evldns_udp_callback, port); 445 | if (event_add(port->event, NULL) < 0) { 446 | // TODO: warn 447 | } 448 | } 449 | 450 | return 1; 451 | } 452 | 453 | /* 454 | * dispose of the current request - only reached if the original send succeeds 455 | */ 456 | if (server_request_free(req)) { 457 | return 0; 458 | } 459 | 460 | /* 461 | * and send anything else that happens to be in the queue 462 | */ 463 | if (!TAILQ_EMPTY(&port->pending)) { 464 | evldns_udp_write_callback(port); 465 | } 466 | 467 | return 0; 468 | } 469 | 470 | static void 471 | evldns_udp_read_callback(evldns_server_port *port) 472 | { 473 | while (1) { 474 | evldns_server_request *req = calloc(1, sizeof(evldns_server_request)); 475 | if (!req) { 476 | perror("calloc"); 477 | return; 478 | } 479 | 480 | req->wire_request = malloc(LDNS_MAX_PACKETLEN); 481 | if (!req->wire_request) { 482 | free(req); 483 | perror("malloc"); 484 | return; 485 | } 486 | 487 | req->addrlen = sizeof(struct sockaddr_storage); 488 | req->socket = port->socket; 489 | req->port = port; 490 | 491 | ssize_t buflen = recvfrom(req->socket, req->wire_request, LDNS_MAX_PACKETLEN, 0, 492 | (struct sockaddr *)&req->addr, &req->addrlen); 493 | if (buflen < 0) { 494 | if (errno != EAGAIN) { 495 | perror("recvfrom"); 496 | } 497 | free(req->wire_request); 498 | free(req); 499 | return; 500 | } 501 | req->wire_reqlen = (uint16_t)buflen; 502 | 503 | if (server_process_packet(req) >= 0) { 504 | evldns_server_udp_write_queue(req); 505 | } else { 506 | server_request_free(req); 507 | } 508 | } 509 | } 510 | 511 | static void 512 | evldns_udp_write_callback(evldns_server_port *port) 513 | { 514 | struct evldns_server_request *req; 515 | TAILQ_FOREACH(req, &port->pending, next) { 516 | 517 | int r = sendto(port->socket, req->wire_response, req->wire_resplen, 0, 518 | (struct sockaddr *)&req->addr, req->addrlen); 519 | 520 | if (r < 0) { 521 | if (errno == EAGAIN) { 522 | return; 523 | } 524 | perror("sendto"); 525 | } 526 | 527 | TAILQ_REMOVE(&port->pending, req, next); 528 | if (server_request_free(req)) { 529 | return; 530 | } 531 | } 532 | 533 | /* no more write events pending - go back to read-only mode */ 534 | (void)event_del(port->event); 535 | (void)event_assign(port->event, port->server->base, port->socket, 536 | EV_READ | EV_PERSIST, evldns_udp_callback, port); 537 | if (event_add(port->event, NULL) < 0) { 538 | // TODO: warn 539 | } 540 | } 541 | 542 | /*-------------------------------------------------------------------*/ 543 | 544 | ldns_pkt * 545 | evldns_response(const ldns_pkt *req, ldns_pkt_rcode rcode) 546 | { 547 | ldns_pkt *p = ldns_pkt_new(); 548 | ldns_rr_list *q = ldns_rr_list_clone(ldns_pkt_question(req)); 549 | ldns_pkt_opcode opcode = ldns_pkt_get_opcode(req); 550 | 551 | ldns_pkt_set_id(p, ldns_pkt_id(req)); /* copy ID field */ 552 | 553 | if (opcode == LDNS_PACKET_QUERY) { 554 | ldns_pkt_set_cd(p, ldns_pkt_cd(req)); /* copy CD bit */ 555 | ldns_pkt_set_rd(p, ldns_pkt_rd(req)); /* copy RD bit */ 556 | } 557 | 558 | ldns_pkt_set_qr(p, 1); /* this is a response */ 559 | ldns_pkt_set_opcode(p, opcode); /* copy opcode */ 560 | ldns_pkt_set_rcode(p, rcode); /* set rcode */ 561 | 562 | ldns_rr_list_deep_free(p->_question); 563 | ldns_pkt_set_question(p, q); 564 | ldns_pkt_set_qdcount(p, ldns_rr_list_rr_count(q)); 565 | 566 | if (ldns_pkt_edns(req)) { 567 | ldns_pkt_set_edns_udp_size(p, 4096); 568 | if (ldns_pkt_edns_do(req)) { 569 | ldns_pkt_set_edns_do(p, 1); 570 | } 571 | } 572 | 573 | return p; 574 | } 575 | 576 | /*-------------------------------------------------------------------*/ 577 | 578 | static void 579 | server_port_free(evldns_server_port *port) 580 | { 581 | free(port); 582 | } 583 | 584 | static int 585 | server_request_free(evldns_server_request *req) 586 | { 587 | req->port->refcnt--; 588 | 589 | ldns_pkt_free(req->request); 590 | ldns_pkt_free(req->response); 591 | 592 | free(req->wire_request); 593 | free(req->wire_response); 594 | free(req->event); 595 | free(req); 596 | 597 | // TODO?: perhaps free port structure on refcnt == 0? 598 | 599 | return 0; 600 | } 601 | 602 | void evldns_add_callback(evldns_server *server, const char *dname, ldns_rr_class rr_class, ldns_rr_type rr_type, evldns_callback callback, void *data) 603 | { 604 | evldns_cb *cb = (evldns_cb *)calloc(1, sizeof(evldns_cb)); 605 | if (!cb) { 606 | perror("calloc"); 607 | return; 608 | } 609 | 610 | if (dname != NULL) { 611 | cb->rdf = ldns_dname_new_frm_str(dname); 612 | ldns_dname2canonical(cb->rdf); 613 | } 614 | cb->rr_class = rr_class; 615 | cb->rr_type = rr_type; 616 | cb->callback = callback; 617 | cb->data = data; 618 | TAILQ_INSERT_TAIL(&server->callbacks, cb, next); 619 | } 620 | 621 | static void 622 | dispatch_callbacks(struct evldnscbq *callbacks, evldns_server_request *req) 623 | { 624 | evldns_cb *cb; 625 | ldns_pkt *pkt = req->request; 626 | ldns_rr *q = ldns_rr_list_rr(ldns_pkt_question(pkt), 0); 627 | if (q) { 628 | ldns_rr_type qtype = ldns_rr_get_type(q); 629 | ldns_rr_class qclass = ldns_rr_get_class(q); 630 | ldns_rdf *qname = ldns_dname_clone_from(ldns_rr_owner(q), 0); 631 | ldns_dname2canonical(qname); 632 | 633 | TAILQ_FOREACH(cb, callbacks, next) { 634 | if ((cb->rr_class != LDNS_RR_CLASS_ANY) && 635 | (cb->rr_class != ldns_rr_get_class(q))) 636 | { 637 | continue; 638 | } 639 | 640 | /* TODO: dispatch if request QTYPE == ANY? */ 641 | if ((cb->rr_type != LDNS_RR_TYPE_ANY) && 642 | (cb->rr_type != ldns_rr_get_type(q))) 643 | { 644 | continue; 645 | } 646 | 647 | if (cb->rdf) { 648 | if (!ldns_dname_match_wildcard(qname, cb->rdf)) { 649 | continue; 650 | } 651 | } 652 | 653 | (*cb->callback)(req, cb->data, qname, qtype, qclass); 654 | 655 | if (req->response || req->wire_response || req->blackhole) { 656 | break; 657 | } 658 | } 659 | 660 | ldns_rdf_deep_free(qname); 661 | } 662 | } 663 | 664 | static int 665 | server_process_packet(evldns_server_request *req) 666 | { 667 | uint8_t *buffer = req->wire_request; 668 | size_t buflen = req->wire_reqlen; 669 | 670 | req->port->refcnt++; 671 | 672 | /* 673 | * dispose of the previous packet buffers if they're still around 674 | */ 675 | if (req->request) { 676 | ldns_pkt_free(req->request); 677 | req->request = 0; 678 | } 679 | 680 | /* 681 | * convert the received packet into ldns format 682 | */ 683 | if (ldns_wire2pkt(&req->request, buffer, buflen) != LDNS_STATUS_OK) { 684 | return -1; 685 | } 686 | 687 | /* 688 | * don't respond to responses 689 | */ 690 | if (ldns_pkt_qr(req->request)) { 691 | return -1; 692 | } 693 | 694 | /* 695 | * send it to the callback chain 696 | */ 697 | dispatch_callbacks(&req->port->server->callbacks, req); 698 | 699 | /* 700 | * blackhole the request if the callback chain didn't want to answer it 701 | */ 702 | if (req->blackhole) { 703 | req->blackhole = 0; 704 | return -2; 705 | } 706 | 707 | /* 708 | * if the callbacks didn't generate a wire-format response 709 | * then do the necessary stuff here 710 | */ 711 | if (!req->wire_response) { 712 | 713 | /* 714 | * if the callbacks didn't even create an ldns format 715 | * response then return a default (REFUSED) ldns response 716 | */ 717 | if (!req->response) { 718 | req->response = evldns_response(req->request, 719 | LDNS_RCODE_REFUSED); 720 | } 721 | 722 | /* 723 | * convert from ldns format to wire format 724 | */ 725 | ldns_status status = ldns_pkt2wire(&req->wire_response, 726 | req->response, &req->wire_resplen); 727 | if (status != LDNS_STATUS_OK) { 728 | return -1; 729 | } 730 | } 731 | 732 | return 0; 733 | } 734 | --------------------------------------------------------------------------------