/** Setup an LDAP sync request * * Allocates a request, with request/reply packets. * * Sets various fields in the request->packet with information from the file descriptor * we received from libldap. * * @param[in] listen The common listener encapsulating the libldap fd. * @param[in] inst of the proto_ldap_sync module. * @param[in] sync_id the unique identifier of the sync. * @return * - A new request on success. * - NULL on error. */ static REQUEST *proto_ldap_request_setup(rad_listen_t *listen, proto_ldap_inst_t *inst, int sync_id) { TALLOC_CTX *ctx; RADIUS_PACKET *packet; REQUEST *request; ctx = talloc_pool(NULL, main_config->talloc_pool_size); if (!ctx) return NULL; talloc_set_name_const(ctx, "ldap_inst_pool"); packet = fr_radius_alloc(ctx, false); packet->sockfd = listen->fd; packet->id = sync_id; packet->src_ipaddr = inst->dst_ipaddr; packet->src_port = inst->dst_port; packet->dst_ipaddr = inst->src_ipaddr; packet->dst_port = inst->src_port; gettimeofday(&packet->timestamp, NULL); request = request_setup(ctx, listen, packet, inst->client, NULL); if (!request) return NULL; request->process = request_queued; return request; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request = NULL; VALUE_PAIR *vp; VALUE_PAIR *filter_vps = NULL; /* * If the server was built with debugging enabled always install * the basic fatal signal handlers. */ #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("unittest"); exit(EXIT_FAILURE); } #endif if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; else progname++; debug_flag = 0; set_radius_dir(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&main_config, 0, sizeof(main_config)); main_config.myip.af = AF_UNSPEC; main_config.port = 0; main_config.name = "radiusd"; /* * The tests should have only IPs, not host names. */ fr_hostname_lookups = false; /* * We always log to stdout. */ fr_log_fp = stdout; default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; /* Process the options. */ while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:xX")) != EOF) { switch (argval) { case 'd': set_radius_dir(NULL, optarg); break; case 'D': main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filter_file = optarg; break; case 'h': usage(0); break; case 'i': input_file = optarg; break; case 'm': main_config.debug_memory = true; break; case 'M': memory_report = true; main_config.debug_memory = true; break; case 'n': main_config.name = optarg; break; case 'o': output_file = optarg; break; case 'X': debug_flag += 2; main_config.log_auth = true; main_config.log_auth_badpass = true; main_config.log_auth_goodpass = true; break; case 'x': debug_flag++; break; default: usage(1); break; } } if (debug_flag) { version(); } fr_debug_flag = debug_flag; /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radiusd"); exit(EXIT_FAILURE); } /* Read the configuration files, BEFORE doing anything else. */ if (main_config_init() < 0) { rcode = EXIT_FAILURE; goto finish; } /* * Load the modules */ if (modules_init(main_config.config) < 0) { rcode = EXIT_FAILURE; goto finish; } fr_state_init(); /* Set the panic action (if required) */ if (main_config.panic_action && #ifndef NDEBUG !getenv("PANIC_ACTION") && #endif (fr_fault_setup(main_config.panic_action, argv[0]) < 0)) { rcode = EXIT_FAILURE; goto finish; } setlinebuf(stdout); /* unbuffered output */ if (!input_file || (strcmp(input_file, "-") == 0)) { fp = stdin; } else { fp = fopen(input_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", input_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } /* * Grab the VPs from stdin, or from the file. */ request = request_setup(fp); if (!request) { fprintf(stderr, "Failed reading input: %s\n", fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * No filter file, OR there's no more input, OR we're * reading from a file, and it's different from the * filter file. */ if (!filter_file || filedone || ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) { if (output_file) { fclose(fp); fp = NULL; } filedone = false; } /* * There is a filter file. If necessary, open it. If we * already are reading it via "input_file", then we don't * need to re-open it. */ if (filter_file) { if (!fp) { fp = fopen(filter_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } if (readvp2(request, &filter_vps, fp, &filedone) < 0) { fprintf(stderr, "Failed reading attributes from %s: %s\n", filter_file, fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * FIXME: loop over input packets. */ fclose(fp); } rad_virtual_server(request); if (!output_file || (strcmp(output_file, "-") == 0)) { fp = stdout; } else { fp = fopen(output_file, "w"); if (!fp) { fprintf(stderr, "Failed writing %s: %s\n", output_file, strerror(errno)); exit(EXIT_FAILURE); } } print_packet(fp, request->reply); if (output_file) fclose(fp); /* * Update the list with the response type. */ vp = radius_paircreate(request->reply, &request->reply->vps, PW_RESPONSE_PACKET_TYPE, 0); vp->vp_integer = request->reply->code; { VALUE_PAIR const *failed[2]; if (filter_vps && !pairvalidate(failed, filter_vps, request->reply->vps)) { pairvalidate_debug(request, failed); fr_perror("Output file %s does not match attributes in filter %s", output_file ? output_file : input_file, filter_file); rcode = EXIT_FAILURE; goto finish; } } INFO("Exiting normally"); finish: talloc_free(request); /* * Detach any modules. */ modules_free(); xlat_free(); /* modules may have xlat's */ fr_state_delete(); /* * Free the configuration items. */ main_config_free(); if (memory_report) { INFO("Allocated memory at time of report:"); fr_log_talloc_report(NULL); } return rcode; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request; VALUE_PAIR *filter_vps = NULL; if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; else progname++; debug_flag = 0; radius_dir = talloc_strdup(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&mainconfig, 0, sizeof(mainconfig)); mainconfig.myip.af = AF_UNSPEC; mainconfig.port = -1; mainconfig.name = "radiusd"; /* * We always log to stdout. */ fr_log_fp = stdout; default_log.dest = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; /* Process the options. */ while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:xX")) != EOF) { switch(argval) { case 'd': if (radius_dir) { rad_const_free(radius_dir); } radius_dir = talloc_strdup(NULL, optarg); break; case 'D': mainconfig.dictionary_dir = talloc_strdup(NULL, optarg); break; case 'f': filter_file = optarg; break; case 'h': usage(0); break; case 'i': input_file = optarg; break; case 'm': mainconfig.debug_memory = 1; break; case 'M': memory_report = 1; mainconfig.debug_memory = 1; break; case 'n': mainconfig.name = optarg; break; case 'o': output_file = optarg; break; case 'X': debug_flag += 2; mainconfig.log_auth = true; mainconfig.log_auth_badpass = true; mainconfig.log_auth_goodpass = true; break; case 'x': debug_flag++; break; default: usage(1); break; } } if (memory_report) { talloc_enable_null_tracking(); #ifdef WITH_VERIFY_PTR talloc_set_abort_fn(die_horribly); #endif } talloc_set_log_fn(log_talloc); if (debug_flag) { version(); } fr_debug_flag = debug_flag; /* Read the configuration files, BEFORE doing anything else. */ if (read_mainconfig(0) < 0) { exit(EXIT_FAILURE); } setlinebuf(stdout); /* unbuffered output */ if (!input_file || (strcmp(input_file, "-") == 0)) { fp = stdin; } else { fp = fopen(input_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", input_file, strerror(errno)); exit(EXIT_FAILURE); } } /* * Grab the VPs from stdin, or from the file. */ request = request_setup(fp); if (!request) { fprintf(stderr, "Failed reading input: %s\n", fr_strerror()); exit(EXIT_FAILURE); } /* * No filter file, OR there's no more input, OR we're * reading from a file, and it's different from the * filter file. */ if (!filter_file || filedone || ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) { if (output_file) { fclose(fp); fp = NULL; } filedone = 0; } /* * There is a filter file. If necessary, open it. If we * already are reading it via "input_file", then we don't * need to re-open it. */ if (filter_file) { if (!fp) { fp = fopen(filter_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno)); exit(EXIT_FAILURE); } } filter_vps = readvp2(request, fp, &filedone, "radiusd"); if (!filter_vps) { fprintf(stderr, "Failed reading attributes from %s: %s\n", filter_file, fr_strerror()); exit(EXIT_FAILURE); } /* * FIXME: loop over input packets. */ fclose(fp); } rad_virtual_server(request); if (!output_file || (strcmp(output_file, "-") == 0)) { fp = stdout; } else { fp = fopen(output_file, "w"); if (!fp) { fprintf(stderr, "Failed writing %s: %s\n", output_file, strerror(errno)); exit(EXIT_FAILURE); } } print_packet(fp, request->reply); if (output_file) fclose(fp); if (filter_vps && !pairvalidate(filter_vps, request->reply->vps)) { fprintf(stderr, "Output file %s does not match attributes in filter %s\n", output_file, filter_file); exit(EXIT_FAILURE); } talloc_free(request); INFO("Exiting normally."); /* * Detach any modules. */ detach_modules(); xlat_free(); /* modules may have xlat's */ /* * Free the configuration items. */ free_mainconfig(); rad_const_free(radius_dir); if (memory_report) { INFO("Allocated memory at time of report:"); log_talloc_report(NULL); } return rcode; }
END_TEST /* * request */ START_TEST(test_quit) { #define QUIT "quit" #define SERIALIZED "*1\r\n$4\r\n" QUIT "\r\n" #define INVALID "*2\r\n$4\r\n" QUIT "\r\n$3\r\nnow\r\n" int ret; struct element *el; test_reset(); req->type = REQ_QUIT; el = array_push(req->token); el->type = ELEM_BULK; el->bstr = (struct bstring){sizeof(QUIT) - 1, QUIT}; ret = compose_req(&buf, req); ck_assert_int_eq(ret, sizeof(SERIALIZED) - 1); ck_assert_int_eq(cc_bcmp(buf->rpos, SERIALIZED, ret), 0); el->type = ELEM_UNKNOWN; /* this effectively resets *el */ request_reset(req); ck_assert_int_eq(parse_req(req, buf), PARSE_OK); ck_assert_int_eq(req->type, REQ_QUIT); ck_assert_int_eq(req->token->nelem, 1); el = array_first(req->token); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(cc_bcmp(el->bstr.data, QUIT, sizeof(QUIT) - 1), 0); /* invalid number of arguments */ test_reset(); buf_write(buf, INVALID, sizeof(INVALID) - 1); ck_assert_int_eq(parse_req(req, buf), PARSE_EINVALID); #undef INVALID #undef SERIALIZED #undef QUIT } END_TEST START_TEST(test_ping) { #define PING "ping" #define VAL "hello" #define S_PING "*1\r\n$4\r\n" PING "\r\n" #define S_ECHO "*2\r\n$4\r\n" PING "\r\n$5\r\nhello\r\n" int ret; struct element *el; test_reset(); /* simple ping */ buf_write(buf, S_PING, sizeof(S_PING) - 1); ck_assert_int_eq(parse_req(req, buf), PARSE_OK); ck_assert_int_eq(req->type, REQ_PING); /* ping as echo */ test_reset(); req->type = REQ_PING; el = array_push(req->token); el->type = ELEM_BULK; el->bstr = (struct bstring){sizeof(PING) - 1, PING}; el = array_push(req->token); el->type = ELEM_BULK; el->bstr = (struct bstring){sizeof(VAL) - 1, VAL}; ret = compose_req(&buf, req); ck_assert_int_eq(ret, sizeof(S_ECHO) - 1); ck_assert_int_eq(cc_bcmp(buf->rpos, S_ECHO, ret), 0); el->type = ELEM_UNKNOWN; /* resets *el */ request_reset(req); ck_assert_int_eq(parse_req(req, buf), PARSE_OK); ck_assert_int_eq(req->type, REQ_PING); ck_assert_int_eq(req->token->nelem, 2); el = array_first(req->token); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(cc_bcmp(el->bstr.data, PING, sizeof(PING) - 1), 0); el = array_get(req->token, 1); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(cc_bcmp(el->bstr.data, VAL, sizeof(VAL) - 1), 0); #undef S_ECHO #undef ECHO #undef S_PING #undef QUIT } END_TEST START_TEST(test_unfin_req) { char *token[4] = { "*2\r\n", "*2\r\n$3\r\n", "*2\r\n$3\r\nfoo\r\n", "*2\r\n$3\r\nfoo\r\n$3\r\n", }; for (int i = 0; i < 4; i++) { char *pos; size_t len; len = strlen(token[i]); buf_reset(buf); buf_write(buf, token[i], len); pos = buf->rpos; ck_assert_int_eq(parse_req(req, buf), PARSE_EUNFIN); ck_assert(buf->rpos == pos); } } END_TEST /* * response */ START_TEST(test_ok) { #define OK "OK" #define SERIALIZED "+" OK "\r\n" int ret; struct element *el; test_reset(); rsp->type = ELEM_STR; el = array_push(rsp->token); el->type = ELEM_STR; el->bstr = (struct bstring){sizeof(OK) - 1, OK}; ret = compose_rsp(&buf, rsp); ck_assert_int_eq(ret, sizeof(SERIALIZED) - 1); ck_assert_int_eq(cc_bcmp(buf->rpos, SERIALIZED, ret), 0); el->type = ELEM_UNKNOWN; /* resets *el */ response_reset(rsp); ck_assert_int_eq(parse_rsp(rsp, buf), PARSE_OK); ck_assert_int_eq(rsp->type, ELEM_STR); ck_assert_int_eq(rsp->token->nelem, 1); el = array_first(rsp->token); ck_assert_int_eq(el->type, ELEM_STR); ck_assert_int_eq(cc_bcmp(el->bstr.data, OK, sizeof(OK) - 1), 0); #undef SERIALIZED #undef OK } END_TEST START_TEST(test_array_reply) { #define SERIALIZED "*5\r\n:-10\r\n$-1\r\n-ERR invalid arg\r\n+foo\r\n$5\r\nHELLO\r\n" size_t len = sizeof(SERIALIZED) - 1; struct element *el; test_reset(); buf_write(buf, SERIALIZED, len); ck_assert_int_eq(parse_rsp(rsp, buf), PARSE_OK); ck_assert_int_eq(rsp->type, ELEM_ARRAY); ck_assert_int_eq(rsp->token->nelem, 5); el = array_first(rsp->token); ck_assert_int_eq(el->type, ELEM_INT); el = array_get(rsp->token, 1); ck_assert_int_eq(el->type, ELEM_NIL); el = array_get(rsp->token, 2); ck_assert_int_eq(el->type, ELEM_ERR); el = array_get(rsp->token, 3); ck_assert_int_eq(el->type, ELEM_STR); el = array_get(rsp->token, 4); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(el->bstr.len, 5); ck_assert_int_eq(cc_bcmp(el->bstr.data, "HELLO", 5), 0); ck_assert_int_eq(buf_rsize(buf), 0); ck_assert_int_eq(compose_rsp(&buf, rsp), len); ck_assert_int_eq(buf_rsize(buf), len); ck_assert_int_eq(cc_bcmp(buf->rpos, SERIALIZED, len), 0); #undef SERIALIZED } END_TEST /* * edge cases */ START_TEST(test_empty_buf) { struct element el; test_reset(); ck_assert(!token_is_array(buf)); ck_assert_int_eq(parse_element(&el, buf), PARSE_EUNFIN); ck_assert_int_eq(parse_rsp(rsp, buf), PARSE_EUNFIN); ck_assert_int_eq(parse_req(req, buf), PARSE_EUNFIN); } END_TEST /* * request/response pool */ START_TEST(test_req_pool_basic) { #define POOL_SIZE 10 int i; struct request *reqs[POOL_SIZE]; request_options_st options = { .request_ntoken = {.type = OPTION_TYPE_UINT, .val.vuint = REQ_NTOKEN}, .request_poolsize = {.type = OPTION_TYPE_UINT, .val.vuint = POOL_SIZE}}; request_setup(&options, NULL); for (i = 0; i < POOL_SIZE; i++) { reqs[i] = request_borrow(); ck_assert_msg(reqs[i] != NULL, "expected to borrow a request"); } ck_assert_msg(request_borrow() == NULL, "expected request pool to be depleted"); for (i = 0; i < POOL_SIZE; i++) { request_return(&reqs[i]); ck_assert_msg(reqs[i] == NULL, "expected request to be nulled after return"); } request_teardown(); #undef POOL_SIZE } END_TEST START_TEST(test_rsp_pool_basic) { #define POOL_SIZE 10 int i; struct response *rsps[POOL_SIZE]; response_options_st options = { .response_ntoken = {.type = OPTION_TYPE_UINT, .val.vuint = RSP_NTOKEN}, .response_poolsize = {.type = OPTION_TYPE_UINT, .val.vuint = POOL_SIZE}}; response_setup(&options, NULL); for (i = 0; i < POOL_SIZE; i++) { rsps[i] = response_borrow(); ck_assert_msg(rsps[i] != NULL, "expected to borrow a response"); } ck_assert_msg(response_borrow() == NULL, "expected response pool to be depleted"); for (i = 0; i < POOL_SIZE; i++) { response_return(&rsps[i]); ck_assert_msg(rsps[i] == NULL, "expected response to be nulled after return"); } response_teardown(); #undef POOL_SIZE } END_TEST /* * test suite */ static Suite * redis_suite(void) { Suite *s = suite_create(SUITE_NAME); /* token */ TCase *tc_token = tcase_create("token"); suite_add_tcase(s, tc_token); tcase_add_test(tc_token, test_simple_string); tcase_add_test(tc_token, test_error); tcase_add_test(tc_token, test_integer); tcase_add_test(tc_token, test_bulk_string); tcase_add_test(tc_token, test_array); tcase_add_test(tc_token, test_nil_bulk); tcase_add_test(tc_token, test_unfin_token); /* basic requests */ TCase *tc_request = tcase_create("request"); suite_add_tcase(s, tc_request); tcase_add_test(tc_request, test_quit); tcase_add_test(tc_request, test_ping); tcase_add_test(tc_request, test_unfin_req); /* basic responses */ TCase *tc_response = tcase_create("response"); suite_add_tcase(s, tc_response); tcase_add_test(tc_response, test_ok); tcase_add_test(tc_response, test_array_reply); /* edge cases */ TCase *tc_edge = tcase_create("edge cases"); suite_add_tcase(s, tc_edge); tcase_add_test(tc_edge, test_empty_buf); /* req/rsp objects, pooling */ TCase *tc_pool = tcase_create("request/response pool"); suite_add_tcase(s, tc_pool); tcase_add_test(tc_pool, test_req_pool_basic); tcase_add_test(tc_pool, test_rsp_pool_basic); return s; } /* TODO(yao): move main to a different file, keep most test files main-less */ int main(void) { int nfail; /* setup */ test_setup(); Suite *suite = redis_suite(); SRunner *srunner = srunner_create(suite); srunner_set_log(srunner, DEBUG_LOG); srunner_run_all(srunner, CK_ENV); /* set CK_VEBOSITY in ENV to customize */ nfail = srunner_ntests_failed(srunner); srunner_free(srunner); /* teardown */ test_teardown(); return (nfail == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }
static void setup(void) { char *fname = NULL; uint64_t intvl; if (atexit(teardown) != 0) { log_stderr("cannot register teardown procedure with atexit()"); exit(EX_OSERR); /* only failure comes from NOMEM */ } /* Setup logging first */ log_setup(&stats.log); if (debug_setup(&setting.debug) != CC_OK) { log_stderr("debug log setup failed"); exit(EX_CONFIG); } /* setup top-level application options */ if (option_bool(&setting.ds.daemonize)) { daemonize(); } fname = option_str(&setting.ds.pid_filename); if (fname != NULL) { /* to get the correct pid, call create_pidfile after daemonize */ create_pidfile(fname); } /* setup library modules */ buf_setup(&setting.buf, &stats.buf); dbuf_setup(&setting.dbuf, &stats.dbuf); event_setup(&stats.event); sockio_setup(&setting.sockio, &stats.sockio); tcp_setup(&setting.tcp, &stats.tcp); timing_wheel_setup(&stats.timing_wheel); /* setup pelikan modules */ time_setup(&setting.time); procinfo_setup(&stats.procinfo); request_setup(&setting.request, &stats.request); response_setup(&setting.response, &stats.response); parse_setup(&stats.parse_req, NULL); compose_setup(NULL, &stats.compose_rsp); slab_setup(&setting.slab, &stats.slab); process_setup(&setting.process, &stats.process); admin_process_setup(); core_admin_setup(&setting.admin); core_server_setup(&setting.server, &stats.server); core_worker_setup(&setting.worker, &stats.worker); /* adding recurring events to maintenance/admin thread */ intvl = option_uint(&setting.ds.dlog_intvl); if (core_admin_register(intvl, debug_log_flush, NULL) == NULL) { log_stderr("Could not register timed event to flush debug log"); goto error; } return; error: if (fname != NULL) { remove_pidfile(fname); } /* since we registered teardown with atexit, it'll be called upon exit */ exit(EX_CONFIG); }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request = NULL; VALUE_PAIR *vp; VALUE_PAIR *filter_vps = NULL; bool xlat_only = false; fr_state_tree_t *state = NULL; fr_talloc_fault_setup(); /* * If the server was built with debugging enabled always install * the basic fatal signal handlers. */ #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("unittest"); exit(EXIT_FAILURE); } #endif rad_debug_lvl = 0; set_radius_dir(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&main_config, 0, sizeof(main_config)); main_config.name = "unittest"; /* * The tests should have only IPs, not host names. */ fr_hostname_lookups = false; /* * We always log to stdout. */ fr_log_fp = stdout; default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; /* Process the options. */ while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:O:xX")) != EOF) { switch (argval) { case 'd': set_radius_dir(NULL, optarg); break; case 'D': main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filter_file = optarg; break; case 'h': usage(0); break; case 'i': input_file = optarg; break; case 'm': main_config.debug_memory = true; break; case 'M': memory_report = true; main_config.debug_memory = true; break; case 'n': main_config.name = optarg; break; case 'o': output_file = optarg; break; case 'O': if (strcmp(optarg, "xlat_only") == 0) { xlat_only = true; break; } fprintf(stderr, "Unknown option '%s'\n", optarg); exit(EXIT_FAILURE); case 'X': rad_debug_lvl += 2; main_config.log_auth = true; main_config.log_auth_badpass = true; main_config.log_auth_goodpass = true; break; case 'x': rad_debug_lvl++; break; default: usage(1); break; } } if (rad_debug_lvl) version_print(); fr_debug_lvl = rad_debug_lvl; /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("%s", main_config.name); exit(EXIT_FAILURE); } /* * Initialising OpenSSL once, here, is safer than having individual modules do it. */ #ifdef HAVE_OPENSSL_CRYPTO_H if (tls_global_init() < 0) { rcode = EXIT_FAILURE; goto finish; } #endif if (xlat_register(NULL, "poke", xlat_poke, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN) < 0) { rcode = EXIT_FAILURE; goto finish; } if (map_proc_register(NULL, "test-fail", mod_map_proc, NULL, NULL, 0) < 0) { rcode = EXIT_FAILURE; goto finish; } /* Read the configuration files, BEFORE doing anything else. */ if (main_config_init() < 0) { exit_failure: rcode = EXIT_FAILURE; goto finish; } /* * Setup dummy virtual server */ cf_section_add(main_config.config, cf_section_alloc(main_config.config, "server", "unit_test")); /* * Initialize Auth-Type, etc. in the virtual servers * before loading the modules. Some modules need those * to be defined. */ if (virtual_servers_bootstrap(main_config.config) < 0) goto exit_failure; /* * Bootstrap the modules. This links to them, and runs * their "bootstrap" routines. * * After this step, all dynamic attributes, xlats, etc. are defined. */ if (modules_bootstrap(main_config.config) < 0) exit(EXIT_FAILURE); /* * Load the modules */ if (modules_init(main_config.config) < 0) goto exit_failure; /* * And then load the virtual servers. */ if (virtual_servers_init(main_config.config) < 0) goto exit_failure; state = fr_state_tree_init(NULL, main_config.max_requests * 2, 10); /* * Set the panic action (if required) */ { char const *panic_action = NULL; panic_action = getenv("PANIC_ACTION"); if (!panic_action) panic_action = main_config.panic_action; if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) { fr_perror("%s", main_config.name); exit(EXIT_FAILURE); } } setlinebuf(stdout); /* unbuffered output */ if (!input_file || (strcmp(input_file, "-") == 0)) { fp = stdin; } else { fp = fopen(input_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", input_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } /* * For simplicity, read xlat's. */ if (xlat_only) { if (!do_xlats(input_file, fp)) rcode = EXIT_FAILURE; if (input_file) fclose(fp); goto finish; } /* * Grab the VPs from stdin, or from the file. */ request = request_setup(fp); if (!request) { fprintf(stderr, "Failed reading input: %s\n", fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * No filter file, OR there's no more input, OR we're * reading from a file, and it's different from the * filter file. */ if (!filter_file || filedone || ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) { if (output_file) { fclose(fp); fp = NULL; } filedone = false; } /* * There is a filter file. If necessary, open it. If we * already are reading it via "input_file", then we don't * need to re-open it. */ if (filter_file) { if (!fp) { fp = fopen(filter_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } if (fr_pair_list_afrom_file(request, &filter_vps, fp, &filedone) < 0) { fprintf(stderr, "Failed reading attributes from %s: %s\n", filter_file, fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * FIXME: loop over input packets. */ fclose(fp); } rad_virtual_server(request); if (!output_file || (strcmp(output_file, "-") == 0)) { fp = stdout; } else { fp = fopen(output_file, "w"); if (!fp) { fprintf(stderr, "Failed writing %s: %s\n", output_file, strerror(errno)); exit(EXIT_FAILURE); } } print_packet(fp, request->reply); if (output_file) fclose(fp); /* * Update the list with the response type. */ vp = radius_pair_create(request->reply, &request->reply->vps, PW_RESPONSE_PACKET_TYPE, 0); vp->vp_integer = request->reply->code; { VALUE_PAIR const *failed[2]; if (filter_vps && !fr_pair_validate(failed, filter_vps, request->reply->vps)) { fr_pair_validate_debug(request, failed); fr_perror("Output file %s does not match attributes in filter %s (%s)", output_file ? output_file : input_file, filter_file, fr_strerror()); rcode = EXIT_FAILURE; goto finish; } } INFO("Exiting normally"); finish: talloc_free(request); talloc_free(state); /* * Free the configuration items. */ main_config_free(); /* * Detach any modules. */ modules_free(); xlat_unregister(NULL, "poke", xlat_poke); xlat_free(); /* modules may have xlat's */ if (memory_report) { INFO("Allocated memory at time of report:"); fr_log_talloc_report(NULL); } return rcode; }