/* ARISA - Access List Functions * Copyright (C) 2004 Carl Ritson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "arisa.h" static int test_entry(access_entry_t *ent, network_t *net, const char *host, const char *hostmask, const char *nick) { int ret,mode,accept = 1; assert(ent != NULL); if(ent->network != NULL) accept &= (ent->network == net); if(ent->host != NULL) { if(hostmask != NULL && (strchr(ent->host,'!') != NULL || strchr(ent->host,'@') != NULL)) { accept &= (irc_hostmask_match(hostmask,ent->host)); } else if(host != NULL) { accept &= (irc_hostmask_match(host,ent->host)); } else { return 0; } } if(ent->channel != NULL) { if(net == NULL || nick == NULL) return 0; ret = 0; LOCK(net); if(net->info != NULL) { LOCK(net->info); ret = nickhash_present_mode(net->info->nick_tracker, ent->channel,nick,&mode); UNLOCK(net->info); } UNLOCK(net); accept &= (ret != 0); if(ret != 0 && ent->mode != 0) accept &= ((mode & ent->mode) != 0); } else if(ent->mode != 0) { mode = 0; LOCK(net); if(net->info != NULL) { LOCK(net->info); mode = nickhash_highest_mode( net->info->nick_tracker,nick); UNLOCK(net->info); } UNLOCK(net); accept &= ((mode & ent->mode) != 0); } return (accept != 0 ? 1 : 0); } int access_check(access_t *acc, network_t *net, const char *host, const char *hostmask, const char *nick) { int i,accept; if(acc == NULL) return 0; LOCK(acc); if((acc->flags & ACCESS_SMART) == ACCESS_SMART) { accept = 1; for(i = 0; i < acc->no_entries && accept; ++i) { if(acc->entries[i]->flags & ACCESS_ALLOW) accept = 0; } } else if(acc->flags & ACCESS_ALLOW) { accept = 1; } else { accept = 0; } for(i = 0; i < acc->no_entries; ++i) { if(test_entry(acc->entries[i],net,host,hostmask,nick)) { if(acc->entries[i]->flags & ACCESS_ALLOW) { if(acc->flags & ACCESS_ALLOW) { accept = 1; break; } else accept = 1; } else { if(acc->flags & ACCESS_DENY) { accept = 0; break; } else accept = 0; } } } UNLOCK(acc); return accept; } int access_check_channel(access_t *acc, network_t *net, channel_t *chan) { int i,accept; if(acc == NULL) return 0; LOCK(acc); if((acc->flags & ACCESS_SMART) == ACCESS_SMART) { accept = 1; for(i = 0; i < acc->no_entries && accept; ++i) { if(acc->entries[i]->flags & ACCESS_ALLOW) accept = 0; } } else if(acc->flags & ACCESS_ALLOW) { accept = 1; } else { accept = 0; } for(i = 0; i < acc->no_entries; ++i) { if(acc->entries[i]->network == net && acc->entries[i]->channel == chan) { if(acc->entries[i]->flags & ACCESS_ALLOW) { if(acc->flags & ACCESS_ALLOW) { accept = 1; break; } else accept = 1; } else { if(acc->flags & ACCESS_DENY) { accept = 0; break; } else accept = 0; } } } UNLOCK(acc); return accept; } int access_add_entry(access_t *acc, int flags, network_t *net, const char *host, channel_t *chan, int mode) { access_entry_t *ent = alloc_access_entry(); int tmp,ret = 0; ent->flags = flags; ent->network = net; if(host != NULL) ent->host = xstrdup(host); ent->channel = chan; ent->mode = mode; LOCK(acc); PTRARR_ADD(&(acc->entries),&(acc->no_entries),ent); UNLOCK(acc); /* The following code deals with the race condition, we added an * entry for a network or channel which was deleted before we added * the entry. We do it after we add the entry, incase one of the * purge functions gets ahead of us and does its job. * FIXME: more clear description? */ if(net != NULL) { LOCK(net); if(net->deleted != 0) ret = -1; UNLOCK(net); } if(chan != NULL) { LOCK(chan); if(chan->deleted != 0) ret = -1; UNLOCK(chan); } if(ret != 0) { LOCK(acc); tmp = acc->no_entries; PTRARR_DEL(&(acc->entries),&(acc->no_entries),ent); if(tmp != acc->no_entries) free_access_entry(ent); // else it was already removed by a purge function UNLOCK(acc); } return ret; } void access_copy(access_t *dst, access_t *src) { int i; // two irregular locks, serialise on the global lock LOCK(global); LOCK(src); LOCK(dst); for(i = 0; i < dst->no_entries; ++i) free_access_entry(dst->entries[i]); if(dst->entries != NULL) xfree(dst->entries); dst->flags = src->flags; dst->no_entries = src->no_entries; dst->entries = xalloc(sizeof(access_entry_t *) * dst->no_entries); for(i = 0; i < src->no_entries; ++i) { dst->entries[i] = alloc_access_entry(); dst->entries[i]->flags = src->entries[i]->flags; dst->entries[i]->network = src->entries[i]->network; dst->entries[i]->channel = src->entries[i]->channel; dst->entries[i]->mode = src->entries[i]->mode; if(src->entries[i]->host != NULL) dst->entries[i]->host = xstrdup(src->entries[i]->host); } UNLOCK(dst); UNLOCK(src); UNLOCK(global); } int access_del_entry(access_t *acc, access_entry_t *ent) { int tmp, ret = -1; LOCK(acc); tmp = acc->no_entries; PTRARR_DEL(&(acc->entries),&(acc->no_entries),ent); if(acc->no_entries != tmp) { free_access_entry(ent); ret = 0; } UNLOCK(acc); return ret; } int access_del_entry_no(access_t *acc, int no) { int ret = -1; LOCK(acc); if(no >= 0 && no < acc->no_entries) { access_entry_t *ent = acc->entries[no]; PTRARR_DEL(&(acc->entries),&(acc->no_entries),ent); free_access_entry(ent); ret = 0; } UNLOCK(acc); return ret; } static void purge_netchan(access_t *acc, network_t *net, channel_t *chan) { int i; LOCK(acc); for(i = 0; i < acc->no_entries; ++i) { if( (chan == NULL && net != NULL && acc->entries[i]->network == net) || (net == NULL && chan != NULL && acc->entries[i]->channel == chan) || (acc->entries[i]->network == net && acc->entries[i]->channel == chan)) { free_access_entry(acc->entries[i]); acc->entries[i] = NULL; } } PTRARR_DEL(&(acc->entries),&(acc->no_entries),NULL); UNLOCK(acc); } static void access_purge_netchan(network_t *net, channel_t *chan) { int i; LOCK(global); for(i = 0; i < global->no_interfaces; ++i) { LOCK(global->interfaces[i]); if(global->interfaces[i]->type == INTERFACE_RECV) { purge_netchan(global->interfaces[i]->recv.access, net,chan); } UNLOCK(global->interfaces[i]); } for(i = 0; i < global->no_packlists; ++i) { LOCK(global->packlists[i]); purge_netchan(global->packlists[i]->access,net,chan); UNLOCK(global->packlists[i]); } UNLOCK(global); } void access_purge_channel(channel_t *chan) { access_purge_netchan(NULL,chan); } void access_purge_network(network_t *net) { access_purge_netchan(net,NULL); } void access_verify_entries(access_t *acc) { int i; assert(acc != NULL); LOCK(acc); for(i = 0; i < acc->no_entries; ++i) { if(acc->entries[i]->network != NULL) { if(!irc_network_valid(acc->entries[i]->network)) { free_access_entry(acc->entries[i]); acc->entries[i] = NULL; continue; } } if(acc->entries[i]->network != NULL && acc->entries[i]->channel != NULL) { if(!irc_channel_valid(acc->entries[i]->network, acc->entries[i]->channel)) { free_access_entry(acc->entries[i]); acc->entries[i] = NULL; continue; } } } PTRARR_DEL(&(acc->entries),&(acc->no_entries),NULL); UNLOCK(acc); }