#include <sys_defs.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <mymalloc.h>
#include <msg.h>
#include <vstream.h>
#include <auto_clnt.h>
#include <netstring.h>
#include <split_at.h>
#include <stringops.h>
#include <htable.h>
#include <dict_sockmap.h>
typedef struct {
DICT dict;
char *sockmap_name;
VSTRING *rdwr_buf;
HTABLE_INFO *client_info;
} DICT_SOCKMAP;
#define DICT_SOCKMAP_DEF_TIMEOUT 100
#define DICT_SOCKMAP_DEF_MAX_REPLY 100000
#define DICT_SOCKMAP_DEF_MAX_IDLE 10
#define DICT_SOCKMAP_DEF_MAX_TTL 100
static int dict_sockmap_timeout = DICT_SOCKMAP_DEF_TIMEOUT;
static int dict_sockmap_max_reply = DICT_SOCKMAP_DEF_MAX_REPLY;
static int dict_sockmap_max_idle = DICT_SOCKMAP_DEF_MAX_IDLE;
static int dict_sockmap_max_ttl = DICT_SOCKMAP_DEF_MAX_TTL;
static HTABLE *dict_sockmap_handles;
typedef struct {
AUTO_CLNT *client_handle;
int refcount;
} DICT_SOCKMAP_REFC_HANDLE;
#define DICT_SOCKMAP_RH_NAME(ht) (ht)->key
#define DICT_SOCKMAP_RH_HANDLE(ht) \
((DICT_SOCKMAP_REFC_HANDLE *) (ht)->value)->client_handle
#define DICT_SOCKMAP_RH_REFCOUNT(ht) \
((DICT_SOCKMAP_REFC_HANDLE *) (ht)->value)->refcount
#define DICT_SOCKMAP_PROT_OK "OK"
#define DICT_SOCKMAP_PROT_NOTFOUND "NOTFOUND"
#define DICT_SOCKMAP_PROT_TEMP "TEMP"
#define DICT_SOCKMAP_PROT_TIMEOUT "TIMEOUT"
#define DICT_SOCKMAP_PROT_PERM "PERM"
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
static const char *dict_sockmap_lookup(DICT *dict, const char *key)
{
const char *myname = "dict_sockmap_lookup";
DICT_SOCKMAP *dp = (DICT_SOCKMAP *) dict;
AUTO_CLNT *sockmap_clnt = DICT_SOCKMAP_RH_HANDLE(dp->client_info);
VSTREAM *fp;
int netstring_err;
char *reply_payload;
int except_count;
const char *error_class;
if (msg_verbose)
msg_info("%s: key %s", myname, key);
if (dict->flags & DICT_FLAG_FOLD_MUL) {
if (dict->fold_buf == 0)
dict->fold_buf = vstring_alloc(100);
vstring_strcpy(dict->fold_buf, key);
key = lowercase(STR(dict->fold_buf));
}
for (except_count = 0; ; except_count++) {
if ((fp = auto_clnt_access(sockmap_clnt)) == 0) {
msg_warn("table %s:%s lookup error: %m", dict->type, dict->name);
dict->error = DICT_ERR_RETRY;
return (0);
}
netstring_setup(fp, dict_sockmap_timeout);
if ((netstring_err = vstream_setjmp(fp)) == 0) {
vstring_sprintf(dp->rdwr_buf, "%s %s", dp->sockmap_name, key);
NETSTRING_PUT_BUF(fp, dp->rdwr_buf);
netstring_get(fp, dp->rdwr_buf, dict_sockmap_max_reply);
break;
}
else {
if (except_count == 0 && netstring_err == NETSTRING_ERR_EOF
&& errno != ETIMEDOUT) {
auto_clnt_recover(sockmap_clnt);
continue;
}
else {
msg_warn("table %s:%s lookup error: %s",
dict->type, dict->name,
netstring_strerror(netstring_err));
dict->error = DICT_ERR_RETRY;
return (0);
}
}
}
VSTRING_TERMINATE(dp->rdwr_buf);
reply_payload = split_at(STR(dp->rdwr_buf), ' ');
if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_OK) == 0) {
dict->error = 0;
return (reply_payload);
} else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_NOTFOUND) == 0) {
dict->error = 0;
return (0);
}
if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_TEMP) == 0) {
error_class = "temporary";
dict->error = DICT_ERR_RETRY;
} else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_TIMEOUT) == 0) {
error_class = "timeout";
dict->error = DICT_ERR_RETRY;
} else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_PERM) == 0) {
error_class = "permanent";
dict->error = DICT_ERR_CONFIG;
} else {
error_class = "unknown";
dict->error = DICT_ERR_RETRY;
}
while (reply_payload && ISSPACE(*reply_payload))
reply_payload++;
msg_warn("%s:%s socketmap server %s error%s%.200s",
dict->type, dict->name, error_class,
reply_payload && *reply_payload ? ": " : "",
reply_payload && *reply_payload ?
printable(reply_payload, '?') : "");
return (0);
}
static void dict_sockmap_close(DICT *dict)
{
const char *myname = "dict_sockmap_close";
DICT_SOCKMAP *dp = (DICT_SOCKMAP *) dict;
if (dict_sockmap_handles == 0 || dict_sockmap_handles->used == 0)
msg_panic("%s: attempt to close a non-existent map", myname);
vstring_free(dp->rdwr_buf);
myfree(dp->sockmap_name);
if (--DICT_SOCKMAP_RH_REFCOUNT(dp->client_info) == 0) {
auto_clnt_free(DICT_SOCKMAP_RH_HANDLE(dp->client_info));
htable_delete(dict_sockmap_handles,
DICT_SOCKMAP_RH_NAME(dp->client_info), myfree);
}
if (dict->fold_buf)
vstring_free(dict->fold_buf);
dict_free(dict);
}
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;
#define DICT_SOCKMAP_OPEN_RETURN(d) do { \
DICT *__d = (d); \
if (saved_name != 0) \
myfree(saved_name); \
return (__d); \
} while (0)
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));
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));
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, (void *) ref_handle);
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;
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;
dp->dict.flags = dict_flags | DICT_FLAG_PATTERN;
DICT_SOCKMAP_OPEN_RETURN(DICT_DEBUG (&dp->dict));
}