Mercurial > dovecot > core-2.2
changeset 10615:8f9fc7fa7c73 HEAD
Added dns-client service and library for doing async dns lookups.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 31 Jan 2010 19:10:38 +0200 |
parents | 45709a87e4b3 |
children | 23956a9b915b |
files | configure.in src/Makefile.am src/dns/Makefile.am src/dns/dns-client-settings.c src/dns/dns-client.c src/lib-dns/Makefile.am src/lib-dns/dns-lookup.c src/lib-dns/dns-lookup.h src/lib-dovecot/Makefile.am |
diffstat | 9 files changed, 447 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.in Sun Jan 31 19:09:44 2010 +0200 +++ b/configure.in Sun Jan 31 19:10:38 2010 +0200 @@ -2407,7 +2407,7 @@ LIBDOVECOT_STORAGE='$(top_builddir)/src/lib-storage/libdovecot-storage.la' LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la' else - LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib/liblib.la' + LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib/liblib.la' LIBDOVECOT="$LIBDOVECOT_DEPS \$(LIBICONV)" LIBDOVECOT_STORAGE_LAST='$(top_builddir)/src/lib-storage/list/libstorage_list.la $(top_builddir)/src/lib-storage/index/libstorage_index.la $(top_builddir)/src/lib-storage/libstorage.la $(top_builddir)/src/lib-index/libindex.la' LIBDOVECOT_STORAGE_FIRST='$(top_builddir)/src/lib-storage/libstorage_service.la $(top_builddir)/src/lib-storage/register/libstorage_register.la' @@ -2578,6 +2578,7 @@ src/lib-auth/Makefile src/lib-charset/Makefile src/lib-dict/Makefile +src/lib-dns/Makefile src/lib-imap/Makefile src/lib-index/Makefile src/lib-lda/Makefile @@ -2609,6 +2610,7 @@ src/log/Makefile src/lmtp/Makefile src/dict/Makefile +src/dns/Makefile src/imap/Makefile src/imap-login/Makefile src/login-common/Makefile
--- a/src/Makefile.am Sun Jan 31 19:09:44 2010 +0200 +++ b/src/Makefile.am Sun Jan 31 19:10:38 2010 +0200 @@ -2,6 +2,7 @@ lib \ lib-auth \ lib-charset \ + lib-dns \ lib-mail \ lib-imap \ lib-master \ @@ -21,6 +22,7 @@ anvil \ auth \ dict \ + dns \ master \ login-common \ imap-login \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dns/Makefile.am Sun Jan 31 19:10:38 2010 +0200 @@ -0,0 +1,14 @@ +pkglibexecdir = $(libexecdir)/dovecot + +pkglibexec_PROGRAMS = dns-client + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-master \ + -I$(top_srcdir)/src/lib-settings + +dns_client_LDADD = $(LIBDOVECOT) $(MODULE_LIBS) +dns_client_DEPENDENCIES = $(LIBDOVECOT_DEPS) +dns_client_SOURCES = \ + dns-client.c \ + dns-client-settings.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dns/dns-client-settings.c Sun Jan 31 19:10:38 2010 +0200 @@ -0,0 +1,47 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "settings-parser.h" +#include "service-settings.h" + +#include <stddef.h> + +/* <settings checks> */ +static struct file_listener_settings dns_client_unix_listeners_array[] = { + { "dns-client", 0666, "", "" }, + { "login/dns-client", 0666, "", "" } +}; +static struct file_listener_settings *dns_client_unix_listeners[] = { + &dns_client_unix_listeners_array[0], + &dns_client_unix_listeners_array[1] +}; +static buffer_t dns_client_unix_listeners_buf = { + dns_client_unix_listeners, sizeof(dns_client_unix_listeners), { 0, } +}; +/* </settings checks> */ + +struct service_settings dns_client_service_settings = { + .name = "dns_client", + .protocol = "", + .type = "", + .executable = "dns-client", + .user = "dovecot", + .group = "", + .privileged_group = "", + .extra_groups = "", + .chroot = "", + + .drop_priv_before_exec = FALSE, + + .process_min_avail = 0, + .process_limit = 0, + .client_limit = 1, + .service_count = 0, + .vsz_limit = -1U, + + .unix_listeners = { { &dns_client_unix_listeners_buf, + sizeof(dns_client_unix_listeners[0]) } }, + .fifo_listeners = ARRAY_INIT, + .inet_listeners = ARRAY_INIT +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dns/dns-client.c Sun Jan 31 19:10:38 2010 +0200 @@ -0,0 +1,147 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "restrict-access.h" +#include "master-service.h" + +#include <unistd.h> + +struct dns_client { + int fd; + struct istream *input; + struct ostream *output; + struct io *io; + struct timeout *to; +}; + +#define MAX_INBUF_SIZE 1024 +#define MAX_OUTBUF_SIZE (1024*64) +#define INPUT_TIMEOUT_MSECS (1000*10) + +static struct dns_client *dns_client = NULL; + +static void dns_client_destroy(struct dns_client **client); + +static int dns_client_input_line(struct dns_client *client, const char *line) +{ + struct ip_addr *ips; + unsigned int i, ips_count; + int ret; + + if (strncmp(line, "IP\t", 3) == 0) { + ret = net_gethostbyname(line + 3, &ips, &ips_count); + if (ret == 0 && ips_count == 0) { + /* shouldn't happen, but fix it anyway.. */ + ret = NO_ADDRESS; + } + if (ret != 0) { + o_stream_send_str(client->output, + t_strdup_printf("%d\n", ret)); + } else { + o_stream_send_str(client->output, + t_strdup_printf("0 %u\n", ips_count)); + for (i = 0; i < ips_count; i++) { + o_stream_send_str(client->output, t_strconcat( + net_ip2addr(&ips[i]), "\n", NULL)); + } + } + } else if (strcmp(line, "QUIT") == 0) { + return -1; + } else { + o_stream_send_str(client->output, "Unknown command\n"); + } + + if (client->output->overflow) + return -1; + return 0; +} + +static void dns_client_input(struct dns_client *client) +{ + const char *line; + int ret = 0; + + o_stream_cork(client->output); + while ((line = i_stream_read_next_line(client->input)) != NULL) { + if (dns_client_input_line(client, line) < 0) { + ret = -1; + break; + } + } + o_stream_uncork(client->output); + timeout_reset(client->to); + + if (client->input->eof || client->input->stream_errno != 0 || ret < 0) + dns_client_destroy(&client); +} + +static void dns_client_timeout(struct dns_client *client) +{ + dns_client_destroy(&client); +} + +static struct dns_client *dns_client_create(int fd) +{ + struct dns_client *client; + + client = i_new(struct dns_client, 1); + client->fd = fd; + client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE); + client->io = io_add(fd, IO_READ, dns_client_input, client); + client->to = timeout_add(INPUT_TIMEOUT_MSECS, dns_client_timeout, + client); + return client; +} + +static void dns_client_destroy(struct dns_client **_client) +{ + struct dns_client *client = *_client; + + *_client = NULL; + + timeout_remove(&client->to); + io_remove(&client->io); + i_stream_destroy(&client->input); + o_stream_destroy(&client->output); + if (close(client->fd) < 0) + i_error("close() failed: %m"); + i_free(client); + + dns_client = NULL; + master_service_client_connection_destroyed(master_service); +} + +static void client_connected(const struct master_service_connection *conn) +{ + if (dns_client != NULL) { + i_error("dns-client must be configured with client_limit=1"); + (void)close(conn->fd); + return; + } + dns_client = dns_client_create(conn->fd); +} + +int main(int argc, char *argv[]) +{ + master_service = master_service_init("dns-client", 0, + &argc, &argv, NULL); + if (master_getopt(master_service) > 0) + return FATAL_DEFAULT; + + master_service_init_log(master_service, "dns-client: "); + restrict_access_by_env(NULL, FALSE); + restrict_access_allow_coredumps(TRUE); + + master_service_init_finish(master_service); + + master_service_run(master_service, client_connected); + if (dns_client != NULL) + dns_client_destroy(&dns_client); + + master_service_deinit(&master_service); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dns/Makefile.am Sun Jan 31 19:10:38 2010 +0200 @@ -0,0 +1,17 @@ +noinst_LTLIBRARIES = libdns.la + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib + +libdns_la_SOURCES = \ + dns-lookup.c + +headers = \ + dns-lookup.h + +if INSTALL_HEADERS + pkginc_libdir=$(pkgincludedir) + pkginc_lib_HEADERS = $(headers) +else + noinst_HEADERS = $(headers) +endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dns/dns-lookup.c Sun Jan 31 19:10:38 2010 +0200 @@ -0,0 +1,184 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "network.h" +#include "istream.h" +#include "write-full.h" +#include "time-util.h" +#include "dns-lookup.h" + +#include <stdio.h> +#include <unistd.h> + +#define MAX_INBUF_SIZE 512 + +struct dns_lookup { + int fd; + char *path; + + struct istream *input; + struct io *io; + struct timeout *to; + + struct timeval start_time; + unsigned int warn_msecs; + + struct dns_lookup_result result; + struct ip_addr *ips; + unsigned int ip_idx; + + dns_lookup_callback_t *callback; + void *context; +}; + +static void dns_lookup_free(struct dns_lookup **_lookup); + +static int dns_lookup_input_line(struct dns_lookup *lookup, const char *line) +{ + struct dns_lookup_result *result = &lookup->result; + + if (result->ips_count == 0) { + /* first line: <ret> <ip count> */ + if (sscanf(line, "%d %u", &result->ret, + &result->ips_count) == 0) + return -1; + if (result->ret != 0) { + result->error = net_gethosterror(result->ret); + return 1; + } + if (result->ips_count == 0) + return -1; + + result->ips = lookup->ips = + i_new(struct ip_addr, result->ips_count); + } else { + if (net_addr2ip(line, &lookup->ips[lookup->ip_idx]) < 0) + return -1; + if (++lookup->ip_idx == result->ips_count) { + result->ret = 0; + return 1; + } + } + return 0; +} + +static void dns_lookup_save_msecs(struct dns_lookup *lookup) +{ + struct timeval now; + int diff; + + if (gettimeofday(&now, NULL) < 0) + i_fatal("gettimeofday() failed: %m"); + + diff = timeval_diff_msecs(&now, &lookup->start_time); + if (diff > 0) + lookup->result.msecs = diff; +} + +static void dns_lookup_input(struct dns_lookup *lookup) +{ + const char *line; + struct dns_lookup_result *result = &lookup->result; + int ret = 0; + + while ((line = i_stream_read_next_line(lookup->input)) != NULL) { + ret = dns_lookup_input_line(lookup, line); + if (ret > 0) + break; + if (ret < 0) { + result->error = t_strdup_printf( + "Invalid input from %s", lookup->path); + break; + } + } + + if (result->error != NULL) { + /* already got the error */ + } else if (lookup->input->stream_errno != 0) { + result->error = t_strdup_printf("read(%s) failed: %m", + lookup->path); + ret = -1; + } else if (lookup->input->eof) { + result->error = t_strdup_printf("Unexpected EOF from %s", + lookup->path); + ret = -1; + } + if (ret != 0) { + dns_lookup_save_msecs(lookup); + lookup->callback(result, lookup->context); + dns_lookup_free(&lookup); + } +} + +static void dns_lookup_timeout(struct dns_lookup *lookup) +{ + lookup->result.error = "DNS lookup timed out"; + + lookup->callback(&lookup->result, lookup->context); + dns_lookup_free(&lookup); +} + +#undef dns_lookup +int dns_lookup(const char *host, const struct dns_lookup_settings *set, + dns_lookup_callback_t *callback, void *context) +{ + struct dns_lookup *lookup; + struct dns_lookup_result result; + const char *cmd; + int fd; + + memset(&result, 0, sizeof(result)); + result.ret = NO_RECOVERY; + + fd = net_connect_unix(set->dns_client_socket_path); + if (fd == -1) { + result.error = t_strdup_printf("connect(%s) failed: %m", + set->dns_client_socket_path); + callback(&result, context); + return -1; + } + + cmd = t_strconcat("IP\t", host, "\n", NULL); + if (write_full(fd, cmd, strlen(cmd)) < 0) { + result.error = t_strdup_printf("write(%s) failed: %m", + set->dns_client_socket_path); + (void)close(fd); + callback(&result, context); + return -1; + } + + lookup = i_new(struct dns_lookup, 1); + lookup->fd = fd; + lookup->path = i_strdup(set->dns_client_socket_path); + lookup->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + lookup->io = io_add(fd, IO_READ, dns_lookup_input, lookup); + if (set->timeout_msecs != 0) { + lookup->to = timeout_add(set->timeout_msecs, + dns_lookup_timeout, lookup); + } + lookup->result.ret = NO_RECOVERY; + lookup->callback = callback; + lookup->context = context; + if (gettimeofday(&lookup->start_time, NULL) < 0) + i_fatal("gettimeofday() failed: %m"); + return 0; +} + +static void dns_lookup_free(struct dns_lookup **_lookup) +{ + struct dns_lookup *lookup = *_lookup; + + *_lookup = NULL; + + if (lookup->to != NULL) + timeout_remove(&lookup->to); + io_remove(&lookup->io); + i_stream_destroy(&lookup->input); + if (close(lookup->fd) < 0) + i_error("close(%s) failed: %m", lookup->path); + + i_free(lookup->ips); + i_free(lookup->path); + i_free(lookup); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dns/dns-lookup.h Sun Jan 31 19:10:38 2010 +0200 @@ -0,0 +1,32 @@ +#ifndef DNS_LOOKUP_H +#define DNS_LOOKUP_H + +struct dns_lookup_settings { + const char *dns_client_socket_path; + unsigned int timeout_msecs; +}; + +struct dns_lookup_result { + /* all is ok if ret=0, otherwise it contains net_gethosterror() + compatible error code. error string is always set if ret != 0. */ + int ret; + const char *error; + + /* how many milliseconds the lookup took. */ + unsigned int msecs; + + unsigned int ips_count; + const struct ip_addr *ips; +}; + +typedef void dns_lookup_callback_t(const struct dns_lookup_result *result, + void *context); + +int dns_lookup(const char *host, const struct dns_lookup_settings *set, + dns_lookup_callback_t *callback, void *context); +#define dns_lookup(host, set, callback, context) \ + CONTEXT_CALLBACK2(dns_lookup, dns_lookup_callback_t, \ + callback, const struct dns_lookup_result *, \ + context, host, set) + +#endif
--- a/src/lib-dovecot/Makefile.am Sun Jan 31 19:09:44 2010 +0200 +++ b/src/lib-dovecot/Makefile.am Sun Jan 31 19:10:38 2010 +0200 @@ -4,6 +4,7 @@ ../lib-imap/libimap.la \ ../lib-mail/libmail.la \ ../lib-auth/libauth.la \ + ../lib-dns/libdns.la \ ../lib-charset/libcharset.la \ ../lib-master/libmaster.la \ ../lib/liblib.la