/* * Caller is responsible for managing the packet entries. */ fr_packet_list_t *fr_packet_list_create(int alloc_id) { int i; fr_packet_list_t *pl; pl = malloc(sizeof(*pl)); if (!pl) return NULL; memset(pl, 0, sizeof(*pl)); pl->tree = rbtree_create(packet_entry_cmp, NULL, 0); if (!pl->tree) { fr_packet_list_free(pl); return NULL; } for (i = 0; i < MAX_SOCKETS; i++) { pl->sockets[i].sockfd = -1; } if (alloc_id) { pl->alloc_id = 1; pl->dst2id_ht = fr_hash_table_create(packet_dst2id_hash, packet_dst2id_cmp, packet_dst2id_free); if (!pl->dst2id_ht) { fr_packet_list_free(pl); return NULL; } } return pl; }
static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t **pht, char *compat_mode_str) { int rcode; PAIR_LIST *users = NULL; PAIR_LIST *entry, *next; fr_hash_table_t *ht, *tailht; int order = 0; if (!filename) { *pht = NULL; return 0; } rcode = pairlist_read(ctx, filename, &users, 1); if (rcode < 0) { return -1; } /* * Walk through the 'users' file list, if we're debugging, * or if we're in compat_mode. */ if ((debug_flag) || (strcmp(compat_mode_str, "cistron") == 0)) { VALUE_PAIR *vp; int compat_mode = false; if (strcmp(compat_mode_str, "cistron") == 0) { compat_mode = true; } entry = users; while (entry) { vp_cursor_t cursor; if (compat_mode) { DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...", filename, entry->lineno, entry->name); } /* * Look for improper use of '=' in the * check items. They should be using * '==' for on-the-wire RADIUS attributes, * and probably ':=' for server * configuration items. */ for (vp = paircursor(&cursor, &entry->check); vp; vp = pairnext(&cursor)) { /* * Ignore attributes which are set * properly. */ if (vp->op != T_OP_EQ) { continue; } /* * If it's a vendor attribute, * or it's a wire protocol, * ensure it has '=='. */ if ((vp->da->vendor != 0) || (vp->da->attr < 0x100)) { if (!compat_mode) { WDEBUG("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s", filename, entry->lineno, vp->da->name, vp->da->name, entry->name); } else { DEBUG("\tChanging '%s =' to '%s =='", vp->da->name, vp->da->name); } vp->op = T_OP_CMP_EQ; continue; } /* * Cistron Compatibility mode. * * Re-write selected attributes * to be '+=', instead of '='. * * All others get set to '==' */ if (compat_mode) { /* * Non-wire attributes become += * * On the write attributes * become == */ if ((vp->da->attr >= 0x100) && (vp->da->attr <= 0xffff) && (vp->da->attr != PW_HINT) && (vp->da->attr != PW_HUNTGROUP_NAME)) { DEBUG("\tChanging '%s =' to '%s +='", vp->da->name, vp->da->name); vp->op = T_OP_ADD; } else { DEBUG("\tChanging '%s =' to '%s =='", vp->da->name, vp->da->name); vp->op = T_OP_CMP_EQ; } } } /* end of loop over check items */ /* * Look for server configuration items * in the reply list. * * It's a common enough mistake, that it's * worth doing. */ for (vp = paircursor(&cursor, &entry->reply); vp; vp = pairnext(&cursor)) { /* * If it's NOT a vendor attribute, * and it's NOT a wire protocol * and we ignore Fall-Through, * then bitch about it, giving a * good warning message. */ if ((vp->da->vendor == 0) && (vp->da->attr > 0xff) && (vp->da->attr > 1000)) { WDEBUG("[%s]:%d Check item \"%s\"\n" "\tfound in reply item list for user \"%s\".\n" "\tThis attribute MUST go on the first line" " with the other check items", filename, entry->lineno, vp->da->name, entry->name); } } entry = entry->next; } } ht = fr_hash_table_create(pairlist_hash, pairlist_cmp, my_pairlist_free); if (!ht) { pairlist_free(&users); return -1; } tailht = fr_hash_table_create(pairlist_hash, pairlist_cmp, NULL); if (!tailht) { fr_hash_table_free(ht); pairlist_free(&users); return -1; } /* * Now that we've read it in, put the entries into a hash * for faster access. */ for (entry = users; entry != NULL; entry = next) { PAIR_LIST *tail; next = entry->next; entry->next = NULL; entry->order = order++; /* * Insert it into the hash table, and remember * the tail of the linked list. */ tail = fr_hash_table_finddata(tailht, entry); if (!tail) { /* * Insert it into the head & tail. */ if (!fr_hash_table_insert(ht, entry) || !fr_hash_table_insert(tailht, entry)) { pairlist_free(&next); fr_hash_table_free(ht); fr_hash_table_free(tailht); return -1; } } else { tail->next = entry; if (!fr_hash_table_replace(tailht, entry)) { pairlist_free(&next); fr_hash_table_free(ht); fr_hash_table_free(tailht); return -1; } } } fr_hash_table_free(tailht); *pht = ht; return 0; }
/* * Allocate the thread pool, and seed it with an initial number * of threads. * * FIXME: What to do on a SIGHUP??? */ int thread_pool_init(CONF_SECTION *cs, int *spawn_flag) { #ifndef WITH_GCD int i, rcode; CONF_SECTION *pool_cf; #endif time_t now; cs = cs; /* -Wunused */ now = time(NULL); rad_assert(spawn_flag != NULL); rad_assert(*spawn_flag == TRUE); rad_assert(pool_initialized == FALSE); /* not called on HUP */ #ifndef WITH_GCD pool_cf = cf_subsection_find_next(cs, NULL, "thread"); if (!pool_cf) *spawn_flag = FALSE; #endif /* * Initialize the thread pool to some reasonable values. */ memset(&thread_pool, 0, sizeof(THREAD_POOL)); #ifndef WITH_GCD thread_pool.head = NULL; thread_pool.tail = NULL; thread_pool.total_threads = 0; thread_pool.max_thread_num = 1; thread_pool.cleanup_delay = 5; thread_pool.stop_flag = 0; #endif thread_pool.spawn_flag = *spawn_flag; /* * Don't bother initializing the mutexes or * creating the hash tables. They won't be used. */ if (!*spawn_flag) return 0; #ifdef WNOHANG if ((pthread_mutex_init(&thread_pool.wait_mutex,NULL) != 0)) { radlog(L_ERR, "FATAL: Failed to initialize wait mutex: %s", strerror(errno)); return -1; } /* * Create the hash table of child PID's */ thread_pool.waiters = fr_hash_table_create(pid_hash, pid_cmp, free); if (!thread_pool.waiters) { radlog(L_ERR, "FATAL: Failed to set up wait hash"); return -1; } #endif #ifndef WITH_GCD if (cf_section_parse(pool_cf, NULL, thread_config) < 0) { return -1; } /* * Catch corner cases. */ if (thread_pool.min_spare_threads < 1) thread_pool.min_spare_threads = 1; if (thread_pool.max_spare_threads < 1) thread_pool.max_spare_threads = 1; if (thread_pool.max_spare_threads < thread_pool.min_spare_threads) thread_pool.max_spare_threads = thread_pool.min_spare_threads; if (thread_pool.max_threads == 0) thread_pool.max_threads = 256; if ((thread_pool.max_queue_size < 2) || (thread_pool.max_queue_size > 1048576)) { radlog(L_ERR, "FATAL: max_queue_size value must be in range 2-1048576"); return -1; } #endif /* WITH_GCD */ /* * The pool has already been initialized. Don't spawn * new threads, and don't forget about forked children, */ if (pool_initialized) { return 0; } #ifndef WITH_GCD /* * Initialize the queue of requests. */ memset(&thread_pool.semaphore, 0, sizeof(thread_pool.semaphore)); rcode = sem_init(&thread_pool.semaphore, 0, SEMAPHORE_LOCKED); if (rcode != 0) { radlog(L_ERR, "FATAL: Failed to initialize semaphore: %s", strerror(errno)); return -1; } rcode = pthread_mutex_init(&thread_pool.queue_mutex,NULL); if (rcode != 0) { radlog(L_ERR, "FATAL: Failed to initialize queue mutex: %s", strerror(errno)); return -1; } /* * Allocate multiple fifos. */ for (i = 0; i < RAD_LISTEN_MAX; i++) { thread_pool.fifo[i] = fr_fifo_create(thread_pool.max_queue_size, NULL); if (!thread_pool.fifo[i]) { radlog(L_ERR, "FATAL: Failed to set up request fifo"); return -1; } } #endif #ifdef HAVE_OPENSSL_CRYPTO_H /* * If we're linking with OpenSSL too, then we need * to set up the mutexes and enable the thread callbacks. */ if (!setup_ssl_mutexes()) { radlog(L_ERR, "FATAL: Failed to set up SSL mutexes"); return -1; } #endif #ifndef WITH_GCD /* * Create a number of waiting threads. * * If we fail while creating them, do something intelligent. */ for (i = 0; i < thread_pool.start_threads; i++) { if (spawn_thread(now, 0) == NULL) { return -1; } } #else thread_pool.queue = dispatch_queue_create("org.freeradius.threads", NULL); if (!thread_pool.queue) { radlog(L_ERR, "Failed creating dispatch queue: %s\n", strerror(errno)); exit(1); } #endif DEBUG2("Thread pool initialized"); pool_initialized = TRUE; return 0; }
/* * Initialize the directory, then fix the attr member of * all attributes. */ int dict_init(const char *dir, const char *fn) { /* * Check if we need to change anything. If not, don't do * anything. */ if (dict_stat_check(dir, fn)) { return 0; } /* * Free the dictionaries, and the stat cache. */ dict_free(); stat_root_dir = strdup(dir); stat_root_file = strdup(fn); /* * Create the table of vendor by name. There MAY NOT * be multiple vendors of the same name. * * Each vendor is malloc'd, so the free function is free. */ vendors_byname = fr_hash_table_create(dict_vendor_name_hash, dict_vendor_name_cmp, fr_pool_free); if (!vendors_byname) { return -1; } /* * Create the table of vendors by value. There MAY * be vendors of the same value. If there are, we * pick the latest one. */ vendors_byvalue = fr_hash_table_create(dict_vendor_value_hash, dict_vendor_value_cmp, fr_pool_free); if (!vendors_byvalue) { return -1; } /* * Create the table of attributes by name. There MAY NOT * be multiple attributes of the same name. * * Each attribute is malloc'd, so the free function is free. */ attributes_byname = fr_hash_table_create(dict_attr_name_hash, dict_attr_name_cmp, fr_pool_free); if (!attributes_byname) { return -1; } /* * Create the table of attributes by value. There MAY * be attributes of the same value. If there are, we * pick the latest one. */ attributes_byvalue = fr_hash_table_create(dict_attr_value_hash, dict_attr_value_cmp, fr_pool_free); if (!attributes_byvalue) { return -1; } values_byname = fr_hash_table_create(dict_value_name_hash, dict_value_name_cmp, fr_pool_free); if (!values_byname) { return -1; } values_byvalue = fr_hash_table_create(dict_value_value_hash, dict_value_value_cmp, fr_pool_free); if (!values_byvalue) { return -1; } value_fixup = NULL; /* just to be safe. */ if (my_dict_init(dir, fn, NULL, 0) < 0) return -1; if (value_fixup) { DICT_ATTR *a; value_fixup_t *this, *next; for (this = value_fixup; this != NULL; this = next) { next = this->next; a = dict_attrbyname(this->attrstr); if (!a) { fr_strerror_printf( "dict_init: No ATTRIBUTE \"%s\" defined for VALUE \"%s\"", this->attrstr, this->dval->name); return -1; /* leak, but they should die... */ } this->dval->attr = a->attr; /* * Add the value into the dictionary. */ if (!fr_hash_table_replace(values_byname, this->dval)) { fr_strerror_printf("dict_addvalue: Duplicate value name %s for attribute %s", this->dval->name, a->name); return -1; } /* * Allow them to use the old name, but * prefer the new name when printing * values. */ if (!fr_hash_table_finddata(values_byvalue, this->dval)) { fr_hash_table_replace(values_byvalue, this->dval); } free(this); /* * Just so we don't lose track of things. */ value_fixup = next; } } /* * Walk over all of the hash tables to ensure they're * initialized. We do this because the threads may perform * lookups, and we don't want multi-threaded re-ordering * of the table entries. That would be bad. */ fr_hash_table_walk(vendors_byname, null_callback, NULL); fr_hash_table_walk(vendors_byvalue, null_callback, NULL); fr_hash_table_walk(attributes_byname, null_callback, NULL); fr_hash_table_walk(attributes_byvalue, null_callback, NULL); fr_hash_table_walk(values_byvalue, null_callback, NULL); fr_hash_table_walk(values_byname, null_callback, NULL); return 0; }
/* * (Re-)read radiusd.conf into memory. */ static int mod_instantiate(CONF_SECTION *conf, void *instance) { detail_instance_t *inst = instance; CONF_SECTION *cs; inst->name = cf_section_name2(conf); if (!inst->name) { inst->name = cf_section_name1(conf); } inst->lf= fr_logfile_init(inst); if (!inst->lf) { cf_log_err_cs(conf, "Failed creating log file context"); return -1; } /* * Suppress certain attributes. */ cs = cf_section_sub_find(conf, "suppress"); if (cs) { CONF_ITEM *ci; inst->ht = fr_hash_table_create(detail_hash, detail_cmp, NULL); for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { char const *attr; DICT_ATTR const *da; if (!cf_item_is_pair(ci)) continue; attr = cf_pair_attr(cf_itemtopair(ci)); if (!attr) continue; /* pair-anoia */ da = dict_attrbyname(attr); if (!da) { cf_log_err_cs(conf, "No such attribute '%s'", attr); return -1; } /* * Be kind to minor mistakes. */ if (fr_hash_table_finddata(inst->ht, da)) { WARN("rlm_detail (%s): Ignoring duplicate entry '%s'", inst->name, attr); continue; } if (!fr_hash_table_insert(inst->ht, da)) { ERROR("rlm_detail (%s): Failed inserting '%s' into suppression table", inst->name, attr); return -1; } DEBUG("rlm_detail (%s): '%s' suppressed, will not appear in detail output", inst->name, attr); } /* * If we didn't suppress anything, delete the hash table. */ if (fr_hash_table_num_elements(inst->ht) == 0) { fr_hash_table_free(inst->ht); inst->ht = NULL; } } return 0; }
static int parse(struct radproxy_data *cfg, int linenum, char *args[], int argc) { char *errmsg = NULL; static int kwtype = 0; if (argc <= 0) { printf("line %d: no parameter\n", linenum); return -1; } if (strcasecmp(args[0], "listen") == 0) { kwtype = KW_LISTEN; } else if (strcasecmp(args[0], "client") == 0) { kwtype = KW_CLIENT; return 0; } /*else { printf("line %d: unknown keyword '%s'", linenum, args[0]); return -2; }*/ switch(kwtype) { case KW_CLIENT: { struct radproxy_client *c = parse_client(linenum, args, argc); if (!c) goto error; if (!cfg->clients) { cfg->clients = c; } else { struct radproxy_client *p = cfg->clients; while (p && p->next) p=p->next; p->next = c; } } break; case KW_LISTEN: { struct radproxy_desc *p; if (strcasecmp(args[0], "listen") == 0) { if (argc != 2) { errmsg = "a port number needed after keyword listen"; goto error; } p = calloc(1, sizeof(*p)); if (!p) goto error; p->port = atoi(args[1]); if (p->port <= 0) { errmsg = "invalid port number"; goto error; } if (cfg->proxys) { struct radproxy_desc *q = cfg->proxys; while (q && q->next) q = q->next; q->next = p; } else { cfg->proxys = p; } break; } p = cfg->proxys; while (p &&p->next) p=p->next; if (!p) { errmsg = "go hell"; goto error; } if (strcasecmp(args[0], "mode") == 0) { if (argc != 2) { goto error; } if (strcasecmp(args[1], "radius") == 0) { p->mode = mode_radius; } else if (strcasecmp(args[1], "udp") == 0) { p->mode = mode_udp; } else { errmsg = "unknown mode"; goto error; } } else if (strcasecmp(args[0], "option") == 0) { if (argc < 2) { errmsg = "not enough parameter after keyword option"; goto error; } if (strcasecmp(args[1], "state") == 0) { p->option |= OPTION_STATE; if (argc != 3) { errmsg = "a state timeout number needed after 'state'"; goto error; } p->state_timeout = atoi(args[2]); if (p->state_timeout < 0) { p->state_timeout = 60; printf("invalid state timeout value '%d', reset to 60s\n", p->state_timeout); } p->ht_state = fr_hash_table_create(hash_state, hash_state_cmp, NULL); } else if (strcasecmp(args[1], "roundrobin") == 0) { p->option |= OPTION_ROUND_ROBIN; } else if (strcasecmp(args[1], "source") == 0) { p->option |= OPTION_SOURCE; } else if (strcasecmp(args[1], "sign") == 0) { p->option |= OPTION_SIGN; } else if (strcasecmp(args[1], "packchk") == 0) { p->option |= OPTION_PACK_CHECK; } else if (strcasecmp(args[1], "failover") == 0) { p->option |= OPTION_FAILOVER; if (argc != 5) { errmsg = "parameter 'interv'(s) 'timeout'(ms) 'maxtry' needed after keyword 'failover'"; goto error; } p->interv = atoi(args[2]); p->timeout = atoi(args[3]); p->maxtry = atoi(args[4]); if (p->interv <= 0) p->interv = 10; if (p->timeout <= 0) p->timeout = 3000; if (p->maxtry <= 0) p->maxtry = 3; } else { printf("line %d: unknown keyword '%s'\n", linenum, args[1]); goto error; } } else if (strcasecmp(args[0], "server") == 0) { struct radproxy_backend_server *s = parse_server(linenum, args+1, argc-1); if (s) { struct radproxy_backend_server **q = realloc(p->servers, (p->server_cnt+1)*sizeof(struct radproxy_back_server*)); if (q) { q[p->server_cnt] = s; p->servers = q; p->server_cnt += 1; } } else { goto error; } } else if (strcasecmp(args[0], "name") == 0) { if (argc >= 2) { if (p->name) free(p->name); p->name = strdup(args[1]); } } } break; } return 0; error: if (errmsg) printf("line %d: %s\n", linenum, errmsg); return 1; }
/* * Allocate the thread pool, and seed it with an initial number * of threads. * * FIXME: What to do on a SIGHUP??? */ int thread_pool_init(CONF_SECTION *cs, int spawn_flag) { int i, rcode; CONF_SECTION *pool_cf; time_t now; now = time(NULL); /* * We're not spawning new threads, don't do * anything. */ if (!spawn_flag) return 0; /* * After a SIGHUP, we don't over-write the previous values. */ if (!pool_initialized) { /* * Initialize the thread pool to some reasonable values. */ memset(&thread_pool, 0, sizeof(THREAD_POOL)); thread_pool.head = NULL; thread_pool.tail = NULL; thread_pool.total_threads = 0; thread_pool.max_thread_num = 1; thread_pool.cleanup_delay = 5; thread_pool.spawn_flag = spawn_flag; #ifdef WNOHANG if ((pthread_mutex_init(&thread_pool.wait_mutex,NULL) != 0)) { radlog(L_ERR, "FATAL: Failed to initialize wait mutex: %s", strerror(errno)); return -1; } /* * Create the hash table of child PID's */ thread_pool.waiters = fr_hash_table_create(pid_hash, pid_cmp, free); if (!thread_pool.waiters) { radlog(L_ERR, "FATAL: Failed to set up wait hash"); return -1; } #endif } pool_cf = cf_subsection_find_next(cs, NULL, "thread"); if (!pool_cf) { radlog(L_ERR, "FATAL: Attempting to start in multi-threaded mode with no thread configuration in radiusd.conf"); return -1; } if (cf_section_parse(pool_cf, NULL, thread_config) < 0) { return -1; } /* * Catch corner cases. */ if (thread_pool.min_spare_threads < 1) thread_pool.min_spare_threads = 1; if (thread_pool.max_spare_threads < 1) thread_pool.max_spare_threads = 1; if (thread_pool.max_spare_threads < thread_pool.min_spare_threads) thread_pool.max_spare_threads = thread_pool.min_spare_threads; /* * The pool has already been initialized. Don't spawn * new threads, and don't forget about forked children, */ if (pool_initialized) { return 0; } /* * Initialize the queue of requests. */ memset(&thread_pool.semaphore, 0, sizeof(thread_pool.semaphore)); rcode = sem_init(&thread_pool.semaphore, 0, SEMAPHORE_LOCKED); if (rcode != 0) { radlog(L_ERR, "FATAL: Failed to initialize semaphore: %s", strerror(errno)); return -1; } rcode = pthread_mutex_init(&thread_pool.queue_mutex,NULL); if (rcode != 0) { radlog(L_ERR, "FATAL: Failed to initialize queue mutex: %s", strerror(errno)); return -1; } /* * Allocate multiple fifos. */ for (i = 0; i < RAD_LISTEN_MAX; i++) { thread_pool.fifo[i] = fr_fifo_create(65536, NULL); if (!thread_pool.fifo[i]) { radlog(L_ERR, "FATAL: Failed to set up request fifo"); return -1; } } #ifdef HAVE_OPENSSL_CRYPTO_H /* * If we're linking with OpenSSL too, then we need * to set up the mutexes and enable the thread callbacks. */ if (!setup_ssl_mutexes()) { radlog(L_ERR, "FATAL: Failed to set up SSL mutexes"); return -1; } #endif /* * Create a number of waiting threads. * * If we fail while creating them, do something intelligent. */ for (i = 0; i < thread_pool.start_threads; i++) { if (spawn_thread(now) == NULL) { return -1; } } DEBUG2("Thread pool initialized"); pool_initialized = TRUE; return 0; }