Beispiel #1
0
int     been_here_check_fixed(BH_TABLE *dup_filter, const char *string)
{
    VSTRING *folded_string;
    const char *lookup_key;
    int     status;

    /*
     * Special processing: case insensitive lookup.
     */
    if (dup_filter->flags & BH_FLAG_FOLD) {
	folded_string = vstring_alloc(100);
	lookup_key = casefold(folded_string, string);
    } else {
	folded_string = 0;
	lookup_key = string;
    }

    /*
     * Do the duplicate check.
     */
    status = (htable_locate(dup_filter->table, lookup_key) != 0);
    if (msg_verbose)
	msg_info("been_here_check: %s: %d", string, status);

    /*
     * Cleanup.
     */
    if (folded_string)
	vstring_free(folded_string);

    return (status);
}
void    psc_free_session_state(PSC_STATE *state)
{
    const char *myname = "psc_free_session_state";
    HTABLE_INFO *ht;

    /*
     * Update the per-client session count.
     */
    if ((ht = htable_locate(psc_client_concurrency,
			    state->smtp_client_addr)) == 0)
	msg_panic("%s: unknown client address: %s",
		  myname, state->smtp_client_addr);
    if (--(ht->value) == 0)
	htable_delete(psc_client_concurrency, state->smtp_client_addr,
		      (void (*) (char *)) 0);

    if (state->smtp_client_stream != 0) {
	event_server_disconnect(state->smtp_client_stream);
	psc_check_queue_length--;
    }
    if (state->smtp_server_fd >= 0) {
	close(state->smtp_server_fd);
	psc_post_queue_length--;
    }
    if (state->send_buf != 0)
	state->send_buf = vstring_free(state->send_buf);
    myfree(state->smtp_client_addr);
    myfree(state->smtp_client_port);
    myfree(state->smtp_server_addr);
    myfree(state->smtp_server_port);
    if (state->dnsbl_reply)
	vstring_free(state->dnsbl_reply);
    if (state->helo_name)
	myfree(state->helo_name);
    if (state->sender)
	myfree(state->sender);
    if (state->cmd_buffer)
	vstring_free(state->cmd_buffer);
    if (state->expand_buf)
	vstring_free(state->expand_buf);
    myfree((char *) state);

    if (psc_check_queue_length < 0 || psc_post_queue_length < 0)
	msg_panic("bad queue length: check_queue=%d, post_queue=%d",
		  psc_check_queue_length, psc_post_queue_length);

    /*
     * Update the stress level.
     */
    if (psc_stress != 0
	&& psc_check_queue_length <= psc_lowat_check_queue_length) {
	psc_stress = 0;
	msg_info("leaving STRESS mode with %d connections",
		 psc_check_queue_length);
    }
}
Beispiel #3
0
static DICT *proxy_map_find(const char *map_type_name, int request_flags,
                            int *statp)
{
    DICT   *dict;

#define PROXY_COLON	DICT_TYPE_PROXY ":"
#define PROXY_COLON_LEN	(sizeof(PROXY_COLON) - 1)
#define READ_OPEN_FLAGS	O_RDONLY
#define WRITE_OPEN_FLAGS (O_RDWR | O_CREAT)

    /*
     * Canonicalize the map name. If the map is not on the approved list,
     * deny the request.
     */
#define PROXY_MAP_FIND_ERROR_RETURN(x)  { *statp = (x); return (0); }

    while (strncmp(map_type_name, PROXY_COLON, PROXY_COLON_LEN) == 0)
        map_type_name += PROXY_COLON_LEN;
    if (strchr(map_type_name, ':') == 0)
        PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_BAD);
    if (htable_locate(proxy_auth_maps, map_type_name) == 0) {
        msg_warn("request for unapproved table: \"%s\"", map_type_name);
        msg_warn("to approve this table for %s access, list %s:%s in %s:%s",
                 proxy_writer == 0 ? "read-only" : "read-write",
                 DICT_TYPE_PROXY, map_type_name, MAIN_CONF_FILE,
                 proxy_writer == 0 ? VAR_PROXY_READ_MAPS :
                 VAR_PROXY_WRITE_MAPS);
        PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_DENY);
    }

    /*
     * Open one instance of a map for each combination of name+flags.
     *
     * Assume that a map instance can be shared among clients with different
     * paranoia flag settings and with different map lookup flag settings.
     *
     * XXX The open() flags are passed implicitly, via the selection of the
     * service name. For a more sophisticated interface, appropriate subsets
     * of open() flags should be received directly from the client.
     */
    vstring_sprintf(map_type_name_flags, "%s:%s", map_type_name,
                    dict_flags_str(request_flags & DICT_FLAG_INST_MASK));
    if (msg_verbose)
        msg_info("proxy_map_find: %s", STR(map_type_name_flags));
    if ((dict = dict_handle(STR(map_type_name_flags))) == 0) {
        dict = dict_open(map_type_name, proxy_writer ?
                         WRITE_OPEN_FLAGS : READ_OPEN_FLAGS,
                         request_flags);
        if (dict == 0)
            msg_panic("proxy_map_find: dict_open null result");
        dict_register(STR(map_type_name_flags), dict);
    }
    dict->error = 0;
    return (dict);
}
Beispiel #4
0
int     tls_mgr_delete(const char *unused_type, const char *key)
{
    if (tls_cache == 0)
	return TLS_MGR_STAT_ERR;

    if (htable_locate(tls_cache, key)) {
	htable_delete(tls_cache, key, free_value);
	--cache_count;
    }
    return (TLS_MGR_STAT_OK);
}
PSC_STATE *psc_new_session_state(VSTREAM *stream,
				         const char *client_addr,
				         const char *client_port,
				         const char *server_addr,
				         const char *server_port)
{
    PSC_STATE *state;
    HTABLE_INFO *ht;

    state = (PSC_STATE *) mymalloc(sizeof(*state));
    PSC_INIT_TESTS(state);
    if ((state->smtp_client_stream = stream) != 0)
	psc_check_queue_length++;
    state->smtp_server_fd = (-1);
    state->smtp_client_addr = mystrdup(client_addr);
    state->smtp_client_port = mystrdup(client_port);
    state->smtp_server_addr = mystrdup(server_addr);
    state->smtp_server_port = mystrdup(server_port);
    state->send_buf = vstring_alloc(100);
    state->test_name = "TEST NAME HERE";
    state->dnsbl_reply = 0;
    state->final_reply = "421 4.3.2 Service currently unavailable\r\n";
    state->rcpt_reply = "450 4.3.2 Service currently unavailable\r\n";
    state->command_count = 0;
    state->protocol = MAIL_PROTO_SMTP;
    state->helo_name = 0;
    state->sender = 0;
    state->cmd_buffer = 0;
    state->read_state = 0;
    state->ehlo_discard_mask = 0;		/* XXX Should be ~0 */
    state->expand_buf = 0;
    state->where = PSC_SMTPD_CMD_CONNECT;

    /*
     * Update the stress level.
     */
    if (psc_stress == 0
	&& psc_check_queue_length >= psc_hiwat_check_queue_length) {
	psc_stress = 1;
	msg_info("entering STRESS mode with %d connections",
		 psc_check_queue_length);
    }

    /*
     * Update the per-client session count.
     */
    if ((ht = htable_locate(psc_client_concurrency, client_addr)) == 0)
	ht = htable_enter(psc_client_concurrency, client_addr, (char *) 0);
    ht->value += 1;
    state->client_concurrency = CAST_CHAR_PTR_TO_INT(ht->value);

    return (state);
}
Beispiel #6
0
NVTABLE_INFO *nvtable_update(NVTABLE * table, const char *key, const char *value)
{
    NVTABLE_INFO *ht;

    if ((ht = htable_locate(table, key)) != 0) {
	myfree(ht->value);
    } else {
	ht = htable_enter(table, key, (char *) 0);
    }
    ht->value = mystrdup(value);
    return (ht);
}
Beispiel #7
0
static void post_jail_init(char *service_name, char **unused_argv)
{
    const char *sep = ", \t\r\n";
    char   *saved_filter;
    char   *bp;
    char   *type_name;

    /*
     * Are we proxy writer?
     */
    if (strcmp(service_name, MAIL_SERVICE_PROXYWRITE) == 0)
        proxy_writer = 1;
    else if (strcmp(service_name, MAIL_SERVICE_PROXYMAP) != 0)
        msg_fatal("service name must be one of %s or %s",
                  MAIL_SERVICE_PROXYMAP, MAIL_SERVICE_PROXYMAP);

    /*
     * Pre-allocate buffers.
     */
    request = vstring_alloc(10);
    request_map = vstring_alloc(10);
    request_key = vstring_alloc(10);
    request_value = vstring_alloc(10);
    map_type_name_flags = vstring_alloc(10);

    /*
     * Prepare the pre-approved list of proxied tables.
     */
    saved_filter = bp = mystrdup(proxy_writer ? var_proxy_write_maps :
                                 var_proxy_read_maps);
    proxy_auth_maps = htable_create(13);
    while ((type_name = mystrtok(&bp, sep)) != 0) {
        if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN))
            continue;
        do {
            type_name += PROXY_COLON_LEN;
        } while (!strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN));
        if (strchr(type_name, ':') != 0
                && htable_locate(proxy_auth_maps, type_name) == 0)
            (void) htable_enter(proxy_auth_maps, type_name, (char *) 0);
    }
    myfree(saved_filter);

    /*
     * Never, ever, get killed by a master signal, as that could corrupt a
     * persistent database when we're in the middle of an update.
     */
    if (proxy_writer != 0)
        setsid();
}
Beispiel #8
0
int     delivered_hdr_find(DELIVERED_HDR_INFO *info, const char *address)
{
    HTABLE_INFO *ht;

    /*
     * mail_copy() uses quote_822_local() when writing the Delivered-To:
     * header. We must therefore apply the same transformation when looking
     * up the recipient. Lowercase the delivered-to address for consistency.
     */
    quote_822_local(info->buf, address);
    if (info->flags & FOLD_ADDR_ALL)
	fold_addr(STR(info->buf), info->flags);
    ht = htable_locate(info->table, STR(info->buf));
    return (ht != 0);
}
Beispiel #9
0
int     tls_mgr_update(const char *unused_type, const char *key,
		               const char *buf, ssize_t len)
{
    HTABLE_INFO *ent;
    VSTRING *s;

    if (tls_cache == 0)
	return TLS_MGR_STAT_ERR;

    if ((ent = htable_locate(tls_cache, key)) == 0) {
	s = vstring_alloc(len);
	ent = htable_enter(tls_cache, key, (char *) s);
    } else {
	s = (VSTRING *) ent->value;
    }
    vstring_memcpy(s, buf, len);

    ++cache_count;
    return (TLS_MGR_STAT_OK);
}
Beispiel #10
0
static void smtp_scrub_addr_list(HTABLE *cached_addr, DNS_RR **addr_list)
{
    MAI_HOSTADDR_STR hostaddr;
    DNS_RR *addr;
    DNS_RR *next;

    /*
     * XXX Extend the DNS_RR structure with fields for the printable address
     * and/or binary sockaddr representations, so that we can avoid repeated
     * binary->string transformations for the same address.
     */
    for (addr = *addr_list; addr; addr = next) {
	next = addr->next;
	if (dns_rr_to_pa(addr, &hostaddr) == 0) {
	    msg_warn("cannot convert type %s record to printable address",
		     dns_strtype(addr->type));
	    continue;
	}
	if (htable_locate(cached_addr, hostaddr.buf))
	    *addr_list = dns_rr_remove(*addr_list, addr);
    }
}
Beispiel #11
0
int     been_here_fixed(BH_TABLE *dup_filter, const char *string)
{
    char   *folded_string;
    const char *lookup_key;
    int     status;

    /*
     * Special processing: case insensitive lookup.
     */
    if (dup_filter->flags & BH_FLAG_FOLD) {
        folded_string = mystrdup(string);
        lookup_key = lowercase(folded_string);
    } else {
        folded_string = 0;
        lookup_key = string;
    }

    /*
     * Do the duplicate check.
     */
    if (htable_locate(dup_filter->table, lookup_key) != 0) {
        status = 1;
    } else {
        if (dup_filter->limit <= 0
                || dup_filter->limit > dup_filter->table->used)
            htable_enter(dup_filter->table, lookup_key, (char *) 0);
        status = 0;
    }
    if (msg_verbose)
        msg_info("been_here: %s: %d", string, status);

    /*
     * Cleanup.
     */
    if (folded_string)
        myfree(folded_string);

    return (status);
}
Beispiel #12
0
static int dict_ht_update(DICT *dict, const char *name, const char *value)
{
    DICT_HT *dict_ht = (DICT_HT *) dict;
    HTABLE_INFO *ht;
    char   *saved_value = mystrdup(value);

    /*
     * Optionally fold the key.
     */
    if (dict->flags & DICT_FLAG_FOLD_FIX) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(10);
	vstring_strcpy(dict->fold_buf, name);
	name = lowercase(vstring_str(dict->fold_buf));
    }
    if ((ht = htable_locate(dict_ht->table, name)) != 0) {
	myfree(ht->value);
    } else {
	ht = htable_enter(dict_ht->table, name, (char *) 0);
    }
    ht->value = saved_value;
    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
}
Beispiel #13
0
DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
{
    DICT_THASH *dict_thash;
    VSTREAM *fp = 0;
    struct stat st;
    time_t  before;
    time_t  after;
    VSTRING *line_buffer = 0;
    int     lineno;
    char   *key;
    char   *value;
    HTABLE *table;
    HTABLE_INFO *ht;

    /*
     * Let the optimizer worry about eliminating redundant code.
     */
#define DICT_THASH_OPEN_RETURN(d) { \
	DICT *__d = (d); \
	if (fp != 0) \
	    vstream_fclose(fp); \
	if (line_buffer != 0) \
	    vstring_free(line_buffer); \
	return (__d); \
    } while (0)

    /*
     * Sanity checks.
     */
    if (open_flags != O_RDONLY)
	DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
					      open_flags, dict_flags,
				  "%s:%s map requires O_RDONLY access mode",
					      DICT_TYPE_THASH, path));

    /*
     * Read the flat text file into in-memory hash. Read the file again if it
     * may have changed while we were reading.
     */
    for (before = time((time_t *) 0); /* see below */ ; before = after) {
	if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) {
	    DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
						  open_flags, dict_flags,
					     "open database %s: %m", path));
	}
	if (line_buffer == 0)
	    line_buffer = vstring_alloc(100);
	lineno = 0;
	table = htable_create(13);
	while (readlline(line_buffer, fp, &lineno)) {

	    /*
	     * Split on the first whitespace character, then trim leading and
	     * trailing whitespace from key and value.
	     */
	    key = STR(line_buffer);
	    value = key + strcspn(key, " \t\r\n");
	    if (*value)
		*value++ = 0;
	    while (ISSPACE(*value))
		value++;
	    trimblanks(key, 0)[0] = 0;
	    trimblanks(value, 0)[0] = 0;

	    /*
	     * Enforce the "key whitespace value" format. Disallow missing
	     * keys or missing values.
	     */
	    if (*key == 0 || *value == 0) {
		msg_warn("%s, line %d: expected format: key whitespace value"
			 " -- ignoring this line", path, lineno);
		continue;
	    }
	    if (key[strlen(key) - 1] == ':')
		msg_warn("%s, line %d: record is in \"key: value\" format;"
			 " is this an alias file?", path, lineno);

	    /*
	     * Optionally fold the key.
	     */
	    if (dict_flags & DICT_FLAG_FOLD_FIX)
		lowercase(key);

	    /*
	     * Store the value under the key. Handle duplicates
	     * appropriately.
	     */
	    if ((ht = htable_locate(table, key)) != 0) {
		if (dict_flags & DICT_FLAG_DUP_IGNORE) {
		     /* void */ ;
		} else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
		    myfree(ht->value);
		    ht->value = mystrdup(value);
		} else if (dict_flags & DICT_FLAG_DUP_WARN) {
		    msg_warn("%s, line %d: duplicate entry: \"%s\"",
			     path, lineno, key);
		} else {
		    msg_fatal("%s, line %d: duplicate entry: \"%s\"",
			      path, lineno, key);
		}
	    } else {
		htable_enter(table, key, mystrdup(value));
	    }
	}

	/*
	 * See if the source file is hot.
	 */
	if (fstat(vstream_fileno(fp), &st) < 0)
	    msg_fatal("fstat %s: %m", path);
	if (vstream_fclose(fp))
	    msg_fatal("read %s: %m", path);
	fp = 0;					/* DICT_THASH_OPEN_RETURN() */
	after = time((time_t *) 0);
	if (st.st_mtime < before - 1 || st.st_mtime > after)
	    break;

	/*
	 * Yes, it is hot. Discard the result and read the file again.
	 */
	htable_free(table, myfree);
	if (msg_verbose > 1)
	    msg_info("pausing to let file %s cool down", path);
	doze(300000);
    }

    /*
     * Create the in-memory table.
     */
    dict_thash = (DICT_THASH *)
	dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
    dict_thash->dict.lookup = dict_thash_lookup;
    dict_thash->dict.sequence = dict_thash_sequence;
    dict_thash->dict.close = dict_thash_close;
    dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
    if (dict_flags & DICT_FLAG_FOLD_FIX)
	dict_thash->dict.fold_buf = vstring_alloc(10);
    dict_thash->info = 0;
    dict_thash->table = table;
    dict_thash->dict.owner.uid = st.st_uid;
    dict_thash->dict.owner.status = (st.st_uid != 0);

    DICT_THASH_OPEN_RETURN(DICT_DEBUG (&dict_thash->dict));
}
Beispiel #14
0
int     psc_dnsbl_request(const char *client_addr,
			          void (*callback) (int, void *),
			          void *context)
{
    const char *myname = "psc_dnsbl_request";
    int     fd;
    VSTREAM *stream;
    HTABLE_INFO **ht;
    PSC_DNSBL_SCORE *score;
    HTABLE_INFO *hash_node;
    static int request_count;

    /*
     * Some spambots make several connections at nearly the same time,
     * causing their pregreet delays to overlap. Such connections can share
     * the efforts of DNSBL lookup.
     * 
     * We store a reference-counted DNSBL score under its client IP address. We
     * increment the reference count with each score request, and decrement
     * the reference count with each score retrieval.
     * 
     * Do not notify the requestor NOW when the DNS replies are already in.
     * Reason: we must not make a backwards call while we are still in the
     * middle of executing the corresponding forward call. Instead we create
     * a zero-delay timer request and call the notification function from
     * there.
     * 
     * psc_dnsbl_request() could instead return a result value to indicate that
     * the DNSBL score is already available, but that would complicate the
     * caller with two different notification code paths: one asynchronous
     * code path via the callback invocation, and one synchronous code path
     * via the psc_dnsbl_request() result value. That would be a source of
     * future bugs.
     */
    if ((hash_node = htable_locate(dnsbl_score_cache, client_addr)) != 0) {
	score = (PSC_DNSBL_SCORE *) hash_node->value;
	score->refcount += 1;
	PSC_CALL_BACK_EXTEND(hash_node, score);
	PSC_CALL_BACK_ENTER(score, callback, context);
	if (msg_verbose > 1)
	    msg_info("%s: reuse blocklist score for %s refcount=%d pending=%d",
		     myname, client_addr, score->refcount,
		     score->pending_lookups);
	if (score->pending_lookups == 0)
	    event_request_timer(callback, context, EVENT_NULL_DELAY);
	return (PSC_CALL_BACK_INDEX_OF_LAST(score));
    }
    if (msg_verbose > 1)
	msg_info("%s: create blocklist score for %s", myname, client_addr);
    score = (PSC_DNSBL_SCORE *) mymalloc(sizeof(*score));
    score->request_id = request_count++;
    score->dnsbl_name = 0;
    score->dnsbl_weight = 0;
    /* As with dnsblog(8), a value < 0 means no reply TTL. */
    score->pass_ttl = -1;
    score->fail_ttl = -1;
    score->total = 0;
    score->refcount = 1;
    score->pending_lookups = 0;
    PSC_CALL_BACK_INIT(score);
    PSC_CALL_BACK_ENTER(score, callback, context);
    (void) htable_enter(dnsbl_score_cache, client_addr, (void *) score);

    /*
     * Send a query to all DNSBL servers. Later, DNSBL lookup will be done
     * with an UDP-based DNS client that is built directly into Postfix code.
     * We therefore do not optimize the maximum out of this temporary
     * implementation.
     */
    for (ht = dnsbl_site_list; *ht; ht++) {
	if ((fd = LOCAL_CONNECT(psc_dnsbl_service, NON_BLOCKING, 1)) < 0) {
	    msg_warn("%s: connect to %s service: %m",
		     myname, psc_dnsbl_service);
	    continue;
	}
	stream = vstream_fdopen(fd, O_RDWR);
	vstream_control(stream,
			CA_VSTREAM_CTL_CONTEXT(ht[0]->key),
			CA_VSTREAM_CTL_END);
	attr_print(stream, ATTR_FLAG_NONE,
		   SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, ht[0]->key),
		   SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, client_addr),
		   SEND_ATTR_INT(MAIL_ATTR_LABEL, score->request_id),
		   ATTR_TYPE_END);
	if (vstream_fflush(stream) != 0) {
	    msg_warn("%s: error sending to %s service: %m",
		     myname, psc_dnsbl_service);
	    vstream_fclose(stream);
	    continue;
	}
	PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive,
			       (void *) stream, var_psc_dnsbl_tmout);
	score->pending_lookups += 1;
    }
    return (PSC_CALL_BACK_INDEX_OF_LAST(score));
}
Beispiel #15
0
DICT   *dict_sockmap_open(const char *mapname, int open_flags, int dict_flags)
{
    DICT_SOCKMAP *dp;
    char   *saved_name = 0;
    char   *sockmap;
    DICT_SOCKMAP_REFC_HANDLE *ref_handle;
    HTABLE_INFO *client_info;

    /*
     * Let the optimizer worry about eliminating redundant code.
     */
#define DICT_SOCKMAP_OPEN_RETURN(d) { \
	DICT *__d = (d); \
	if (saved_name != 0) \
	    myfree(saved_name); \
	return (__d); \
    } while (0)

    /*
     * Sanity checks.
     */
    if (open_flags != O_RDONLY)
	DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname,
					   open_flags, dict_flags,
				  "%s:%s map requires O_RDONLY access mode",
					   DICT_TYPE_SOCKMAP, mapname));
    if (dict_flags & DICT_FLAG_NO_UNAUTH)
	DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname,
					   open_flags, dict_flags,
		     "%s:%s map is not allowed for security-sensitive data",
					   DICT_TYPE_SOCKMAP, mapname));

    /*
     * Separate the socketmap name from the socketmap server name.
     */
    saved_name = mystrdup(mapname);
    if ((sockmap = split_at_right(saved_name, ':')) == 0)
	DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname,
					   open_flags, dict_flags,
				    "%s requires server:socketmap argument",
					   DICT_TYPE_SOCKMAP));

    /*
     * Use one reference-counted client handle for all socketmaps with the
     * same inet:host:port or unix:pathname information.
     * 
     * XXX Todo: graceful degradation after endpoint syntax error.
     */
    if (dict_sockmap_handles == 0)
	dict_sockmap_handles = htable_create(1);
    if ((client_info = htable_locate(dict_sockmap_handles, saved_name)) == 0) {
	ref_handle = (DICT_SOCKMAP_REFC_HANDLE *) mymalloc(sizeof(*ref_handle));
	client_info = htable_enter(dict_sockmap_handles,
				   saved_name, (char *) ref_handle);
	/* XXX Late initialization, so we can reuse macros for consistency. */
	DICT_SOCKMAP_RH_REFCOUNT(client_info) = 1;
	DICT_SOCKMAP_RH_HANDLE(client_info) =
	    auto_clnt_create(saved_name, dict_sockmap_timeout,
			     dict_sockmap_max_idle, dict_sockmap_max_ttl);
    } else
	DICT_SOCKMAP_RH_REFCOUNT(client_info) += 1;

    /*
     * Instantiate a socket map handle.
     */
    dp = (DICT_SOCKMAP *) dict_alloc(DICT_TYPE_SOCKMAP, mapname, sizeof(*dp));
    dp->rdwr_buf = vstring_alloc(100);
    dp->sockmap_name = mystrdup(sockmap);
    dp->client_info = client_info;
    dp->dict.lookup = dict_sockmap_lookup;
    dp->dict.close = dict_sockmap_close;
    /* Don't look up parent domains or network superblocks. */
    dp->dict.flags = dict_flags | DICT_FLAG_PATTERN;

    DICT_SOCKMAP_OPEN_RETURN(DICT_DEBUG (&dp->dict));
}