/* * Hash functions for cached trusted and address tables * * Copyright (C) 2003-2006 Juha Heinanen * * This file is part of openser, a free SIP server. * * openser 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 * * openser 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 #include #include "../../mem/shm_mem.h" #include "../../parser/parse_from.h" #include "../../ut.h" #include "../../hash_func.h" #include "../../usr_avp.h" #include "../../ip_addr.h" #include "hash.h" #include "trusted.h" #include "address.h" #define perm_hash(_s) core_hash( &(_s), 0, PERM_HASH_SIZE) /* tag AVP specs */ static int tag_avp_type; static int_str tag_avp; static str tag_str; /* * Parse and set tag AVP specs */ int init_tag_avp(char *tag_avp_param) { if (tag_avp_param && *tag_avp_param) { tag_str.s = tag_avp_param; tag_str.len = strlen(tag_str.s); if (parse_avp_spec( &tag_str, &tag_avp_type, &tag_avp)<0) { LOG(L_CRIT,"ERROR:permissions:init_tag_avp: " "invalid tag AVP spec \"%s\"\n", tag_avp_param); return -1; } } else { tag_avp.n = 0; tag_avp_type = 0; } return 0; } /* * Gets tag avp specs */ void get_tag_avp(int_str *tag_avp_p, int *tag_avp_type_p) { *tag_avp_p = tag_avp; *tag_avp_type_p = tag_avp_type; } /* * Create and initialize a hash table */ struct trusted_list** new_hash_table(void) { struct trusted_list** ptr; /* Initializing hash tables and hash table variable */ ptr = (struct trusted_list **)shm_malloc (sizeof(struct trusted_list*) * PERM_HASH_SIZE); if (!ptr) { LOG(L_ERR, "new_hash_table(): No memory for hash table\n"); return 0; } memset(ptr, 0, sizeof(struct trusted_list*) * PERM_HASH_SIZE); return ptr; } /* * Release all memory allocated for a hash table */ void free_hash_table(struct trusted_list** table) { if (!table) return; empty_hash_table(table); shm_free(table); } /* * Add into hash table, where proto is integer * representation of string argument proto. */ int hash_table_insert(struct trusted_list** table, char* src_ip, char* proto, char* pattern, char* tag) { struct trusted_list *np; unsigned int hash_val; np = (struct trusted_list *) shm_malloc(sizeof(*np)); if (np == NULL) { LOG(L_CRIT, "hash_table_insert(): Cannot allocate shm memory " "for table entry\n"); return -1; } if (strcmp(proto, "any") == 0) { np->proto = PROTO_NONE; } else if (strcmp(proto, "udp") == 0) { np->proto = PROTO_UDP; } else if (strcmp(proto, "tcp") == 0) { np->proto = PROTO_TCP; } else if (strcmp(proto, "tls") == 0) { np->proto = PROTO_TLS; } else if (strcmp(proto, "sctp") == 0) { np->proto = PROTO_SCTP; } else if (strcmp(proto, "none") == 0) { shm_free(np); return 1; } else { LOG(L_CRIT, "hash_table_insert(): Unknown protocol\n"); shm_free(np); return -1; } np->src_ip.len = strlen(src_ip); np->src_ip.s = (char *) shm_malloc(np->src_ip.len); if (np->src_ip.s == NULL) { LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory for src_ip " "string\n"); shm_free(np); return -1; } (void) strncpy(np->src_ip.s, src_ip, np->src_ip.len); if (pattern) { np->pattern = (char *) shm_malloc(strlen(pattern)+1); if (np->pattern == NULL) { LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory " "for pattern string\n"); shm_free(np->src_ip.s); shm_free(np); return -1; } (void) strcpy(np->pattern, pattern); } else { np->pattern = 0; } if (tag) { np->tag.len = strlen(tag); np->tag.s = (char *) shm_malloc((np->tag.len) + 1); if (np->tag.s == NULL) { LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory " "for pattern string\n"); shm_free(np->src_ip.s); shm_free(np->pattern); shm_free(np); return -1; } (void) strcpy(np->tag.s, tag); } else { np->tag.len = 0; np->tag.s = 0; } hash_val = perm_hash(np->src_ip); np->next = table[hash_val]; table[hash_val] = np; return 1; } /* * Check if an entry exists in hash table that has given src_ip and protocol * value and pattern that matches to From URI. If, assign */ int match_hash_table(struct trusted_list** table, struct sip_msg* msg) { str uri; char uri_string[MAX_URI_SIZE + 1]; regex_t preg; struct trusted_list *np; str src_ip; int_str val; src_ip.s = ip_addr2a(&msg->rcv.src_ip); src_ip.len = strlen(src_ip.s); if (parse_from_header(msg) < 0) return -1; uri = get_from(msg)->uri; if (uri.len > MAX_URI_SIZE) { LOG(L_ERR, "match_hash_table(): From URI too large\n"); return -1; } memcpy(uri_string, uri.s, uri.len); uri_string[uri.len] = (char)0; for (np = table[perm_hash(src_ip)]; np != NULL; np = np->next) { if ((np->src_ip.len == src_ip.len) && (strncasecmp(np->src_ip.s, src_ip.s, src_ip.len) == 0) && ((np->proto == PROTO_NONE) || (np->proto == msg->rcv.proto))) { if (!(np->pattern)) goto found; if (regcomp(&preg, np->pattern, REG_NOSUB)) { LOG(L_ERR, "match_hash_table(): Error in regular expression\n"); return -1; } if (regexec(&preg, uri_string, 0, (regmatch_t *)0, 0)) { regfree(&preg); } else { regfree(&preg); goto found; } } } return -1; found: if (tag_avp.n && np->tag.s) { val.s = np->tag; if (add_avp(tag_avp_type|AVP_VAL_STR, tag_avp, val) != 0) { LOG(L_ERR, "match_hash_table(): ERROR: setting of " "tag_avp failed\n"); return -1; } } return 1; } /* * Print trusted entries stored in hash table */ int hash_table_mi_print(struct trusted_list** table, struct mi_node* rpl) { int i; struct trusted_list *np; for (i = 0; i < PERM_HASH_SIZE; i++) { np = table[i]; while (np) { if (addf_mi_node_child(rpl, 0, 0, 0, "%4d <%.*s, %d, %s, %s>", i, np->src_ip.len, ZSW(np->src_ip.s), np->proto, np->pattern?np->pattern:"NULL", np->tag.len?np->tag.s:"NULL") == 0) { return -1; } np = np->next; } } return 0; } /* * Free contents of hash table, it doesn't destroy the * hash table itself */ void empty_hash_table(struct trusted_list **table) { int i; struct trusted_list *np, *next; for (i = 0; i < PERM_HASH_SIZE; i++) { np = table[i]; while (np) { if (np->src_ip.s) shm_free(np->src_ip.s); if (np->pattern) shm_free(np->pattern); if (np->tag.s) shm_free(np->tag.s); next = np->next; shm_free(np); np = next; } table[i] = 0; } } /* * Create and initialize an address hash table */ struct addr_list** new_addr_hash_table(void) { struct addr_list** ptr; /* Initializing hash tables and hash table variable */ ptr = (struct addr_list **)shm_malloc (sizeof(struct addr_list*) * PERM_HASH_SIZE); if (!ptr) { LOG(L_ERR, "permissions:new_addr_hash_table(): " "No memory for hash table\n"); return 0; } memset(ptr, 0, sizeof(struct addr_list*) * PERM_HASH_SIZE); return ptr; } /* * Release all memory allocated for a hash table */ void free_addr_hash_table(struct addr_list** table) { if (!table) return; empty_addr_hash_table(table); shm_free(table); } /* * Add into hash table */ int addr_hash_table_insert(struct addr_list** table, unsigned int grp, unsigned int ip_addr, unsigned int port) { struct addr_list *np; unsigned int hash_val; str addr_str; np = (struct addr_list *) shm_malloc(sizeof(*np)); if (np == NULL) { LOG(L_CRIT, "permissions:addr_hash_table_insert(): " "Cannot allocate shm memory for table entry\n"); return -1; } np->grp = grp; np->ip_addr = ip_addr; np->port = port; addr_str.s = (char *)(&ip_addr); addr_str.len = 4; hash_val = perm_hash(addr_str); np->next = table[hash_val]; table[hash_val] = np; return 1; } /* * Check if an entry exists in hash table that has given group, ip_addr, and * port. Port 0 in hash table matches any port. */ int match_addr_hash_table(struct addr_list** table, unsigned int group, unsigned int ip_addr, unsigned int port) { struct addr_list *np; str addr_str; addr_str.s = (char *)(&ip_addr); addr_str.len = 4; for (np = table[perm_hash(addr_str)]; np != NULL; np = np->next) { if ((np->ip_addr == ip_addr) && (np->grp == group) && ((np->port == 0) || (np->port == port))) { return 1; } } return -1; } /* * Print addresses stored in hash table */ int addr_hash_table_mi_print(struct addr_list** table, struct mi_node* rpl) { int i; struct addr_list *np; struct ip_addr addr; for (i = 0; i < PERM_HASH_SIZE; i++) { np = table[i]; while (np) { addr.af = AF_INET; addr.len = 4; addr.u.addr32[0] = np->ip_addr; if (addf_mi_node_child(rpl, 0, 0, 0, "%4d <%u, %s, %u>", i, np->grp, ip_addr2a(&addr), np->port) == 0) return -1; np = np->next; } } return 0; } /* * Free contents of hash table, it doesn't destroy the * hash table itself */ void empty_addr_hash_table(struct addr_list **table) { int i; struct addr_list *np, *next; for (i = 0; i < PERM_HASH_SIZE; i++) { np = table[i]; while (np) { next = np->next; shm_free(np); np = next; } table[i] = 0; } } /* * Create and initialize a subnet table */ struct subnet* new_subnet_table(void) { struct subnet* ptr; /* subnet record [PERM_MAX_SUBNETS] contains in its grp field the number of subnet records in the subnet table */ ptr = (struct subnet *)shm_malloc (sizeof(struct subnet) * (PERM_MAX_SUBNETS + 1)); if (!ptr) { LOG(L_ERR, "permissions:new_subnet_table(): " "No memory for subnet table\n"); return 0; } ptr[PERM_MAX_SUBNETS].grp = 0; return ptr; } /* * Add into subnet table so that table is * kept in increasing ordered according to grp. */ int subnet_table_insert(struct subnet* table, unsigned int grp, unsigned int subnet, unsigned int mask, unsigned int port) { int i; unsigned int count; count = table[PERM_MAX_SUBNETS].grp; if (count == PERM_MAX_SUBNETS) { LOG(L_CRIT, "permissions:subnet_table_insert(): " "Subnet table is full\n"); return 0; } mask = 32 - mask; subnet = subnet << mask; i = count - 1; while ((i >= 0) && (table[i].grp > grp)) { table[i + 1] = table[i]; i--; } table[i + 1].grp = grp; table[i + 1].subnet = subnet; table[i + 1].port = port; table[i + 1].mask = mask; table[PERM_MAX_SUBNETS].grp = count + 1; return 1; } /* * Check if an entry exists in subnet table that matches given group, ip_addr, * and port. Port 0 in subnet table matches any port. */ int match_subnet_table(struct subnet* table, unsigned int grp, unsigned int ip_addr, unsigned int port) { unsigned int count, i, subnet; count = table[PERM_MAX_SUBNETS].grp; i = 0; while ((i < count) && (table[i].grp < grp)) i++; if (i == count) return -1; while ((i < count) && (table[i].grp == grp)) { subnet = ip_addr << table[i].mask; if ((table[i].subnet == subnet) && ((table[i].port == port) || (table[i].port == 0))) return 1; i++; } return -1; } /* * Print subnets stored in subnet table */ int subnet_table_mi_print(struct subnet* table, struct mi_node* rpl) { unsigned int count, i; struct ip_addr addr; count = table[PERM_MAX_SUBNETS].grp; for (i = 0; i < count; i++) { addr.af = AF_INET; addr.len = 4; addr.u.addr32[0] = table[i].subnet >> table[i].mask; if (addf_mi_node_child(rpl, 0, 0, 0, "%4d <%u, %s, %u, %u>", i, table[i].grp, ip_addr2a(&addr), 32 - table[i].mask, table[i].port) == 0) { return -1; } } return 0; } /* * Empty contents of subnet table */ void empty_subnet_table(struct subnet *table) { table[PERM_MAX_SUBNETS].grp = 0; } /* * Release memory allocated for a subnet table */ void free_subnet_table(struct subnet* table) { if (!table) return; shm_free(table); }