--- /dev/null
+lib_LTLIBRARIES = libpipebackend.la
+
+libpipebackend_la_SOURCES=pipebackend.cc pipebackend.hh coprocess.cc coprocess.hh
+
--- /dev/null
+#!/bin/sh
+aclocal
+autoconf
+autoheader
+automake --add-missing --foreign
+
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(pipebackend.cc)
+AM_INIT_AUTOMAKE(pdns-pipebackend, 2.7)
+AC_CANONICAL_SYSTEM
+AC_PREFIX_DEFAULT(/opt/pdns)
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AC_DISABLE_STATIC
+AM_PROG_LIBTOOL
+AC_LANG_CPLUSPLUS
+
+dnl Checks for programs.
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -ldl:
+AC_CHECK_LIB(dl, main)
+AC_CHECK_LIB(z, main)
+dnl Replace `main' with a function in -lpthread:
+AC_CHECK_LIB(pthread, main)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(unistd.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+
+dnl Checks for ahudns functions
+dnl Check for ahudns
+
+search_ahudns=1
+
+AC_ARG_WITH(ahudns-includes, [ --with-ahudns-includes=PATH Specify location of ahudns headers],
+[ if test x"$withval" = x"no"; then
+ search_ahudns=0
+ else
+ #if test x"$withval" != x"yes"; then
+ if test -d "$withval"; then
+ AHUDNS_INCLUDES="-I$withval"
+ search_ahudns=0
+ has_ahudns=1
+ fi
+ fi
+])
+
+
+AC_DEFUN(AC_CHECK_AHUDNS,
+[ if test x"$search_ahudns" != x"0"; then
+ if test -f "$1/$2"; then
+ AC_MSG_RESULT($5)
+ AHUDNS_LIBS="$3"
+ AHUDNS_INCLUDES="$4"
+ search_ahudns=0
+ has_ahudns=1
+ fi
+ fi
+])
+
+AC_DEFUN(AC_SEARCH_AHUDNS,
+[ AC_MSG_CHECKING("location of ahudns logger.hh include")
+ AC_CHECK_AHUDNS($HOME/programming/ahudns, logger.hh,,-I$HOME/programming/ahudns, "found in $HOME/programming/ahudns")
+ AC_CHECK_AHUDNS(../ahudns, logger.hh,,-I../ahudns, "found in ../ahudns")
+ AC_CHECK_AHUDNS(../ahudns-1.2, logger.hh,,-I../ahudns-1.2, "found in ../ahudns-1.2")
+])
+
+if test x"$search_ahudns" != x"0"; then
+ AC_SEARCH_AHUDNS()
+fi
+
+if test x"$has_ahudns" = x"1"; then
+ echo Found ahudns include file, assuming working and compliant ahudns
+else
+ echo
+ echo Did not find ahudns include file
+ echo
+ exit 1
+fi
+
+CPPFLAGS="$CPPFLAGS $AHUDNS_INCLUDES"
+
+
+dnl Checks for library functions.
+
+dnl Check for STL
+AC_CHECK_HEADER(sstream,dontneedstl=1)
+
+if test "$dontneedstl" != "1"
+then
+search_stl=1
+
+AC_ARG_WITH(stl-includes, [ --with-stl-includes=PATH Specify location of STL headers],
+[ if test x"$withval" = x"no"; then
+ search_stl=0
+ else
+ #if test x"$withval" != x"yes"; then
+ if test -d "$withval"; then
+ STL_INCLUDES="-I$withval"
+ search_stl=0
+ has_stl=1
+ fi
+ fi
+])
+
+
+
+AC_ARG_WITH(stl-libs, [ --with-stl-libs=PATH Specify location of STL libs],
+[ if test x"$withval" = x"no"; then
+ search_stl=0
+ else
+ #if test x"$withval" != x"yes"; then
+ if test -d "$withval"; then
+ STL_LIBS="$LIBS -L$withval -lstlport_gcc"
+ search_stl=0
+ has_stl=1
+ fi
+ fi
+])
+
+AC_DEFUN(AC_CHECK_STL,
+[ if test x"$search_stl" != x"0"; then
+ if test -f "$1/$2"; then
+ AC_MSG_RESULT($5)
+ STL_LIBS="$3"
+ STL_INCLUDES="$4"
+ search_stl=0
+ has_stl=1
+ fi
+ fi
+])
+
+AC_DEFUN(AC_SEARCH_STL,
+[ AC_MSG_CHECKING("location of sstream")
+ AC_CHECK_STL(/usr/include, sstream, -lstlport_gcc,, "found in /usr/include")
+ AC_CHECK_STL(/usr/include/stlport, sstream, -lstlport_gcc, -I/usr/include/stlport, "found in /usr/include/stlport")
+ AC_CHECK_STL(/usr/include, sstream, -lstlport_gcc,, "found in /usr/include")
+ AC_CHECK_STL(/usr/local/include, sstream, -L/usr/local/lib -lstlport_gcc, -I/usr/local/include, "found in /usr/local")
+ AC_CHECK_STL(/usr/local/include/stl, sstream, -L/usr/local/lib -L/usr/local/lib/stl -lstlport_gcc, -I/usr/local/include/stl, "found in /usr/local/include/stl")
+ AC_CHECK_STL(/home/ahu/download/STLport-4.0/stlport, sstream,-L/home/ahu/download/STLport-4.0/lib/ -lstlport_gcc, -I/home/ahu/download/STLport-4.0/stlport/, "found in /home/ahu/download/STLport-4.0/stlport")
+ AC_CHECK_STL($HOME/STLport-4.0/stlport, sstream,-L$HOME/STLport-4.0/lib/ -lstlport_gcc, -I$HOME/STLport-4.0/stlport/, "found in $HOME/STLport-4.0/stlport")
+])
+
+if test x"$search_stl" != x"0"; then
+ AC_SEARCH_STL()
+fi
+
+if test x"$has_stl" = x"1"; then
+ echo Found sstream include file, assuming working and compliant STL
+else
+ echo
+ echo Did not find sstream include file, this probably means that your STL is not
+ echo compliant - the default gcc libstdc++ isn\'t. Download STLport-4.0 from
+ echo http://www.stlport.org, or use --with-stl-includes and --with-stl-libs
+ echo to specify the location of your STL.
+ echo
+ exit 1
+fi
+
+CPPFLAGS="$CPPFLAGS $STL_INCLUDES"
+LIBS="$LIBS $STL_LIBS"
+fi
+
+AM_CONFIG_HEADER(config.h)
+AC_OUTPUT(Makefile)
--- /dev/null
+#include "coprocess.hh"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <misc.hh>
+
+CoProcess::CoProcess(const string &command,int timeout, int infd, int outfd)
+{
+ const char *argv[2];
+ argv[0]=strdup(command.c_str());
+ argv[1]=0;
+
+ launch(argv,timeout,infd,outfd);
+}
+
+CoProcess::CoProcess(const char **argv, int timeout, int infd, int outfd)
+{
+ launch(argv,timeout,infd,outfd);
+}
+
+void CoProcess::launch(const char **argv, int timeout, int infd, int outfd)
+{
+ d_timeout=timeout;
+ d_infd=infd;
+ d_outfd=outfd;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ if(access(argv[0],X_OK)) // check before fork so we can throw
+ throw AhuException("Command '"+string(argv[0])+"' cannot be executed: "+stringerror());
+
+ if(pipe(d_fd1)<0 || pipe(d_fd2)<0)
+ throw AhuException("Unable to open pipe for coprocess: "+string(strerror(errno)));
+
+ if((d_pid=fork())<0)
+ throw AhuException("Unable to fork for coprocess: "+stringerror());
+ else if(d_pid>0) { // parent speaking
+ close(d_fd1[0]);
+ close(d_fd2[1]);
+ if(!(d_fp=fdopen(d_fd2[0],"r")))
+ throw AhuException("Unable to associate a file pointer with pipe: "+stringerror());
+ setbuf(d_fp,0); // no buffering please, confuses select
+ }
+ else if(!d_pid) { // child
+ close(d_fd1[1]);
+ close(d_fd2[0]);
+
+ if(d_fd1[0]!= infd) {
+ dup2(d_fd1[0], infd);
+ close(d_fd1[0]);
+ }
+
+ if(d_fd2[1]!= outfd) {
+ dup2(d_fd2[1], outfd);
+ close(d_fd2[1]);
+ }
+
+ // stdin & stdout are now connected, fire up our coprocess!
+
+ if(execv(argv[0], const_cast<char * const *>(argv))<0) // now what
+ exit(123);
+
+ /* not a lot we can do here. We shouldn't return because that will leave a forked process around.
+ no way to log this either - only thing we can do is make sure that our parent catches this soonest! */
+ }
+
+}
+
+void CoProcess::checkStatus()
+{
+ int status;
+ int ret=waitpid(d_pid, &status, WNOHANG);
+ if(ret<0)
+ throw AhuException("Unable to ascertain status of coprocess "+itoa(d_pid)+" from "+itoa(getpid())+": "+string(strerror(errno)));
+ else if(ret) {
+ if(WIFEXITED(status)) {
+ int ret=WEXITSTATUS(status);
+ throw AhuException("Coprocess exited with code "+itoa(ret));
+ }
+ if(WIFSIGNALED(status)) {
+ int sig=WTERMSIG(status);
+ string reason="CoProcess died on receiving signal "+itoa(sig);
+#ifdef WCOREDUMP
+ if(WCOREDUMP(status))
+ reason+=". Dumped core";
+#endif
+
+ throw AhuException(reason);
+ }
+ }
+}
+
+void CoProcess::send(const string &snd)
+{
+ checkStatus();
+ string line(snd);
+ line.append(1,'\n');
+
+ int sent=0;
+ int bytes;
+
+ // writen routine - socket may not accept al data in one go
+ while(sent<line.size()) {
+ bytes=write(d_fd1[1],line.c_str()+sent,line.length()-sent);
+ if(bytes<0)
+ throw AhuException("Writing to coprocess failed: "+string(strerror(errno)));
+
+ sent+=bytes;
+ }
+}
+
+void CoProcess::receive(string &receive)
+{
+ char line[1024];
+ memset(line,0,1024);
+
+ if(d_timeout) {
+ struct timeval tv={tv_sec: d_timeout, tv_usec: 0,};
+ fd_set rds;
+ FD_ZERO(&rds);
+ FD_SET(fileno(d_fp),&rds);
+ int ret=select(fileno(d_fp)+1,&rds,0,0,&tv);
+ if(ret<0)
+ throw AhuException("Error waiting on data from coprocess: "+stringerror());
+ if(!ret)
+ throw AhuException("Timeout waiting for data from coprocess");
+ }
+
+ if(!fgets(line,1023,d_fp))
+ throw AhuException("Child closed pipe");
+
+ char *p;
+ if((p=strrchr(line,'\n')))
+ *p=0;
+
+ receive=line;
+}
+void CoProcess::sendReceive(const string &snd, string &rcv)
+{
+ checkStatus();
+ send(snd);
+ receive(rcv);
+
+}
+#ifdef TESTDRIVER
+main()
+{
+ try {
+ CoProcess cp("./irc.pl");
+ string reply;
+ cp.sendReceive("www.trilab.com", reply);
+ cout<<"Answered: '"<<reply<<"'"<<endl;
+ }
+ catch(AhuException &ae) {
+ cerr<<ae.reason<<endl;
+ }
+
+}
+#endif
--- /dev/null
+#ifndef PDNS_COPROCESS_HH
+#define PDNS_COPROCESS_HH
+
+
+#include <ahuexception.hh>
+#include <iostream>
+#include <stdio.h>
+
+using namespace std;
+
+class CoProcess
+{
+public:
+ CoProcess(const string &command,int timeout=0, int infd=0, int outfd=1);
+ CoProcess(const char **argv, int timeout=0, int infd=0, int outfd=1);
+ void launch(const char **argv, int timeout=0, int infd=0, int outfd=1);
+ void sendReceive(const string &send, string &receive);
+ void receive(string &rcv);
+ void send(const string &send);
+private:
+ void CoProcess::checkStatus();
+ int d_fd1[2], d_fd2[2];
+ int d_pid;
+ int d_infd;
+ int d_outfd;
+ int d_timeout;
+ FILE *d_fp;
+};
+#endif
--- /dev/null
+// -*- sateh-c -*-
+// File : pdnsbackend.cc
+// Version : $Id: pipebackend.cc,v 1.1 2002/11/27 15:31:59 ahu Exp $
+//
+
+#include <string>
+#include <map>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sstream>
+#include "coprocess.hh"
+
+using namespace std;
+
+#include <dns.hh>
+#include <dnsbackend.hh>
+#include <dnspacket.hh>
+#include <ueberbackend.hh>
+#include <ahuexception.hh>
+#include <logger.hh>
+#include <arguments.hh>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pipebackend.hh"
+
+static const char *kBackendId = "[PIPEBackend]";
+
+CoWrapper::CoWrapper(const string &command, int timeout)
+{
+ d_cp=0;
+ d_command=command;
+ d_timeout=timeout;
+ launch(); // let exceptions fall through - if initial launch fails, we want to die
+ // I think
+}
+
+void CoWrapper::launch()
+{
+ if(d_cp)
+ return;
+
+ d_cp=new CoProcess(d_command, d_timeout);
+ d_cp->send("HELO\t1");
+ string banner;
+ d_cp->receive(banner);
+ L<<Logger::Error<<"Backend launched with banner: "<<banner<<endl;
+}
+
+void CoWrapper::send(const string &line)
+{
+ launch();
+ try {
+ d_cp->send(line);
+ return;
+ }
+ catch(AhuException &ae) {
+ delete d_cp;
+ d_cp=0;
+ throw;
+ }
+}
+void CoWrapper::receive(string &line)
+{
+ launch();
+ try {
+ d_cp->receive(line);
+ return;
+ }
+ catch(AhuException &ae) {
+ L<<Logger::Warning<<kBackendId<<" unable to receive data from coprocess. "<<ae.reason<<endl;
+ delete d_cp;
+ d_cp=0;
+ throw;
+ }
+}
+
+PipeBackend::PipeBackend(const string &suffix)
+{
+ setArgPrefix("pipe"+suffix);
+ try {
+ d_coproc=new CoWrapper(getArg("command"), getArgAsNum("timeout"));
+ d_regex=getArg("regex").empty() ? 0 : new Regex(getArg("regex"));
+ d_regexstr=getArg("regex");
+ }
+ catch(const ArgException &A) {
+ L<<Logger::Error<<kBackendId<<" Fatal argument error: "<<A.reason<<endl;
+ throw;
+ }
+ catch(...) {
+ throw;
+ }
+}
+
+void PipeBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId)
+{
+ try {
+ d_disavow=false;
+ if(d_regex && !d_regex->match(qname+";"+qtype.getName())) {
+ if(arg().mustDo("query-logging"))
+ L<<Logger::Error<<"Query for '"<<qname<<"' type '"<<qtype.getName()<<"' failed regex '"<<d_regexstr<<"'"<<endl;
+ d_disavow=true; // don't pass to backend
+ } else {
+ ostringstream query;
+ // type qname qclass qtype id ip-address
+ query<<"Q\t"<<qname<<"\tIN\t"<<qtype.getName()<<"\t"<<zoneId<<"\t"<<(pkt_p ? pkt_p->getRemote() : "0.0.0.0");
+ if(arg().mustDo("query-logging"))
+ L<<Logger::Error<<"Query: '"<<query.str()<<"'"<<endl;
+ d_coproc->send(query.str());
+ }
+ }
+ catch(AhuException &ae) {
+ L<<Logger::Error<<kBackendId<<" Error from coprocess: "<<ae.reason<<endl;
+ throw; // hop
+ }
+ d_qtype=qtype;
+ d_qname=qname;
+}
+
+bool PipeBackend::list(int inZoneId)
+{
+ try {
+ d_disavow=false;
+ ostringstream query;
+// The question format:
+
+// type qname qclass qtype id ip-address
+
+ query<<"AXFR\t"<<inZoneId;
+
+ d_coproc->send(query.str());
+ }
+ catch(AhuException &ae) {
+ L<<Logger::Error<<kBackendId<<" Error from coprocess: "<<ae.reason<<endl;
+ throw;
+ }
+ d_qname=itoa(inZoneId);
+ return true;
+}
+
+//! For the dynamic loader
+DNSBackend *PipeBackend::maker()
+{
+ try {
+ return new PipeBackend();
+ }
+ catch(...) {
+ L<<Logger::Error<<kBackendId<<" Unable to instantiate a pipebackend!"<<endl;
+ return 0;
+ }
+}
+
+PipeBackend::~PipeBackend()
+{
+ delete d_regex;
+}
+
+bool PipeBackend::get(DNSResourceRecord &r)
+{
+ if(d_disavow) // this query has been blocked
+ return false;
+
+ string line;
+
+ // The answer format:
+ // DATA qname qclass qtype ttl id content
+
+ for(;;) {
+ d_coproc->receive(line);
+ vector<string>parts;
+ stringtok(parts,line,"\t");
+ if(parts.empty()) {
+ L<<Logger::Error<<kBackendId<<" coprocess returned emtpy line in query for "<<d_qname<<endl;
+ throw AhuException("Format error communicating with coprocess");
+ }
+ else if(parts[0]=="END") {
+ return false;
+ }
+ else if(parts[0]=="LOG") {
+ L<<Logger::Error<<"Coprocess: "<<line.substr(4)<<endl;
+ continue;
+ }
+ else if(parts[0]=="DATA") { // yay
+
+ if(parts.size()<7) {
+ L<<Logger::Error<<kBackendId<<" coprocess returned emtpy line in data section for query for "<<d_qname<<endl;
+ throw AhuException("Format error communicating with coprocess in data section");
+ // now what?
+ }
+ r.qname=parts[1];
+ r.qtype=parts[3];
+ r.ttl=atoi(parts[4].c_str());
+ r.domain_id=atoi(parts[5].c_str());
+ r.content=parts[6];
+ break;
+ }
+ else
+ throw AhuException("Coprocess backend sent incorrect response '"+line+"'");
+ }
+ return true;
+}
+
+//
+// Magic class that is activated when the dynamic library is loaded
+//
+
+class PipeFactory : public BackendFactory
+{
+ public:
+ PipeFactory() : BackendFactory("pipe") {}
+
+ void declareArguments(const string &suffix="")
+ {
+ declare(suffix,"command","Command to execute for piping questions to","");
+ declare(suffix,"timeout","Number of milliseconds to wait for an answer","1000");
+ declare(suffix,"regex","Regular exception of queries to pass to coprocess","");
+ }
+
+ DNSBackend *make(const string &suffix="")
+ {
+ return new PipeBackend(suffix);
+ }
+};
+
+class Loader
+{
+ public:
+ Loader()
+ {
+ BackendMakers().report(new PipeFactory);
+
+ L<<Logger::Notice<<kBackendId<<" This is the pipebackend version "VERSION" ("__DATE__", "__TIME__") reporting"<<endl;
+ }
+};
+
+static Loader loader;
+
--- /dev/null
+//
+// File : pdnsbackend.hh
+// Version : $Id: pipebackend.hh,v 1.1 2002/11/27 15:31:59 ahu Exp $
+//
+
+#ifndef PIPEBACKEND_HH
+#define PIPEBACKEND_HH
+
+#include <string>
+#include <map>
+#include <sys/types.h>
+#include <regex.h>
+
+using namespace std;
+
+/** very small regex wrapper */
+class Regex
+{
+public:
+ /** constructor that accepts the expression to regex */
+ Regex(const string &expr)
+ {
+ if(regcomp(&d_preg, expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED))
+ throw AhuException("Regular expression did not compile");
+ }
+ ~Regex()
+ {
+ regfree(&d_preg);
+ }
+ /** call this to find out if 'line' matches your expression */
+ bool match(const string &line)
+ {
+ return regexec(&d_preg,line.c_str(),0,0,0)==0;
+ }
+
+private:
+ regex_t d_preg;
+};
+
+
+/** The CoWrapper class wraps around a coprocess and restarts it if needed.
+ It may also send out pings and expect banners */
+class CoWrapper
+{
+public:
+ CoWrapper(const string &command, int timeout=0);
+ void send(const string &line);
+ void receive(string &line);
+private:
+ CoProcess *d_cp;
+ string d_command;
+ void launch();
+ int d_timeout;
+};
+
+class PipeBackend : public DNSBackend
+{
+public:
+ PipeBackend(const string &suffix="");
+ ~PipeBackend();
+ void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1);
+ bool list(int domain_id);
+ bool get(DNSResourceRecord &r);
+
+ static DNSBackend *maker();
+
+private:
+ CoWrapper *d_coproc;
+ string d_qname;
+ QType d_qtype;
+ Regex* d_regex;
+ string d_regexstr;
+ bool d_disavow;
+};
+
+
+#endif
+