/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "wildcard-match.h"
#include "hash.h"
#include "llist.h"
#include "settings-parser.h"
+#include "dns-util.h"
#include "master-service-private.h"
#include "master-service-settings.h"
#include "master-service-settings-cache.h"
#define CACHE_INITIAL_ENTRY_POOL_SIZE (1024*16)
#define CACHE_ADD_ENTRY_POOL_SIZE 1024
+struct config_filter {
+ struct config_filter *prev, *next;
+
+ const char *local_name;
+ struct ip_addr local_ip, remote_ip;
+ unsigned int local_bits, remote_bits;
+};
+
struct settings_entry {
struct settings_entry *prev, *next;
HASH_TABLE(char *, struct settings_entry *) local_name_hash;
HASH_TABLE(struct ip_addr *, struct settings_entry *) local_ip_hash;
+ struct config_filter *filters;
+
/* Initial size for new settings entry pools */
size_t approx_entry_pool_size;
/* number of bytes malloced by cached settings entries
return cache;
}
+int master_service_settings_cache_init_filter(struct master_service_settings_cache *cache)
+{
+ const char *const *filters;
+ const char *error;
+
+ if (cache->filters != NULL)
+ return 0;
+ if (master_service_settings_get_filters(cache->service, &filters, &error) < 0) {
+ i_error("master-service: cannot get filters: %s", error);
+ return -1;
+ }
+
+ /* parse filters */
+ while(*filters != NULL) {
+ const char *const *keys = t_strsplit_spaces(*filters, " ");
+ struct config_filter *filter =
+ p_new(cache->pool, struct config_filter, 1);
+ while(*keys != NULL) {
+ if (strncmp(*keys, "local-net=", 10) == 0) {
+ (void)net_parse_range((*keys)+10,
+ &filter->local_ip, &filter->local_bits);
+ } else if (strncmp(*keys, "remote-net=", 11) == 0) {
+ (void)net_parse_range((*keys)+11,
+ &filter->remote_ip, &filter->remote_bits);
+ } else if (strncmp(*keys, "local-name=", 11) == 0) {
+ filter->local_name = p_strdup(cache->pool, (*keys)+11);
+ }
+ keys++;
+ }
+ DLLIST_PREPEND(&cache->filters, filter);
+ filters++;
+ }
+ return 0;
+}
+
+/* Remove any elements which there is no filter for */
+static void
+master_service_settings_cache_fix_input(struct master_service_settings_cache *cache,
+ const struct master_service_settings_input *input,
+ struct master_service_settings_input *new_input)
+{
+ bool found_lip, found_rip, found_local_name;
+
+ found_lip = found_rip = found_local_name = FALSE;
+
+ struct config_filter *filter = cache->filters;
+ while(filter != NULL) {
+ if (filter->local_bits > 0 &&
+ net_is_in_network(&input->local_ip, &filter->local_ip,
+ filter->local_bits))
+ found_lip = TRUE;
+ if (filter->remote_bits > 0 &&
+ net_is_in_network(&input->remote_ip, &filter->remote_ip,
+ filter->remote_bits))
+ found_rip = TRUE;
+ if (input->local_name != NULL && filter->local_name != NULL &&
+ dns_match_wildcard(input->local_name, filter->local_name))
+ found_local_name = TRUE;
+ filter = filter->next;
+ };
+
+ *new_input = *input;
+
+ if (!found_lip)
+ i_zero(&new_input->local_ip);
+ if (!found_rip)
+ i_zero(&new_input->remote_ip);
+ if (!found_local_name)
+ new_input->local_name = NULL;
+}
+
+
void master_service_settings_cache_deinit(struct master_service_settings_cache **_cache)
{
struct master_service_settings_cache *cache = *_cache;
return 0;
new_input = *input;
+ if (cache->filters != NULL) {
+ master_service_settings_cache_fix_input(cache, input, &new_input);
+ if (cache_find(cache, &new_input, parser_r))
+ return 0;
+ }
+
if (dyn_parsers != NULL) {
settings_parser_dyn_update(cache->pool, &new_input.roots,
dyn_parsers);
return 0;
}
+static int
+config_send_filters_request(int fd, const char *path, const char **error_r)
+{
+ int ret;
+ ret = write_full(fd, CONFIG_HANDSHAKE"FILTERS\n", strlen(CONFIG_HANDSHAKE"FILTERS\n"));
+ if (ret < 0) {
+ *error_r = t_strdup_printf("write_full(%s) failed: %m", path);
+ return -1;
+ }
+ return 0;
+}
+
static int
master_service_apply_config_overrides(struct master_service *service,
struct setting_parser_context *parser,
service->config_fd = fd;
}
+int master_service_settings_get_filters(struct master_service *service,
+ const char *const **filters,
+ const char **error_r)
+{
+ struct master_service_settings_input input;
+ int fd;
+ bool retry = TRUE;
+ const char *path = NULL;
+ ARRAY_TYPE(const_string) filters_tmp;
+ t_array_init(&filters_tmp, 8);
+ i_zero(&input);
+
+ if (getenv("DOVECONF_ENV") == NULL &&
+ (service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) == 0) {
+ retry = service->config_fd != -1;
+ for (;;) {
+ fd = master_service_open_config(service, &input, &path, error_r);
+ if (fd == -1) {
+ return -1;
+ }
+ if (config_send_filters_request(fd, path, error_r) == 0)
+ break;
+
+ i_close_fd(&fd);
+ if (!retry)
+ return -1;
+ retry = FALSE;
+ }
+ service->config_fd = fd;
+ struct istream *is = i_stream_create_fd(fd, (size_t)-1, FALSE);
+ const char *line;
+ /* try read response */
+ while((line = i_stream_read_next_line(is)) != NULL) {
+ if (*line == '\0')
+ break;
+ if (strncmp(line, "FILTER\t", 7) == 0) {
+ line = t_strdup(line+7);
+ array_append(&filters_tmp, &line, 1);
+ }
+ }
+ i_stream_unref(&is);
+ }
+
+ array_append_zero(&filters_tmp);
+ *filters = array_idx(&filters_tmp, 0);
+ return 0;
+}
+
int master_service_settings_read(struct master_service *service,
const struct master_service_settings_input *input,
struct master_service_settings_output *output_r,