From ace6dee4866343400e96a3b4f199c373abf5b02a Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Sun, 6 Jun 2004 16:51:40 +0000 Subject: [PATCH] New plugin API and porting of the plugins (+ several changes in the DNS plugin) --- SRC/PLUGINS | 47 ++++++++---- SRC/configure | 1 + SRC/configure.ac | 11 ++- SRC/echoping.c | 106 ++++++++++++++++++---------- SRC/echoping.h | 2 + SRC/error.c | 11 ++- SRC/plugins/dns/dns.c | 34 +++++++-- SRC/plugins/postgresql/postgresql.c | 10 ++- 8 files changed, 151 insertions(+), 71 deletions(-) diff --git a/SRC/PLUGINS b/SRC/PLUGINS index 29787af..2e2916a 100644 --- a/SRC/PLUGINS +++ b/SRC/PLUGINS @@ -1,22 +1,42 @@ -If you want to write your own plugins, they will have to provide three -functions: +If you want to write your own plugins, you will first have to decide +wether your plugin uses a "cooked" hostname or a "raw" one. -char * init (const int argc, const char **argv) - Accepts remaining arguments (you have to use popt to parse them, or -do it by hand, getopt does not allow you to resume the parsing) and -returns a string identifying the port name. If it makes no sense, you -can safely return NULL. +In the first case, the cooked hostname, your plugin will receive from +echoping a struct addrinfo. All the DNS stuff, including IDN, is +performed by echoping. You can immediately start using the struct +addrinfo. + +But some libraries (typically, the one used by the DBMS) do not work +on struct addrinfo but on strings such as "dbname=test +hostname=foo.bar". Plugins using these libraries will have to use the +raw interface. + +You indicate to echoping wether you use the raw interface or the +cooked one by returning a port name or NULL from the init() function. + +You will have to provide three functions: + +char * init (const int argc, const char **argv) Accepts remaining + arguments (you have to use popt to parse them, or do it by hand, + getopt does not allow you to resume the parsing) and returns a + string identifying the port name (cooked interface) or NULL (raw + interface). + +For the cooked interface: void start (struct addrinfo *res) Typically just stores the res structure for later use. -TODO: two start() routines, one which takes a struct addrinfo and one -which does not (PostgreSQL or MySQL does not use struct addrinfo but a -string)? +For the raw interface: + +void start_raw () + Typically connects to the server. int execute () Connects and do whatever the protocol requires. It is called once - per iteration. It returns >=0 if it succeeds and -1 if it failed. + per iteration. It returns >=0 if it succeeds, -1 if it failed + temporarily (so echoping will continue its loop) and -2 if it failes + permanently (so echoping will stop the iteration). void terminate () Cleans everything. It is called after all iterations. @@ -24,11 +44,12 @@ void terminate () Start your plugin source code with: #define IN_PLUGIN -#include "../echoping.h" +#include "/wherever/echoping/is/installed/echoping.h" You can look at random.c, the simplest plugin, and whois.c, the simplest which still does something useful. -TODO: documentation of the plugin. A man page echoping_PLUGINNAME? +The documentation of the plugin should be in a manual page named +echoping_PLUGINNAME. See the above plugins for examples. $Id$ diff --git a/SRC/configure b/SRC/configure index ba1bc9d..d04435a 100755 --- a/SRC/configure +++ b/SRC/configure @@ -18940,6 +18940,7 @@ _ACEOF fi fi; + # Check whether --with-popt or --without-popt was given. if test "${with_popt+set}" = set; then withval="$with_popt" diff --git a/SRC/configure.ac b/SRC/configure.ac index ba14617..2ddbed3 100644 --- a/SRC/configure.ac +++ b/SRC/configure.ac @@ -98,8 +98,15 @@ AC_ARG_WITH(gnutls, CPPFLAGS="${CPPFLAGS} -I$GNUTLSROOT/include" fi fi], - dnl Default: disable it -) + dnl Default: disable it +) + +dnl TODO: test if the getopt variable optreset exists and, if it +dnl does, we can use getopt (Free BSDs and MacOS X). This implies to +dnl maintain the two versions, popt and getopt, while echoping has +dnl many options. It probably means a high-level language to describe +dnl the options and to produce the two versions. gengetopt is an obvious +dnl candidate. AC_ARG_WITH(popt, [ --with-popt[=DIR] popt command-line parsing library],dnl [if test "$withval" != "no"; then diff --git a/SRC/echoping.c b/SRC/echoping.c index a0a9862..de796cc 100644 --- a/SRC/echoping.c +++ b/SRC/echoping.c @@ -87,6 +87,7 @@ main (argc, argv) char *plugin_name, *complete_plugin_name = NULL; char *ext; void *plugin; + int plugin_result; char *dl_result; void to_alarm (); /* our alarm() signal handler */ @@ -100,6 +101,7 @@ main (argc, argv) #endif char *plugin_port_name, *port_name; + unsigned short plugin_raw; unsigned short port_to_use = USE_ECHO; unsigned short http = 0; unsigned short smtp = 0; @@ -143,6 +145,7 @@ main (argc, argv) /* popt variables */ const struct poptOption options[] = { {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v'}, + {"help", '?', POPT_ARG_NONE, NULL, '?'}, {"size", 's', POPT_ARG_INT, &size, 's'}, {"number", 'n', POPT_ARG_INT, &number, 'n'}, #ifdef HAVE_USLEEP @@ -167,7 +170,7 @@ main (argc, argv) {"ipv4", '4', POPT_ARG_NONE, NULL, '4'}, {"ipv6", '6', POPT_ARG_NONE, NULL, '6'}, {"module", 'm', POPT_ARG_STRING, &plugin_name, 'm'}, - {NULL, 0, 0, NULL, 0, NULL, NULL} + POPT_TABLEEND }; poptContext poptcon; @@ -192,20 +195,25 @@ main (argc, argv) } progname = (char *) argv[0]; - poptcon = poptGetContext (NULL, argc, argv, options, 0); + poptcon = + poptGetContext (NULL, argc, argv, options, POPT_CONTEXT_POSIXMEHARDER); - while ((!module_find) && (result = poptGetNextOpt (poptcon)) != -1) + while ((result = poptGetNextOpt (poptcon)) != -1) { if (result < -1) { - printf ("%s: %s", - poptBadOption (poptcon, POPT_BADOPTION_NOALIAS), - poptStrerror (result)); - usage (); + fprintf (stderr, "%s: %s\n", + poptBadOption (poptcon, POPT_BADOPTION_NOALIAS), + poptStrerror (result)); + usage (poptcon); } remaining--; switch ((char) result) { + case '?': + poptPrintHelp (poptcon, stdout, 0); + fprintf (stdout, " hostname [plugin-options...]\n"); + exit (0); case 'v': break; case 'r': @@ -324,7 +332,7 @@ main (argc, argv) break; default: printf ("Unknown character option %d (%c)", result, (char) result); - usage (); + usage (poptcon); } } if (udp && ((port_to_use == USE_CHARGEN) || @@ -445,29 +453,43 @@ main (argc, argv) plugin_name, PLUGINS_DIR, dlerror ()); } plugin_init = dlsym (plugin, "init"); - if (! plugin_init) + if (!plugin_init) { - err_sys ("Cannot find init in %s: %s", plugin_name, dlerror()); + err_sys ("Cannot find init in %s: %s", plugin_name, dlerror ()); } plugin_port_name = plugin_init (remaining, (const char **) leftover); if (plugin_port_name != NULL) - strcpy (port_name, plugin_port_name); + { + strcpy (port_name, plugin_port_name); + plugin_raw = FALSE; + plugin_start = dlsym (plugin, "start"); + if (!plugin_start) + { + err_sys ("Cannot find start in %s: %s", plugin_name, + dlerror ()); + } + } else - port_name = 0; - plugin_start = dlsym (plugin, "start"); - if (! plugin_start) { - err_sys ("Cannot find start in %s: %s", plugin_name, dlerror()); + port_name = 0; + plugin_raw = TRUE; + plugin_raw_start = dlsym (plugin, "start_raw"); + if (!plugin_raw_start) + { + err_sys ("Cannot find start_raw in %s: %s", plugin_name, + dlerror ()); + } } plugin_execute = dlsym (plugin, "execute"); if (!plugin_execute) { - err_sys ("Cannot find execute in %s: %s", plugin_name, dlerror()); + err_sys ("Cannot find execute in %s: %s", plugin_name, dlerror ()); } plugin_terminate = dlsym (plugin, "terminate"); - if (! plugin_terminate) + if (!plugin_terminate) { - err_sys ("Cannot find terminate in %s: %s", plugin_name, dlerror()); + err_sys ("Cannot find terminate in %s: %s", plugin_name, + dlerror ()); } } if (!udp && !ttcp) @@ -475,11 +497,11 @@ main (argc, argv) tcp = 1; } if (remaining == 0) - usage (); + usage (poptcon); if (!module_find && remaining != 1) { - printf ("%d args remaning, should be 1\n", remaining); - usage (); + printf ("%d args remaining, should be 1\n", remaining); + usage (poptcon); } if (verbose) { @@ -613,18 +635,21 @@ main (argc, argv) } } /* Else it is an address, do not IDNize it */ #endif - error = getaddrinfo (server, port_name, &hints, &res); - if (error) + if (!plugin || !plugin_raw) { - err_quit ("getaddrinfo error for host: %s %s", - server, gai_strerror (error)); - } + error = getaddrinfo (server, port_name, &hints, &res); + if (error) + { + err_quit ("getaddrinfo error for host: %s %s", + server, gai_strerror (error)); + } - if (getnameinfo (res->ai_addr, res->ai_addrlen, hbuf, sizeof (hbuf), - pbuf, sizeof (pbuf), niflags) != 0) - { - strcpy (hbuf, "?"); - strcpy (pbuf, "?"); + if (getnameinfo (res->ai_addr, res->ai_addrlen, hbuf, sizeof (hbuf), + pbuf, sizeof (pbuf), niflags) != 0) + { + strcpy (hbuf, "?"); + strcpy (pbuf, "?"); + } } if (plugin) { @@ -632,7 +657,10 @@ main (argc, argv) { printf ("Running start() for the plugin %s...\n", plugin_name); } - plugin_start (res); + if (plugin_raw) + plugin_raw_start (); + else + plugin_start (res); } #ifdef HTTP if (http) @@ -823,9 +851,12 @@ main (argc, argv) } else { - printf - ("Trying to call plugin %s for internet address %s %s...\n", - plugin_name, hbuf, pbuf); + if (plugin_raw) + printf ("Trying to call plugin %s...\n", plugin_name); + else + printf + ("Trying to call plugin %s for internet address %s %s...\n", + plugin_name, hbuf, pbuf); } } #ifdef FLUSH_OUTPUT @@ -856,8 +887,9 @@ main (argc, argv) (void) gettimeofday (&oldtv, (struct timezone *) NULL); if (plugin) { - plugin_execute (); - /* TODO: do something with the return code */ + plugin_result = plugin_execute (); + if (plugin_result == -2) + err_quit(""); } else { diff --git a/SRC/echoping.h b/SRC/echoping.h index 4b5e8a6..d634166 100644 --- a/SRC/echoping.h +++ b/SRC/echoping.h @@ -127,6 +127,8 @@ typedef char * (*init_f) (const int argc, const char **argv); init_f plugin_init; typedef void (*start_f) (struct addrinfo *); start_f plugin_start; +typedef void (*start_raw_f) (); +start_raw_f plugin_raw_start; typedef int (*execute_f) (); execute_f plugin_execute; typedef void (*terminate_f) (); diff --git a/SRC/error.c b/SRC/error.c index 8494eb8..e37d6d6 100644 --- a/SRC/error.c +++ b/SRC/error.c @@ -85,11 +85,10 @@ err_sys (char *str, ...) } void -usage () +usage (poptContext context) { - fprintf (stderr, - "Usage: %s [-4] [-6] [-v] [-r] [-f fill] [-t timeout] [-c] [-d] [-u] [-s size] [-n number] [-w delay] [-h url] [-i url] [-p priority] [-P tos] [-C] [-S] [-m plugin] hostname[:port]\n", - progname); + poptPrintUsage (context, stderr, 0); + fprintf (stderr, " hostname [plugin-options...]\n"); exit (1); } @@ -110,11 +109,11 @@ sys_err_str () if (errno != 0) { - sprintf (msgstr, "(%s)", strerror(errno)); + sprintf (msgstr, "(%s)", strerror (errno)); } else { msgstr[0] = '\0'; - } + } return (msgstr); } diff --git a/SRC/plugins/dns/dns.c b/SRC/plugins/dns/dns.c index 1aa8a5f..490cad4 100644 --- a/SRC/plugins/dns/dns.c +++ b/SRC/plugins/dns/dns.c @@ -1,7 +1,5 @@ /* * DNS plugin. - * TODO: return errors to echoping (name server not existing, for instance) - * TODO: allow options like TCP * $Id$ */ @@ -16,6 +14,8 @@ struct addrinfo name_server; poptContext dns_poptcon; char *request; int type; +short use_tcp = FALSE; +short no_recurse = FALSE; /* nsError stolen from Liu & Albitz check_soa (in their book "DNS and BIND") */ @@ -78,6 +78,12 @@ init (const int argc, const char **argv) {"type", 't', POPT_ARG_STRING, &type_name, 0, "Type of resources queried (A, MX, SOA, etc)", "type"}, + {"tcp", NULL, POPT_ARG_NONE, &use_tcp, 0, + "Use TCP for the request (virtual circuit)", + "tcp"}, + {"no-recurse", NULL, POPT_ARG_NONE, &no_recurse, 0, + "Do not ask recursion", + "no-recurse"}, POPT_AUTOHELP POPT_TABLEEND }; dns_poptcon = poptGetContext (NULL, argc, @@ -100,14 +106,18 @@ init (const int argc, const char **argv) { /* TODO: a better algorithm. Use dns_rdatatype_fromtext in BIND ? */ if (!strcmp (type_name, "A")) type = T_A; + else if (!strcmp (type_name, "AAAA")) + type = T_AAAA; else if (!strcmp (type_name, "NS")) type = T_NS; else if (!strcmp (type_name, "SOA")) type = T_SOA; else if (!strcmp (type_name, "MX")) type = T_MX; - else if (!strcmp (type_name, "AAAA")) - type = T_AAAA; + else if (!strcmp (type_name, "SRV")) + type = T_SRV; + else if (!strcmp (type_name, "TXT")) + type = T_TXT; else dns_usage ("Unknown type"); } @@ -129,6 +139,14 @@ start (struct addrinfo *res) _res.nsaddr_list[0] = name_server_sockaddr_in; /* TODO: and IPv6? Detect _resext with autoconf (*BSD) and use it */ _res.nscount = 1; _res.options &= ~(RES_DNSRCH | RES_DEFNAMES | RES_NOALIASES); + if (use_tcp) + { + _res.options &= RES_USEVC; + } + if (no_recurse) + { + _res.options &= ~RES_RECURSE; + } } int @@ -142,13 +160,15 @@ execute () int response_length; /* buffer length */ if ((response_length = res_query (request, /* the domain we care about */ C_IN, /* Internet class records */ - type, - (u_char *) & response, /*response buffer */ + type, (u_char *) & response, /*response buffer */ sizeof (response))) /*buffer size */ < 0) { /*If negative */ nsError (h_errno, request); /* report the error */ - return -1; /* and quit */ + if (h_errno == TRY_AGAIN) + return -1; /* More luck next time? */ + else + return -2; /* Give in */ } /* TODO: better analysis of the replies. For instance, replies can be in the authority section (delegation info) */ return 0; diff --git a/SRC/plugins/postgresql/postgresql.c b/SRC/plugins/postgresql/postgresql.c index c03eb31..8575fe9 100644 --- a/SRC/plugins/postgresql/postgresql.c +++ b/SRC/plugins/postgresql/postgresql.c @@ -58,7 +58,7 @@ init (const int argc, const char **argv) postgresql_usage (msg); } } - if (request == NULL) /* TODO: a default like SELECT now()? */ + if (request == NULL) /* TODO: a default like SELECT now()? */ postgresql_usage ("Mandatory request missing"); if (conninfo == NULL) postgresql_usage ("Mandatory connection information missing"); @@ -66,18 +66,16 @@ init (const int argc, const char **argv) } void -start (struct addrinfo *res) +start_raw () { conn = PQconnectdb (conninfo); if (conn == NULL) { - printf ("Cannot create connection\n"); - exit (1); + err_quit ("Cannot create connection\n"); } if (PQstatus (conn) == CONNECTION_BAD) { - printf ("Connection failed: %s\n", PQerrorMessage (conn)); - exit (1); + err_quit ("Connection failed: %s\n", PQerrorMessage (conn)); } }