authd: rework module ID system

Provider ID's are now assigned dynamically at load-time. To accomodate
this, there is now a lookup system for finding providers by name (all
providers have names as well).
This commit is contained in:
Elizabeth Myers 2016-04-05 04:31:22 -05:00
parent 376ae2e2a7
commit 731d128990
8 changed files with 232 additions and 206 deletions

View file

@ -54,67 +54,21 @@
static EVH provider_timeout_event; static EVH provider_timeout_event;
rb_dlink_list auth_providers;
/* Clients waiting */
rb_dictionary *auth_clients; rb_dictionary *auth_clients;
rb_dictionary *auth_providers; /* Referenced by name */
static rb_dlink_list free_pids;
static uint32_t pid;
static struct ev_entry *timeout_ev; static struct ev_entry *timeout_ev;
/* Load a provider */
void
load_provider(struct auth_provider *provider)
{
if(rb_dlink_list_length(&auth_providers) >= MAX_PROVIDERS)
{
warn_opers(L_WARN, "provider: cannot load provider with id %d: maximum reached (%d)",
provider->id, MAX_PROVIDERS);
return;
}
if(provider->opt_handlers != NULL)
{
struct auth_opts_handler *handler;
for(handler = provider->opt_handlers; handler->option != NULL; handler++)
rb_dictionary_add(authd_option_handlers, handler->option, handler);
}
if(provider->stats_handler.letter != '\0')
authd_stat_handlers[provider->stats_handler.letter] = provider->stats_handler.handler;
if(provider->init != NULL)
provider->init();
rb_dlinkAddTail(provider, &provider->node, &auth_providers);
}
void
unload_provider(struct auth_provider *provider)
{
if(provider->opt_handlers != NULL)
{
struct auth_opts_handler *handler;
for(handler = provider->opt_handlers; handler->option != NULL; handler++)
rb_dictionary_delete(authd_option_handlers, handler->option);
}
if(provider->stats_handler.letter != '\0')
authd_stat_handlers[provider->stats_handler.letter] = NULL;
if(provider->destroy != NULL)
provider->destroy();
rb_dlinkDelete(&provider->node, &auth_providers);
}
/* Initalise all providers */ /* Initalise all providers */
void void
init_providers(void) init_providers(void)
{ {
auth_clients = rb_dictionary_create("pending auth clients", rb_uint32cmp); auth_clients = rb_dictionary_create("pending auth clients", rb_uint32cmp);
auth_providers = rb_dictionary_create("auth providers", strcmp);
timeout_ev = rb_event_addish("provider_timeout_event", provider_timeout_event, NULL, 1); timeout_ev = rb_event_addish("provider_timeout_event", provider_timeout_event, NULL, 1);
load_provider(&rdns_provider); load_provider(&rdns_provider);
load_provider(&ident_provider); load_provider(&ident_provider);
load_provider(&blacklist_provider); load_provider(&blacklist_provider);
@ -137,28 +91,81 @@ destroy_providers(void)
reject_client(auth, -1, "destroy", "Authentication system is down... try reconnecting in a few seconds"); reject_client(auth, -1, "destroy", "Authentication system is down... try reconnecting in a few seconds");
} }
RB_DLINK_FOREACH(ptr, auth_providers.head) RB_DICTIONARY_FOREACH(provider, &iter, auth_providers)
{ {
provider = ptr->data;
if(provider->destroy) if(provider->destroy)
provider->destroy(); provider->destroy();
} }
rb_dictionary_destroy(auth_clients, NULL, NULL);
rb_dictionary_destroy(auth_providers, NULL, NULL);
rb_event_delete(timeout_ev); rb_event_delete(timeout_ev);
} }
/* Load a provider */
void
load_provider(struct auth_provider *provider)
{
/* Assign a PID */
if(rb_dlink_list_length(&free_pids) > 0)
{
/* use the free list */
provider->id = RB_POINTER_TO_UINT(free_pids.head->data);
rb_dlinkDestroy(free_pids.head, &free_pids);
}
else
provider->id = pid++;
if(provider->opt_handlers != NULL)
{
struct auth_opts_handler *handler;
for(handler = provider->opt_handlers; handler->option != NULL; handler++)
rb_dictionary_add(authd_option_handlers, handler->option, handler);
}
if(provider->stats_handler.letter != '\0')
authd_stat_handlers[provider->stats_handler.letter] = provider->stats_handler.handler;
if(provider->init != NULL)
provider->init();
rb_dictionary_add(auth_providers, provider->name, provider);
}
void
unload_provider(struct auth_provider *provider)
{
if(provider->opt_handlers != NULL)
{
struct auth_opts_handler *handler;
for(handler = provider->opt_handlers; handler->option != NULL; handler++)
rb_dictionary_delete(authd_option_handlers, handler->option);
}
if(provider->stats_handler.letter != '\0')
authd_stat_handlers[provider->stats_handler.letter] = NULL;
if(provider->destroy != NULL)
provider->destroy();
rb_dictionary_delete(auth_providers, provider->name);
/* Reclaim ID */
rb_dlinkAddAlloc(RB_UINT_TO_POINTER(provider->id), &free_pids);
}
/* Cancel outstanding providers for a client */ /* Cancel outstanding providers for a client */
void void
cancel_providers(struct auth_client *auth) cancel_providers(struct auth_client *auth)
{ {
rb_dlink_node *ptr; rb_dictionary_iter iter;
struct auth_provider *provider; struct auth_provider *provider;
RB_DLINK_FOREACH(ptr, auth_providers.head) RB_DICTIONARY_FOREACH(provider, &iter, auth_providers)
{ {
provider = ptr->data;
if(provider->cancel && is_provider_running(auth, provider->id)) if(provider->cancel && is_provider_running(auth, provider->id))
/* Cancel if required */ /* Cancel if required */
provider->cancel(auth); provider->cancel(auth);
@ -171,9 +178,9 @@ cancel_providers(struct auth_client *auth)
/* Provider is done - WARNING: do not use auth instance after calling! */ /* Provider is done - WARNING: do not use auth instance after calling! */
void void
provider_done(struct auth_client *auth, provider_t id) provider_done(struct auth_client *auth, uint32_t id)
{ {
rb_dlink_node *ptr; rb_dictionary_iter iter;
struct auth_provider *provider; struct auth_provider *provider;
set_provider_done(auth, id); set_provider_done(auth, id);
@ -186,10 +193,8 @@ provider_done(struct auth_client *auth, provider_t id)
return; return;
} }
RB_DLINK_FOREACH(ptr, auth_providers.head) RB_DICTIONARY_FOREACH(provider, &iter, auth_providers)
{ {
provider = ptr->data;
if(provider->completed != NULL && is_provider_running(auth, provider->id)) if(provider->completed != NULL && is_provider_running(auth, provider->id))
/* Notify pending clients who asked for it */ /* Notify pending clients who asked for it */
provider->completed(auth, id); provider->completed(auth, id);
@ -198,31 +203,11 @@ provider_done(struct auth_client *auth, provider_t id)
/* Reject a client - WARNING: do not use auth instance after calling! */ /* Reject a client - WARNING: do not use auth instance after calling! */
void void
reject_client(struct auth_client *auth, provider_t id, const char *data, const char *fmt, ...) reject_client(struct auth_client *auth, uint32_t id, const char *data, const char *fmt, ...)
{ {
char reject;
char buf[BUFSIZE]; char buf[BUFSIZE];
va_list args; va_list args;
switch(id)
{
case PROVIDER_RDNS:
reject = 'D';
break;
case PROVIDER_IDENT:
reject = 'I';
break;
case PROVIDER_BLACKLIST:
reject = 'B';
break;
case PROVIDER_OPM:
reject = 'O';
break;
default:
reject = 'N';
break;
}
if(data == NULL) if(data == NULL)
data = "*"; data = "*";
@ -234,7 +219,10 @@ reject_client(struct auth_client *auth, provider_t id, const char *data, const c
* In the future this may not be the case. * In the future this may not be the case.
* --Elizafox * --Elizafox
*/ */
rb_helper_write(authd_helper, "R %x %c %s %s %s :%s", auth->cid, reject, auth->username, auth->hostname, data, buf); rb_helper_write(authd_helper, "R %x %c %s %s %s :%s",
auth->cid, auth->data[id].provider->letter,
auth->username, auth->hostname,
data, buf);
set_provider_done(auth, id); set_provider_done(auth, id);
cancel_providers(auth); cancel_providers(auth);
@ -242,7 +230,7 @@ reject_client(struct auth_client *auth, provider_t id, const char *data, const c
/* Accept a client, cancel outstanding providers if any - WARNING: do nto use auth instance after calling! */ /* Accept a client, cancel outstanding providers if any - WARNING: do nto use auth instance after calling! */
void void
accept_client(struct auth_client *auth, provider_t id) accept_client(struct auth_client *auth, uint32_t id)
{ {
rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname); rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname);
@ -257,7 +245,7 @@ start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_
struct auth_provider *provider; struct auth_provider *provider;
struct auth_client *auth = rb_malloc(sizeof(struct auth_client)); struct auth_client *auth = rb_malloc(sizeof(struct auth_client));
long lcid = strtol(cid, NULL, 16); long lcid = strtol(cid, NULL, 16);
rb_dlink_node *ptr; rb_dictionary_iter iter;
if(lcid >= UINT32_MAX) if(lcid >= UINT32_MAX)
return; return;
@ -285,13 +273,13 @@ start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_
rb_strlcpy(auth->hostname, "*", sizeof(auth->hostname)); rb_strlcpy(auth->hostname, "*", sizeof(auth->hostname));
rb_strlcpy(auth->username, "*", sizeof(auth->username)); rb_strlcpy(auth->username, "*", sizeof(auth->username));
auth->data = rb_malloc(rb_dlink_list_length(&auth_providers) * auth->data = rb_malloc(rb_dictionary_size(auth_providers) *
sizeof(struct auth_client_data)); sizeof(struct auth_client_data));
auth->providers_starting = true; auth->providers_starting = true;
RB_DLINK_FOREACH(ptr, auth_providers.head) RB_DICTIONARY_FOREACH(provider, &iter, auth_providers)
{ {
provider = ptr->data; auth->data[provider->id].provider = provider;
lrb_assert(provider->start != NULL); lrb_assert(provider->start != NULL);
@ -360,11 +348,11 @@ provider_timeout_event(void *notused __unused)
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{ {
rb_dlink_node *ptr; rb_dictionary_iter iter2;
struct auth_provider *provider;
RB_DLINK_FOREACH(ptr, auth_providers.head) RB_DICTIONARY_FOREACH(provider, &iter2, auth_providers)
{ {
struct auth_provider *provider = ptr->data;
const time_t timeout = get_provider_timeout(auth, provider->id); const time_t timeout = get_provider_timeout(auth, provider->id);
if(is_provider_running(auth, provider->id) && provider->timeout != NULL && if(is_provider_running(auth, provider->id) && provider->timeout != NULL &&

View file

@ -27,15 +27,6 @@
#define MAX_PROVIDERS 32 /* This should be enough */ #define MAX_PROVIDERS 32 /* This should be enough */
/* Registered providers */
typedef enum
{
PROVIDER_RDNS,
PROVIDER_IDENT,
PROVIDER_BLACKLIST,
PROVIDER_OPM,
} provider_t;
typedef enum typedef enum
{ {
PROVIDER_STATUS_NOTRUN = 0, PROVIDER_STATUS_NOTRUN = 0,
@ -45,6 +36,7 @@ typedef enum
struct auth_client_data struct auth_client_data
{ {
struct auth_provider *provider; /* Pointer back */
time_t timeout; /* Provider timeout */ time_t timeout; /* Provider timeout */
void *data; /* Provider data */ void *data; /* Provider data */
provider_status_t status; /* Provider status */ provider_status_t status; /* Provider status */
@ -76,8 +68,8 @@ typedef void (*provider_destroy_t)(void);
typedef bool (*provider_start_t)(struct auth_client *); typedef bool (*provider_start_t)(struct auth_client *);
typedef void (*provider_cancel_t)(struct auth_client *); typedef void (*provider_cancel_t)(struct auth_client *);
typedef void (*provider_timeout_t)(struct auth_client *); typedef void (*uint32_timeout_t)(struct auth_client *);
typedef void (*provider_complete_t)(struct auth_client *, provider_t); typedef void (*provider_complete_t)(struct auth_client *, uint32_t);
struct auth_stats_handler struct auth_stats_handler
{ {
@ -89,14 +81,17 @@ struct auth_provider
{ {
rb_dlink_node node; rb_dlink_node node;
provider_t id; uint32_t id; /* Provider ID */
const char *name; /* Name of the provider */
char letter; /* Letter used on reject, etc. */
provider_init_t init; /* Initalise the provider */ provider_init_t init; /* Initalise the provider */
provider_destroy_t destroy; /* Terminate the provider */ provider_destroy_t destroy; /* Terminate the provider */
provider_start_t start; /* Perform authentication */ provider_start_t start; /* Perform authentication */
provider_cancel_t cancel; /* Authentication cancelled */ provider_cancel_t cancel; /* Authentication cancelled */
provider_timeout_t timeout; /* Timeout callback */ uint32_timeout_t timeout; /* Timeout callback */
provider_complete_t completed; /* Callback for when other performers complete (think dependency chains) */ provider_complete_t completed; /* Callback for when other performers complete (think dependency chains) */
struct auth_stats_handler stats_handler; struct auth_stats_handler stats_handler;
@ -109,7 +104,7 @@ extern struct auth_provider ident_provider;
extern struct auth_provider blacklist_provider; extern struct auth_provider blacklist_provider;
extern struct auth_provider opm_provider; extern struct auth_provider opm_provider;
extern rb_dlink_list auth_providers; extern rb_dictionary *auth_providers;
extern rb_dictionary *auth_clients; extern rb_dictionary *auth_clients;
void load_provider(struct auth_provider *provider); void load_provider(struct auth_provider *provider);
@ -119,24 +114,46 @@ void init_providers(void);
void destroy_providers(void); void destroy_providers(void);
void cancel_providers(struct auth_client *auth); void cancel_providers(struct auth_client *auth);
void provider_done(struct auth_client *auth, provider_t id); void provider_done(struct auth_client *auth, uint32_t id);
void accept_client(struct auth_client *auth, provider_t id); void accept_client(struct auth_client *auth, uint32_t id);
void reject_client(struct auth_client *auth, provider_t id, const char *data, const char *fmt, ...); void reject_client(struct auth_client *auth, uint32_t id, const char *data, const char *fmt, ...);
void handle_new_connection(int parc, char *parv[]); void handle_new_connection(int parc, char *parv[]);
void handle_cancel_connection(int parc, char *parv[]); void handle_cancel_connection(int parc, char *parv[]);
/* Get a provider by name */
static inline struct auth_provider *
get_provider(const char *name)
{
return rb_dictionary_retrieve(auth_providers, name);
}
/* Get a provider's id by name */
static inline bool
get_provider_id(const char *name, int *id)
{
struct auth_provider *provider = get_provider(name);
if(provider != NULL)
{
*id = provider->id;
return true;
}
else
return false;
}
/* Get a provider's raw status */ /* Get a provider's raw status */
static inline provider_status_t static inline provider_status_t
get_provider_status(struct auth_client *auth, provider_t provider) get_provider_status(struct auth_client *auth, uint32_t provider)
{ {
return auth->data[provider].status; return auth->data[provider].status;
} }
/* Set a provider's raw status */ /* Set a provider's raw status */
static inline void static inline void
set_provider_status(struct auth_client *auth, provider_t provider, provider_status_t status) set_provider_status(struct auth_client *auth, uint32_t provider, provider_status_t status)
{ {
auth->data[provider].status = status; auth->data[provider].status = status;
} }
@ -144,7 +161,7 @@ set_provider_status(struct auth_client *auth, provider_t provider, provider_stat
/* Set the provider as running /* Set the provider as running
* If you're doing asynchronous work call this */ * If you're doing asynchronous work call this */
static inline void static inline void
set_provider_running(struct auth_client *auth, provider_t provider) set_provider_running(struct auth_client *auth, uint32_t provider)
{ {
auth->refcount++; auth->refcount++;
set_provider_status(auth, provider, PROVIDER_STATUS_RUNNING); set_provider_status(auth, provider, PROVIDER_STATUS_RUNNING);
@ -153,7 +170,7 @@ set_provider_running(struct auth_client *auth, provider_t provider)
/* Provider is no longer operating on this auth client /* Provider is no longer operating on this auth client
* You should use provider_done and not this */ * You should use provider_done and not this */
static inline void static inline void
set_provider_done(struct auth_client *auth, provider_t provider) set_provider_done(struct auth_client *auth, uint32_t provider)
{ {
auth->refcount--; auth->refcount--;
set_provider_status(auth, provider, PROVIDER_STATUS_DONE); set_provider_status(auth, provider, PROVIDER_STATUS_DONE);
@ -161,14 +178,14 @@ set_provider_done(struct auth_client *auth, provider_t provider)
/* Check if provider is operating on this auth client */ /* Check if provider is operating on this auth client */
static inline bool static inline bool
is_provider_running(struct auth_client *auth, provider_t provider) is_provider_running(struct auth_client *auth, uint32_t provider)
{ {
return get_provider_status(auth, provider) == PROVIDER_STATUS_RUNNING; return get_provider_status(auth, provider) == PROVIDER_STATUS_RUNNING;
} }
/* Check if provider has finished on this client */ /* Check if provider has finished on this client */
static inline bool static inline bool
is_provider_done(struct auth_client *auth, provider_t provider) is_provider_done(struct auth_client *auth, uint32_t provider)
{ {
return get_provider_status(auth, provider) == PROVIDER_STATUS_DONE; return get_provider_status(auth, provider) == PROVIDER_STATUS_DONE;
} }
@ -177,7 +194,6 @@ is_provider_done(struct auth_client *auth, provider_t provider)
static inline void * static inline void *
get_provider_data(struct auth_client *auth, uint32_t id) get_provider_data(struct auth_client *auth, uint32_t id)
{ {
lrb_assert(id < rb_dlink_list_length(&auth_providers));
return auth->data[id].data; return auth->data[id].data;
} }
@ -185,7 +201,6 @@ get_provider_data(struct auth_client *auth, uint32_t id)
static inline void static inline void
set_provider_data(struct auth_client *auth, uint32_t id, void *data) set_provider_data(struct auth_client *auth, uint32_t id, void *data)
{ {
lrb_assert(id < rb_dlink_list_length(&auth_providers));
auth->data[id].data = data; auth->data[id].data = data;
} }
@ -194,7 +209,6 @@ set_provider_data(struct auth_client *auth, uint32_t id, void *data)
static inline void static inline void
set_provider_timeout_relative(struct auth_client *auth, uint32_t id, time_t timeout) set_provider_timeout_relative(struct auth_client *auth, uint32_t id, time_t timeout)
{ {
lrb_assert(id < rb_dlink_list_length(&auth_providers));
auth->data[id].timeout = timeout + rb_current_time(); auth->data[id].timeout = timeout + rb_current_time();
} }
@ -203,7 +217,6 @@ set_provider_timeout_relative(struct auth_client *auth, uint32_t id, time_t time
static inline void static inline void
set_provider_timeout_absolute(struct auth_client *auth, uint32_t id, time_t timeout) set_provider_timeout_absolute(struct auth_client *auth, uint32_t id, time_t timeout)
{ {
lrb_assert(id < rb_dlink_list_length(&auth_providers));
auth->data[id].timeout = timeout; auth->data[id].timeout = timeout;
} }
@ -211,7 +224,6 @@ set_provider_timeout_absolute(struct auth_client *auth, uint32_t id, time_t time
static inline time_t static inline time_t
get_provider_timeout(struct auth_client *auth, uint32_t id) get_provider_timeout(struct auth_client *auth, uint32_t id)
{ {
lrb_assert(id < rb_dlink_list_length(&auth_providers));
return auth->data[id].timeout; return auth->data[id].timeout;
} }

View file

@ -43,6 +43,8 @@
#include "stdinc.h" #include "stdinc.h"
#include "dns.h" #include "dns.h"
#define SELF_PID (blacklist_provider.id)
typedef enum filter_t typedef enum filter_t
{ {
FILTER_ALL = 1, FILTER_ALL = 1,
@ -237,7 +239,7 @@ blacklist_dns_callback(const char *result, bool status, query_type type, void *d
bl = bllookup->bl; bl = bllookup->bl;
auth = bllookup->auth; auth = bllookup->auth;
if((bluser = get_provider_data(auth, PROVIDER_BLACKLIST)) == NULL) if((bluser = get_provider_data(auth, SELF_PID)) == NULL)
return; return;
if (result != NULL && status && blacklist_check_reply(bllookup, result)) if (result != NULL && status && blacklist_check_reply(bllookup, result))
@ -245,7 +247,7 @@ blacklist_dns_callback(const char *result, bool status, query_type type, void *d
/* Match found, so proceed no further */ /* Match found, so proceed no further */
bl->hits++; bl->hits++;
blacklists_cancel(auth); blacklists_cancel(auth);
reject_client(auth, PROVIDER_BLACKLIST, bl->host, bl->reason); reject_client(auth, SELF_PID, bl->host, bl->reason);
return; return;
} }
@ -260,9 +262,9 @@ blacklist_dns_callback(const char *result, bool status, query_type type, void *d
notice_client(auth->cid, "*** IP not found in DNS blacklist%s", notice_client(auth->cid, "*** IP not found in DNS blacklist%s",
rb_dlink_list_length(&blacklist_list) > 1 ? "s" : ""); rb_dlink_list_length(&blacklist_list) > 1 ? "s" : "");
rb_free(bluser); rb_free(bluser);
set_provider_data(auth, PROVIDER_BLACKLIST, NULL); set_provider_data(auth, SELF_PID, NULL);
set_provider_timeout_absolute(auth, PROVIDER_BLACKLIST, 0); set_provider_timeout_absolute(auth, SELF_PID, 0);
provider_done(auth, PROVIDER_BLACKLIST); provider_done(auth, SELF_PID);
} }
} }
@ -270,7 +272,7 @@ static void
initiate_blacklist_dnsquery(struct blacklist *bl, struct auth_client *auth) initiate_blacklist_dnsquery(struct blacklist *bl, struct auth_client *auth)
{ {
struct blacklist_lookup *bllookup = rb_malloc(sizeof(struct blacklist_lookup)); struct blacklist_lookup *bllookup = rb_malloc(sizeof(struct blacklist_lookup));
struct blacklist_user *bluser = get_provider_data(auth, PROVIDER_BLACKLIST); struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
char buf[IRCD_RES_HOSTLEN + 1]; char buf[IRCD_RES_HOSTLEN + 1];
int aftype; int aftype;
@ -297,7 +299,7 @@ initiate_blacklist_dnsquery(struct blacklist *bl, struct auth_client *auth)
static inline void static inline void
lookup_all_blacklists(struct auth_client *auth) lookup_all_blacklists(struct auth_client *auth)
{ {
struct blacklist_user *bluser = get_provider_data(auth, PROVIDER_BLACKLIST); struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
rb_dlink_node *ptr; rb_dlink_node *ptr;
notice_client(auth->cid, "*** Checking your IP against DNS blacklist%s", notice_client(auth->cid, "*** Checking your IP against DNS blacklist%s",
@ -311,7 +313,7 @@ lookup_all_blacklists(struct auth_client *auth)
initiate_blacklist_dnsquery(bl, auth); initiate_blacklist_dnsquery(bl, auth);
} }
set_provider_timeout_relative(auth, PROVIDER_BLACKLIST, blacklist_timeout); set_provider_timeout_relative(auth, SELF_PID, blacklist_timeout);
} }
static inline void static inline void
@ -341,7 +343,9 @@ delete_all_blacklists(void)
static bool static bool
blacklists_start(struct auth_client *auth) blacklists_start(struct auth_client *auth)
{ {
lrb_assert(get_provider_data(auth, PROVIDER_BLACKLIST) == NULL); uint32_t rdns_pid, ident_pid;
lrb_assert(get_provider_data(auth, SELF_PID) == NULL);
if(!rb_dlink_list_length(&blacklist_list)) if(!rb_dlink_list_length(&blacklist_list))
{ {
@ -350,31 +354,36 @@ blacklists_start(struct auth_client *auth)
return true; return true;
} }
set_provider_data(auth, PROVIDER_BLACKLIST, rb_malloc(sizeof(struct blacklist_user))); set_provider_data(auth, SELF_PID, rb_malloc(sizeof(struct blacklist_user)));
if(is_provider_done(auth, PROVIDER_RDNS) && is_provider_done(auth, PROVIDER_IDENT)) if((!get_provider_id("rdns", &rdns_pid) || is_provider_done(auth, rdns_pid)) &&
/* This probably can't happen but let's handle this case anyway */ (!get_provider_id("ident", &ident_pid) || is_provider_done(auth, ident_pid)))
{
/* Start the lookup if ident and rdns are finished, or not loaded. */
lookup_all_blacklists(auth); lookup_all_blacklists(auth);
}
set_provider_running(auth, PROVIDER_BLACKLIST); set_provider_running(auth, SELF_PID);
return true; return true;
} }
/* This is called every time a provider is completed as long as we are marked not done */ /* This is called every time a provider is completed as long as we are marked not done */
static void static void
blacklists_initiate(struct auth_client *auth, provider_t provider) blacklists_initiate(struct auth_client *auth, uint32_t provider)
{ {
struct blacklist_user *bluser = get_provider_data(auth, PROVIDER_BLACKLIST); struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
uint32_t rdns_pid, ident_pid;
lrb_assert(provider != PROVIDER_BLACKLIST); lrb_assert(provider != SELF_PID);
lrb_assert(!is_provider_done(auth, PROVIDER_BLACKLIST)); lrb_assert(!is_provider_done(auth, SELF_PID));
lrb_assert(rb_dlink_list_length(&blacklist_list) > 0); lrb_assert(rb_dlink_list_length(&blacklist_list) > 0);
if(bluser == NULL || rb_dlink_list_length(&bluser->queries)) if(bluser == NULL || rb_dlink_list_length(&bluser->queries))
/* Nothing to do */ /* Nothing to do */
return; return;
else if(!(is_provider_done(auth, PROVIDER_RDNS) && is_provider_done(auth, PROVIDER_IDENT))) else if((!get_provider_id("rdns", &rdns_pid) || is_provider_done(auth, rdns_pid)) &&
/* Don't start until we've completed these */ (!get_provider_id("ident", &ident_pid) || is_provider_done(auth, ident_pid)))
/* Don't start until ident and rdns are finished (or not loaded) */
return; return;
else else
lookup_all_blacklists(auth); lookup_all_blacklists(auth);
@ -384,7 +393,7 @@ static void
blacklists_cancel(struct auth_client *auth) blacklists_cancel(struct auth_client *auth)
{ {
rb_dlink_node *ptr, *nptr; rb_dlink_node *ptr, *nptr;
struct blacklist_user *bluser = get_provider_data(auth, PROVIDER_BLACKLIST); struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
if(bluser == NULL) if(bluser == NULL)
return; return;
@ -406,9 +415,9 @@ blacklists_cancel(struct auth_client *auth)
} }
rb_free(bluser); rb_free(bluser);
set_provider_data(auth, PROVIDER_BLACKLIST, NULL); set_provider_data(auth, SELF_PID, NULL);
set_provider_timeout_absolute(auth, PROVIDER_BLACKLIST, 0); set_provider_timeout_absolute(auth, SELF_PID, 0);
provider_done(auth, PROVIDER_BLACKLIST); provider_done(auth, SELF_PID);
} }
static void static void
@ -549,7 +558,8 @@ struct auth_opts_handler blacklist_options[] =
struct auth_provider blacklist_provider = struct auth_provider blacklist_provider =
{ {
.id = PROVIDER_BLACKLIST, .name = "blacklist",
.letter = 'B',
.destroy = blacklists_destroy, .destroy = blacklists_destroy,
.start = blacklists_start, .start = blacklists_start,
.cancel = blacklists_cancel, .cancel = blacklists_cancel,

View file

@ -31,6 +31,8 @@
#include "provider.h" #include "provider.h"
#include "res.h" #include "res.h"
#define SELF_PID (ident_provider.id)
#define IDENT_BUFSIZE 128 #define IDENT_BUFSIZE 128
struct ident_query struct ident_query
@ -88,7 +90,7 @@ ident_connected(rb_fde_t *F __unused, int error, void *data)
int authlen; int authlen;
lrb_assert(auth != NULL); lrb_assert(auth != NULL);
query = get_provider_data(auth, PROVIDER_IDENT); query = get_provider_data(auth, SELF_PID);
lrb_assert(query != NULL); lrb_assert(query != NULL);
/* Check the error */ /* Check the error */
@ -125,7 +127,7 @@ read_ident_reply(rb_fde_t *F, void *data)
int count; int count;
lrb_assert(auth != NULL); lrb_assert(auth != NULL);
query = get_provider_data(auth, PROVIDER_IDENT); query = get_provider_data(auth, SELF_PID);
lrb_assert(query != NULL); lrb_assert(query != NULL);
len = rb_read(F, buf, IDENT_BUFSIZE); len = rb_read(F, buf, IDENT_BUFSIZE);
@ -170,7 +172,7 @@ read_ident_reply(rb_fde_t *F, void *data)
static void static void
client_fail(struct auth_client *auth, ident_message report) client_fail(struct auth_client *auth, ident_message report)
{ {
struct ident_query *query = get_provider_data(auth, PROVIDER_IDENT); struct ident_query *query = get_provider_data(auth, SELF_PID);
lrb_assert(query != NULL); lrb_assert(query != NULL);
@ -180,17 +182,17 @@ client_fail(struct auth_client *auth, ident_message report)
rb_close(query->F); rb_close(query->F);
rb_free(query); rb_free(query);
set_provider_data(auth, PROVIDER_IDENT, NULL); set_provider_data(auth, SELF_PID, NULL);
set_provider_timeout_absolute(auth, PROVIDER_IDENT, 0); set_provider_timeout_absolute(auth, SELF_PID, 0);
notice_client(auth->cid, messages[report]); notice_client(auth->cid, messages[report]);
provider_done(auth, PROVIDER_IDENT); provider_done(auth, SELF_PID);
} }
static void static void
client_success(struct auth_client *auth) client_success(struct auth_client *auth)
{ {
struct ident_query *query = get_provider_data(auth, PROVIDER_IDENT); struct ident_query *query = get_provider_data(auth, SELF_PID);
lrb_assert(query != NULL); lrb_assert(query != NULL);
@ -198,11 +200,11 @@ client_success(struct auth_client *auth)
rb_close(query->F); rb_close(query->F);
rb_free(query); rb_free(query);
set_provider_data(auth, PROVIDER_IDENT, NULL); set_provider_data(auth, SELF_PID, NULL);
set_provider_timeout_absolute(auth, PROVIDER_IDENT, 0); set_provider_timeout_absolute(auth, SELF_PID, 0);
notice_client(auth->cid, messages[REPORT_FOUND]); notice_client(auth->cid, messages[REPORT_FOUND]);
provider_done(auth, PROVIDER_IDENT); provider_done(auth, SELF_PID);
} }
/* get_valid_ident /* get_valid_ident
@ -278,7 +280,7 @@ ident_destroy(void)
/* Nuke all ident queries */ /* Nuke all ident queries */
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{ {
if(get_provider_data(auth, PROVIDER_IDENT) != NULL) if(get_provider_data(auth, SELF_PID) != NULL)
client_fail(auth, REPORT_FAIL); client_fail(auth, REPORT_FAIL);
} }
} }
@ -290,20 +292,20 @@ ident_start(struct auth_client *auth)
struct rb_sockaddr_storage l_addr, c_addr; struct rb_sockaddr_storage l_addr, c_addr;
int family = GET_SS_FAMILY(&auth->c_addr); int family = GET_SS_FAMILY(&auth->c_addr);
lrb_assert(get_provider_data(auth, PROVIDER_IDENT) == NULL); lrb_assert(get_provider_data(auth, SELF_PID) == NULL);
if(!ident_enable) if(!ident_enable)
{ {
rb_free(query); rb_free(query);
notice_client(auth->cid, messages[REPORT_DISABLED]); notice_client(auth->cid, messages[REPORT_DISABLED]);
set_provider_done(auth, PROVIDER_IDENT); set_provider_done(auth, SELF_PID);
return true; return true;
} }
notice_client(auth->cid, messages[REPORT_LOOKUP]); notice_client(auth->cid, messages[REPORT_LOOKUP]);
set_provider_data(auth, PROVIDER_IDENT, query); set_provider_data(auth, SELF_PID, query);
set_provider_timeout_relative(auth, PROVIDER_IDENT, ident_timeout); set_provider_timeout_relative(auth, SELF_PID, ident_timeout);
if((query->F = rb_socket(family, SOCK_STREAM, 0, "ident")) == NULL) if((query->F = rb_socket(family, SOCK_STREAM, 0, "ident")) == NULL)
{ {
@ -324,7 +326,7 @@ ident_start(struct auth_client *auth)
GET_SS_LEN(&l_addr), ident_connected, GET_SS_LEN(&l_addr), ident_connected,
auth, ident_timeout); auth, ident_timeout);
set_provider_running(auth, PROVIDER_IDENT); set_provider_running(auth, SELF_PID);
return true; return true;
} }
@ -332,7 +334,7 @@ ident_start(struct auth_client *auth)
static void static void
ident_cancel(struct auth_client *auth) ident_cancel(struct auth_client *auth)
{ {
struct ident_query *query = get_provider_data(auth, PROVIDER_IDENT); struct ident_query *query = get_provider_data(auth, SELF_PID);
if(query != NULL) if(query != NULL)
client_fail(auth, REPORT_FAIL); client_fail(auth, REPORT_FAIL);
@ -368,7 +370,8 @@ struct auth_opts_handler ident_options[] =
struct auth_provider ident_provider = struct auth_provider ident_provider =
{ {
.id = PROVIDER_IDENT, .name = "ident",
.letter = 'I',
.start = ident_start, .start = ident_start,
.destroy = ident_destroy, .destroy = ident_destroy,
.cancel = ident_cancel, .cancel = ident_cancel,

View file

@ -25,6 +25,8 @@
#include "notice.h" #include "notice.h"
#include "provider.h" #include "provider.h"
#define SELF_PID (opm_provider.id)
#define OPM_READSIZE 128 #define OPM_READSIZE 128
typedef enum protocol_t typedef enum protocol_t
@ -133,7 +135,7 @@ read_opm_reply(rb_fde_t *F, void *data)
ssize_t len; ssize_t len;
lrb_assert(auth != NULL); lrb_assert(auth != NULL);
lookup = get_provider_data(auth, PROVIDER_OPM); lookup = get_provider_data(auth, SELF_PID);
lrb_assert(lookup != NULL); lrb_assert(lookup != NULL);
if((len = rb_read(F, readbuf, sizeof(readbuf))) < 0 && rb_ignore_errno(errno)) if((len = rb_read(F, readbuf, sizeof(readbuf))) < 0 && rb_ignore_errno(errno))
@ -168,7 +170,7 @@ read_opm_reply(rb_fde_t *F, void *data)
/* No longer needed, client is going away */ /* No longer needed, client is going away */
rb_free(lookup); rb_free(lookup);
reject_client(auth, PROVIDER_OPM, readbuf, "Open proxy detected"); reject_client(auth, SELF_PID, readbuf, "Open proxy detected");
break; break;
} }
} }
@ -260,7 +262,7 @@ socks4_connected(rb_fde_t *F, int error, void *data)
lrb_assert(scan != NULL); lrb_assert(scan != NULL);
auth = scan->auth; auth = scan->auth;
lookup = get_provider_data(auth, PROVIDER_OPM); lookup = get_provider_data(auth, SELF_PID);
memcpy(c, "\x04\x01", 2); c += 2; /* Socks version 4, connect command */ memcpy(c, "\x04\x01", 2); c += 2; /* Socks version 4, connect command */
@ -315,7 +317,7 @@ socks5_connected(rb_fde_t *F, int error, void *data)
lrb_assert(scan != NULL); lrb_assert(scan != NULL);
auth = scan->auth; auth = scan->auth;
lookup = get_provider_data(auth, PROVIDER_OPM); lookup = get_provider_data(auth, SELF_PID);
/* Build the version header and socks request /* Build the version header and socks request
* version header (3 bytes): version, number of auth methods, auth type (0 for none) * version header (3 bytes): version, number of auth methods, auth type (0 for none)
@ -380,7 +382,7 @@ http_connect_connected(rb_fde_t *F, int error, void *data)
lrb_assert(scan != NULL); lrb_assert(scan != NULL);
auth = scan->auth; auth = scan->auth;
lookup = get_provider_data(auth, PROVIDER_OPM); lookup = get_provider_data(auth, SELF_PID);
switch(GET_SS_FAMILY(&auth->c_addr)) switch(GET_SS_FAMILY(&auth->c_addr))
{ {
@ -425,7 +427,7 @@ end:
static inline void static inline void
establish_connection(struct auth_client *auth, struct opm_proxy *proxy) establish_connection(struct auth_client *auth, struct opm_proxy *proxy)
{ {
struct opm_lookup *lookup = get_provider_data(auth, PROVIDER_OPM); struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
struct opm_listener *listener; struct opm_listener *listener;
struct opm_scan *scan = rb_malloc(sizeof(struct opm_scan)); struct opm_scan *scan = rb_malloc(sizeof(struct opm_scan));
struct rb_sockaddr_storage c_a, l_a; struct rb_sockaddr_storage c_a, l_a;
@ -617,8 +619,8 @@ opm_scan(struct auth_client *auth)
lrb_assert(auth != NULL); lrb_assert(auth != NULL);
lookup = get_provider_data(auth, PROVIDER_OPM); lookup = get_provider_data(auth, SELF_PID);
set_provider_timeout_relative(auth, PROVIDER_OPM, opm_timeout); set_provider_timeout_relative(auth, SELF_PID, opm_timeout);
lookup->in_progress = true; lookup->in_progress = true;
@ -633,19 +635,21 @@ opm_scan(struct auth_client *auth)
/* This is called every time a provider is completed as long as we are marked not done */ /* This is called every time a provider is completed as long as we are marked not done */
static void static void
blacklists_initiate(struct auth_client *auth, provider_t provider) blacklists_initiate(struct auth_client *auth, uint32_t provider)
{ {
struct opm_lookup *lookup = get_provider_data(auth, PROVIDER_OPM); struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
uint32_t rdns_pid, ident_pid;
lrb_assert(provider != PROVIDER_OPM); lrb_assert(provider != SELF_PID);
lrb_assert(!is_provider_done(auth, PROVIDER_OPM)); lrb_assert(!is_provider_done(auth, SELF_PID));
lrb_assert(rb_dlink_list_length(&proxy_scanners) > 0); lrb_assert(rb_dlink_list_length(&proxy_scanners) > 0);
if(lookup == NULL || lookup->in_progress) if(lookup == NULL || lookup->in_progress)
/* Nothing to do */ /* Nothing to do */
return; return;
else if(!(is_provider_done(auth, PROVIDER_RDNS) && is_provider_done(auth, PROVIDER_IDENT))) else if((!get_provider_id("rdns", &rdns_pid) || is_provider_done(auth, rdns_pid)) &&
/* Don't start until we've completed these */ (!get_provider_id("ident", &ident_pid) || is_provider_done(auth, ident_pid)))
/* Don't start until ident and rdns are finished (or not loaded) */
return; return;
else else
opm_scan(auth); opm_scan(auth);
@ -654,7 +658,9 @@ blacklists_initiate(struct auth_client *auth, provider_t provider)
static bool static bool
opm_start(struct auth_client *auth) opm_start(struct auth_client *auth)
{ {
lrb_assert(get_provider_data(auth, PROVIDER_OPM) == NULL); uint32_t rdns_pid, ident_pid;
lrb_assert(get_provider_data(auth, SELF_PID) == NULL);
if(!opm_enable || rb_dlink_list_length(&proxy_scanners) == 0) if(!opm_enable || rb_dlink_list_length(&proxy_scanners) == 0)
{ {
@ -663,20 +669,23 @@ opm_start(struct auth_client *auth)
return true; return true;
} }
set_provider_data(auth, PROVIDER_BLACKLIST, rb_malloc(sizeof(struct opm_lookup))); set_provider_data(auth, SELF_PID, rb_malloc(sizeof(struct opm_lookup)));
if(is_provider_done(auth, PROVIDER_RDNS) && is_provider_done(auth, PROVIDER_IDENT)) if((!get_provider_id("rdns", &rdns_pid) || is_provider_done(auth, rdns_pid)) &&
/* This probably can't happen but let's handle this case anyway */ (!get_provider_id("ident", &ident_pid) || is_provider_done(auth, ident_pid)))
{
/* Don't start until ident and rdns are finished (or not loaded) */
opm_scan(auth); opm_scan(auth);
}
set_provider_running(auth, PROVIDER_BLACKLIST); set_provider_running(auth, SELF_PID);
return true; return true;
} }
static void static void
opm_cancel(struct auth_client *auth) opm_cancel(struct auth_client *auth)
{ {
struct opm_lookup *lookup = get_provider_data(auth, PROVIDER_OPM); struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
if(lookup != NULL) if(lookup != NULL)
{ {
@ -694,9 +703,9 @@ opm_cancel(struct auth_client *auth)
rb_free(lookup); rb_free(lookup);
set_provider_data(auth, PROVIDER_OPM, NULL); set_provider_data(auth, SELF_PID, NULL);
set_provider_timeout_absolute(auth, PROVIDER_OPM, 0); set_provider_timeout_absolute(auth, SELF_PID, 0);
provider_done(auth, PROVIDER_OPM); provider_done(auth, SELF_PID);
} }
} }
@ -869,7 +878,7 @@ delete_opm_scanner(const char *key __unused, int parc __unused, const char **par
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{ {
rb_dlink_node *ptr; rb_dlink_node *ptr;
struct opm_lookup *lookup = get_provider_data(auth, PROVIDER_OPM); struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
if(lookup == NULL) if(lookup == NULL)
continue; continue;
@ -934,7 +943,8 @@ struct auth_opts_handler opm_options[] =
struct auth_provider opm_provider = struct auth_provider opm_provider =
{ {
.id = PROVIDER_OPM, .name = "opm",
.letter = 'O',
.destroy = opm_destroy, .destroy = opm_destroy,
.start = opm_start, .start = opm_start,
.cancel = opm_cancel, .cancel = opm_cancel,

View file

@ -26,6 +26,8 @@
#include "res.h" #include "res.h"
#include "dns.h" #include "dns.h"
#define SELF_PID (rdns_provider.id)
struct user_query struct user_query
{ {
struct dns_query *query; /* Pending DNS query */ struct dns_query *query; /* Pending DNS query */
@ -56,7 +58,7 @@ static void
dns_answer_callback(const char *res, bool status, query_type type, void *data) dns_answer_callback(const char *res, bool status, query_type type, void *data)
{ {
struct auth_client *auth = data; struct auth_client *auth = data;
struct user_query *query = get_provider_data(auth, PROVIDER_RDNS); struct user_query *query = get_provider_data(auth, SELF_PID);
lrb_assert(query != NULL); lrb_assert(query != NULL);
@ -74,7 +76,7 @@ dns_answer_callback(const char *res, bool status, query_type type, void *data)
static void static void
client_fail(struct auth_client *auth, dns_message report) client_fail(struct auth_client *auth, dns_message report)
{ {
struct user_query *query = get_provider_data(auth, PROVIDER_RDNS); struct user_query *query = get_provider_data(auth, SELF_PID);
lrb_assert(query != NULL); lrb_assert(query != NULL);
@ -85,15 +87,15 @@ client_fail(struct auth_client *auth, dns_message report)
rb_free(query); rb_free(query);
set_provider_data(auth, PROVIDER_RDNS, NULL); set_provider_data(auth, SELF_PID, NULL);
set_provider_timeout_absolute(auth, PROVIDER_RDNS, 0); set_provider_timeout_absolute(auth, SELF_PID, 0);
provider_done(auth, PROVIDER_RDNS); provider_done(auth, SELF_PID);
} }
static void static void
client_success(struct auth_client *auth) client_success(struct auth_client *auth)
{ {
struct user_query *query = get_provider_data(auth, PROVIDER_RDNS); struct user_query *query = get_provider_data(auth, SELF_PID);
lrb_assert(query != NULL); lrb_assert(query != NULL);
@ -102,9 +104,9 @@ client_success(struct auth_client *auth)
rb_free(query); rb_free(query);
set_provider_data(auth, PROVIDER_RDNS, NULL); set_provider_data(auth, SELF_PID, NULL);
set_provider_timeout_absolute(auth, PROVIDER_RDNS, 0); set_provider_timeout_absolute(auth, SELF_PID, 0);
provider_done(auth, PROVIDER_RDNS); provider_done(auth, SELF_PID);
} }
static void static void
@ -115,7 +117,7 @@ rdns_destroy(void)
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{ {
if(get_provider_data(auth, PROVIDER_RDNS) != NULL) if(get_provider_data(auth, SELF_PID) != NULL)
client_fail(auth, REPORT_FAIL); client_fail(auth, REPORT_FAIL);
} }
} }
@ -125,20 +127,20 @@ rdns_start(struct auth_client *auth)
{ {
struct user_query *query = rb_malloc(sizeof(struct user_query)); struct user_query *query = rb_malloc(sizeof(struct user_query));
set_provider_data(auth, PROVIDER_RDNS, query); set_provider_data(auth, SELF_PID, query);
set_provider_timeout_relative(auth, PROVIDER_RDNS, rdns_timeout); set_provider_timeout_relative(auth, SELF_PID, rdns_timeout);
query->query = lookup_hostname(auth->c_ip, dns_answer_callback, auth); query->query = lookup_hostname(auth->c_ip, dns_answer_callback, auth);
notice_client(auth->cid, messages[REPORT_LOOKUP]); notice_client(auth->cid, messages[REPORT_LOOKUP]);
set_provider_running(auth, PROVIDER_RDNS); set_provider_running(auth, SELF_PID);
return true; return true;
} }
static void static void
rdns_cancel(struct auth_client *auth) rdns_cancel(struct auth_client *auth)
{ {
struct user_query *query = get_provider_data(auth, PROVIDER_RDNS); struct user_query *query = get_provider_data(auth, SELF_PID);
if(query != NULL) if(query != NULL)
client_fail(auth, REPORT_FAIL); client_fail(auth, REPORT_FAIL);
@ -166,7 +168,8 @@ struct auth_opts_handler rdns_options[] =
struct auth_provider rdns_provider = struct auth_provider rdns_provider =
{ {
.id = PROVIDER_RDNS, .name = "rdns",
.letter = 'R',
.destroy = rdns_destroy, .destroy = rdns_destroy,
.start = rdns_start, .start = rdns_start,
.cancel = rdns_cancel, .cancel = rdns_cancel,

View file

@ -467,7 +467,7 @@ register_local_user(struct Client *client_p, struct Client *source_p)
return CLIENT_EXITED; return CLIENT_EXITED;
} }
break; break;
case 'O': case 'O': /* OPM */
if(IsExemptKline(source_p) || IsConfExemptProxy(aconf)) if(IsExemptKline(source_p) || IsConfExemptProxy(aconf))
{ {
sendto_one_notice(source_p, ":*** Your IP address %s has been detected as an open proxy (ip:port %s), but you are exempt", sendto_one_notice(source_p, ":*** Your IP address %s has been detected as an open proxy (ip:port %s), but you are exempt",

View file

@ -704,14 +704,14 @@ rb_dictionary_element *rb_dictionary_add(rb_dictionary *dict, const void *key, v
* - name of DTree node to delete * - name of DTree node to delete
* *
* Outputs: * Outputs:
* - on success, the remaining data that needs to be mowgli_freed * - on success, the remaining data that needs to be rb_freed
* - on failure, NULL * - on failure, NULL
* *
* Side Effects: * Side Effects:
* - data is removed from the DTree. * - data is removed from the DTree.
* *
* Notes: * Notes:
* - the returned data needs to be mowgli_freed/released manually! * - the returned data needs to be rb_freed/released manually!
*/ */
void *rb_dictionary_delete(rb_dictionary *dtree, const void *key) void *rb_dictionary_delete(rb_dictionary *dtree, const void *key)
{ {