/** Sends a REST (HTTP) request. * * Send the actual REST request to the server. The response will be handled by * the numerous callbacks configured in rest_request_config. * * @param[in] t Servicing this request. * @param[in] request Current request. * @param[in] handle to use. * @return * - 0 on success. * - -1 on failure. */ int rest_io_request_enqueue(rlm_rest_thread_t *t, REQUEST *request, void *handle) { rlm_rest_handle_t *randle = handle; CURL *candle = randle->candle; CURLcode ret; REQUEST_VERIFY(request); /* * Stick the current request in the curl handle's * private data. This makes it simple to resume * the request in the demux function later... */ ret = curl_easy_setopt(candle, CURLOPT_PRIVATE, request); if (ret != CURLE_OK) { REDEBUG("Request failed: %i - %s", ret, curl_easy_strerror(ret)); return -1; } /* * Increment here, else the debug output looks * messed up is curl_multi_add_handle triggers * event loop modifications calls immediately. */ t->transfers++; ret = curl_multi_add_handle(t->mandle, candle); if (ret != CURLE_OK) { t->transfers--; REDEBUG("Request failed: %i - %s", ret, curl_easy_strerror(ret)); return -1; } return 0; }
/** Run the interpreter after creating a subrequest. * * Just run some "unlang", but don't do anything else. * * This is a shim function added to 'fake' requests by the subrequest and parallel keywords. */ fr_io_final_t unlang_io_process_interpret(UNUSED void const *instance, REQUEST *request, fr_io_action_t action) { rlm_rcode_t rcode; REQUEST_VERIFY(request); /* * Pass this through asynchronously to the module which * is waiting for something to happen. */ if (action != FR_IO_ACTION_RUN) { unlang_interpret_signal(request, (fr_state_signal_t) action); return FR_IO_DONE; } rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; /* * Don't bother setting request->reply->code. */ return FR_IO_DONE; }
/** De-queue curl requests and wake up the requests that initiated them * * @param[in] thread holding the requests to re-enliven. * @param[in] mandle to dequeue curl easy handles/responses from. */ static inline void _rest_io_demux(rlm_rest_thread_t *thread, CURLM *mandle) { struct CURLMsg *m; int msg_queued = 0; while ((m = curl_multi_info_read(mandle, &msg_queued))) { switch (m->msg) { case CURLMSG_DONE: { REQUEST *request = NULL; CURL *candle = m->easy_handle; CURLcode ret; rad_assert(candle); thread->transfers--; ret = curl_easy_getinfo(candle, CURLINFO_PRIVATE, &request); if (!fr_cond_assert_msg(ret == CURLE_OK, "Failed retrieving request data from CURL easy handle (candle)")) { curl_multi_remove_handle(mandle, candle); return; } REQUEST_VERIFY(request); /* * If the request failed, say why... */ if (m->data.result != CURLE_OK) { REDEBUG("REST request failed: %s (%i)", curl_easy_strerror(m->data.result), m->data.result); } /* * Looks like this needs to be done last, * else m->data.result ends up being junk. */ curl_multi_remove_handle(mandle, candle); unlang_interpret_resumable(request); } break; default: #ifndef NDEBUG DEBUG4("Got unknown msg (%i) when dequeueing curl responses", msg_queued); #endif break; } } }
/** Process events while the request is queued. * * \dot * digraph request_queued { * request_queued -> done [ label = "TIMER >= max_request_time" ]; * request_queued -> request_running [ label = "RUNNING" ]; * } * \enddot * * @param[in] request to process. * @param[in] action If something has signalled that the request should stop * being processed. */ static void request_queued(REQUEST *request, fr_state_signal_t action) { REQUEST_VERIFY(request); switch (action) { case FR_SIGNAL_RUN: request->process = request_running; request->process(request, action); break; case FR_SIGNAL_CANCEL: (void) fr_heap_extract(request->backlog, request); //request_delete(request); break; default: break; } }
/** Yield a request back to the interpreter from within a module * * This passes control of the request back to the unlang interpreter, setting * callbacks to execute when the request is 'signalled' asynchronously, or whatever * timer or I/O event the module was waiting for occurs. * * @note The module function which calls #unlang_module_yield should return control * of the C stack to the unlang interpreter immediately after calling #unlang_module_yield. * A common pattern is to use ``return unlang_module_yield(...)``. * * @param[in] request The current request. * @param[in] resume Called on unlang_resumable(). * @param[in] signal Called on unlang_action(). * @param[in] rctx to pass to the callbacks. * @return * - RLM_MODULE_YIELD on success. * - RLM_MODULE_FAIL (or asserts) if the current frame is not a module call or * resume frame. */ rlm_rcode_t unlang_module_yield(REQUEST *request, fr_unlang_module_resume_t resume, fr_unlang_module_signal_t signal, void *rctx) { unlang_stack_t *stack = request->stack; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; unlang_resume_t *mr; rad_assert(stack->depth > 0); REQUEST_VERIFY(request); /* Check the yielded request is sane */ switch (frame->instruction->type) { case UNLANG_TYPE_MODULE: mr = unlang_resume_alloc(request, (void *)resume, (void *)signal, rctx); if (!fr_cond_assert(mr)) { return RLM_MODULE_FAIL; } return RLM_MODULE_YIELD; case UNLANG_TYPE_RESUME: mr = talloc_get_type_abort(frame->instruction, unlang_resume_t); rad_assert(mr->parent->type == UNLANG_TYPE_MODULE); /* * Re-use the current RESUME frame, but over-ride * the callbacks and context. */ mr->resume = (void *)resume; mr->signal = (void *)signal; mr->rctx = rctx; return RLM_MODULE_YIELD; default: rad_assert(0); return RLM_MODULE_FAIL; } }
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action) { rlm_rcode_t rcode; CONF_SECTION *unlang; REQUEST_VERIFY(request); /* * Pass this through asynchronously to the module which * is waiting for something to happen. */ if (action != FR_IO_ACTION_RUN) { unlang_signal(request, (fr_state_signal_t) action); return FR_IO_DONE; } switch (request->request_state) { case REQUEST_INIT: request->component = "radius"; unlang = cf_section_find(request->server_cs, "new", "client"); if (!unlang) { RWDEBUG("Failed to find 'new client' section"); request->reply->code = FR_CODE_ACCESS_REJECT; goto send_reply; } RDEBUG("Running 'new client' from file %s", cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = FR_CODE_ACCESS_ACCEPT; break; case RLM_MODULE_FAIL: case RLM_MODULE_HANDLED: request->reply->code = 0; /* don't reply */ break; default: case RLM_MODULE_REJECT: request->reply->code = FR_CODE_ACCESS_REJECT; break; } unlang = cf_section_find(request->server_cs, "add", "client"); if (!unlang) goto send_reply; rerun_nak: RDEBUG("Running '%s client' from file %s", cf_section_name1(unlang), cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: case RLM_MODULE_HANDLED: /* reply is already set */ break; default: /* * If we over-ride an ACK with a NAK, run * the NAK section. */ if (request->reply->code != FR_CODE_ACCESS_REJECT) { RWDEBUG("Failed running 'add client', trying 'deny client'."); deny: request->reply->code = FR_CODE_ACCESS_REJECT; unlang = cf_section_find(request->server_cs, "deny", "client"); if (unlang) goto rerun_nak; RWDEBUG("Not running 'deny client' section as it does not exist"); } break; } if (request->reply->code == FR_CODE_ACCESS_ACCEPT) { VALUE_PAIR *vp; vp = fr_pair_find_by_da(request->control, attr_freeradius_client_ip_address, TAG_ANY); if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_address, TAG_ANY); if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ip_prefix, TAG_ANY); if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_prefix, TAG_ANY); if (!vp) { ERROR("The 'control' list MUST contain a FreeRADIUS-Client.. IP address attribute"); goto deny; } vp = fr_pair_find_by_da(request->control, attr_freeradius_client_secret, TAG_ANY); if (!vp) { ERROR("The 'control' list MUST contain a FreeRADIUS-Client-Secret attribute"); goto deny; } /* * Else we're flexible. */ } send_reply: /* * This is an internally generated request. Don't print IP addresses. */ if (request->reply->code == FR_CODE_ACCESS_ACCEPT) { RDEBUG("Adding client"); } else { RDEBUG("Denying client"); } break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action) { VALUE_PAIR *vp; rlm_rcode_t rcode; CONF_SECTION *unlang; fr_dict_enum_t const *dv; REQUEST_VERIFY(request); /* * Pass this through asynchronously to the module which * is waiting for something to happen. */ if (action != FR_IO_ACTION_RUN) { unlang_interpret_signal(request, (fr_state_signal_t) action); return FR_IO_DONE; } switch (request->request_state) { case REQUEST_INIT: if (request->parent && RDEBUG_ENABLED) { RDEBUG("Received %s ID %i", fr_packet_codes[request->packet->code], request->packet->id); log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); } request->component = "radius"; /* * We can run CoA-Request or Disconnect-Request sections here */ dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->packet->code)); if (!dv) { REDEBUG("Failed to find value for &request:Packet-Type"); return FR_IO_FAIL; } unlang = cf_section_find(request->server_cs, "recv", dv->alias); if (!unlang) { REDEBUG("Failed to find 'recv %s' section", dv->alias); return FR_IO_FAIL; } RDEBUG("Running 'recv %s' from file %s", dv->alias, cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = request->packet->code + 1; /* ACK */ break; case RLM_MODULE_HANDLED: break; case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: request->reply->code = request->packet->code + 2; /* NAK */ break; } /* * Allow for over-ride of reply code. */ vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY); if (vp) request->reply->code = vp->vp_uint32; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias); if (!unlang) goto send_reply; /* * Note that for NAKs, we do NOT use * reject_delay. This is because we're acting as * a NAS, and we want to respond to the RADIUS * server as quickly as possible. */ rerun_nak: RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); rad_assert(request->log.unlang_indent == 0); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { /* * We need to send CoA-NAK back if Service-Type * is Authorize-Only. Rely on the user's policy * to do that. We're not a real NAS, so this * restriction doesn't (ahem) apply to us. */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: /* * If we over-ride an ACK with a NAK, run * the NAK section. */ if (request->reply->code == request->packet->code + 1) { dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); RWDEBUG("Failed running 'send %s', trying corresponding NAK section.", dv->alias); request->reply->code = request->packet->code + 2; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (!dv) goto send_reply; unlang = cf_section_find(request->server_cs, "send", dv->alias); if (unlang) goto rerun_nak; RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias); } /* * Else it was already a NAK or something else. */ break; case RLM_MODULE_HANDLED: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: /* reply code is already set */ break; } send_reply: gettimeofday(&request->reply->timestamp, NULL); /* * Check for "do not respond". */ if (request->reply->code == FR_CODE_DO_NOT_RESPOND) { RDEBUG("Not sending reply to client."); break; } if (request->parent && RDEBUG_ENABLED) { RDEBUG("Sending %s ID %i", fr_packet_codes[request->reply->code], request->reply->id); log_request_pair_list(L_DBG_LVL_1, request, request->reply->vps, ""); } break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request) { rlm_rcode_t rcode; CONF_SECTION *unlang; fr_dict_enum_t const *dv; VALUE_PAIR *vp; REQUEST_VERIFY(request); switch (request->request_state) { case REQUEST_INIT: if (request->parent && RDEBUG_ENABLED) { RDEBUG("Received %s ID %i", fr_packet_codes[request->packet->code], request->packet->id); log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); } request->component = "radius"; unlang = cf_section_find(request->server_cs, "recv", "Status-Server"); if (!unlang) { RWDEBUG("Failed to find 'recv Status-Server' section"); request->reply->code = FR_CODE_ACCESS_REJECT; goto send_reply; } RDEBUG("Running 'recv Status-Server' from file %s", cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = FR_CODE_ACCESS_ACCEPT; break; case RLM_MODULE_FAIL: case RLM_MODULE_HANDLED: request->reply->code = 0; /* don't reply */ break; default: case RLM_MODULE_REJECT: request->reply->code = FR_CODE_ACCESS_REJECT; break; } /* * Allow for over-ride of reply code. */ vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY); if (vp) request->reply->code = vp->vp_uint32; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias); if (!unlang) goto send_reply; rerun_nak: RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: case RLM_MODULE_HANDLED: /* reply is already set */ break; default: /* * If we over-ride an ACK with a NAK, run * the NAK section. */ if (request->reply->code != FR_CODE_ACCESS_REJECT) { dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); RWDEBUG("Failed running 'send %s', trying 'send Access-Reject'", dv->alias); request->reply->code = FR_CODE_ACCESS_REJECT; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (!dv) goto send_reply; unlang = cf_section_find(request->server_cs, "send", dv->alias); if (unlang) goto rerun_nak; RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias); } break; } send_reply: gettimeofday(&request->reply->timestamp, NULL); /* * Check for "do not respond". */ if (request->reply->code == FR_CODE_DO_NOT_RESPOND) { RDEBUG("Not sending reply to client."); break; } if (request->parent && RDEBUG_ENABLED) { RDEBUG("Sending %s ID %i", fr_packet_codes[request->reply->code], request->reply->id); log_request_pair_list(L_DBG_LVL_1, request, request->reply->vps, ""); } break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
/** Very simple state machine to process requests * * Unlike normal protocol requests which may have multiple distinct states, * we really only have REQUEST_INIT and REQUEST_RECV phases. * * Conversion of LDAPMessage to VALUE_PAIR structs is done in the listener * because we cannot easily duplicate the LDAPMessage to send it across to * the worker for parsing. * * Most LDAP directories can only handle between 2000-5000 modifications a second * so we're unlikely to be I/O or CPU bound using this division of responsibilities. * * @param[in] request to process. * @param[in] action If something has signalled that the request should stop * being processed. */ static void request_running(REQUEST *request, fr_state_signal_t action) { CONF_SECTION *unlang; char const *verb; char const *state; rlm_rcode_t rcode = RLM_MODULE_FAIL; REQUEST_VERIFY(request); /* * Async (in the same thread, tho) signal to be done. */ if (action == FR_SIGNAL_CANCEL) goto done; /* * We ignore all other actions. */ if (action != FR_SIGNAL_RUN) return; switch (request->request_state) { case REQUEST_INIT: if (RDEBUG_ENABLED) proto_ldap_packet_debug(request, request->packet, true); log_request_proto_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); request->server_cs = request->listener->server_cs; request->component = "ldap"; switch (request->packet->code) { case LDAP_SYNC_CODE_PRESENT: verb = "recv"; state = "Present"; break; case LDAP_SYNC_CODE_ADD: verb = "recv"; state = "Add"; break; case LDAP_SYNC_CODE_MODIFY: verb = "recv"; state = "Modify"; break; case LDAP_SYNC_CODE_DELETE: verb = "recv"; state = "Delete"; break; case LDAP_SYNC_CODE_COOKIE_LOAD: verb = "load"; state = "Cookie"; break; case LDAP_SYNC_CODE_COOKIE_STORE: verb = "store"; state = "Cookie"; break; default: rad_assert(0); return; } unlang = cf_section_find(request->server_cs, verb, state); if (!unlang) unlang = cf_section_find(request->server_cs, "recv", "*"); if (!unlang) { RDEBUG2("Ignoring %s operation. Add \"%s %s {}\" to virtual-server \"%s\"" " to handle", fr_int2str(ldap_sync_code_table, request->packet->code, "<INVALID>"), verb, state, cf_section_name2(request->server_cs)); rcode = RLM_MODULE_NOOP; goto done; } RDEBUG("Running '%s %s' from file %s", cf_section_name1(unlang), cf_section_name2(unlang), cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) goto done; if (rcode == RLM_MODULE_YIELD) return; /* FALL-THROUGH */ default: done: switch (rcode) { case RLM_MODULE_UPDATED: case RLM_MODULE_OK: { } default: break; } rad_assert(request->log.unlang_indent == 0); //request_delete(request); break; } }
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action) { VALUE_PAIR *vp; rlm_rcode_t rcode; CONF_SECTION *unlang; REQUEST_VERIFY(request); /* * Pass this through asynchronously to the module which * is waiting for something to happen. */ if (action != FR_IO_ACTION_RUN) { unlang_signal(request, (fr_state_signal_t) action); return FR_IO_DONE; } switch (request->request_state) { case REQUEST_INIT: RDEBUG("Received %s ID %i", fr_dict_enum_alias_by_value(attr_packet_type, fr_box_uint32(request->reply->code)), request->packet->id); log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); request->component = "radius"; unlang = cf_section_find(request->server_cs, "recv", NULL); if (!unlang) { REDEBUG("Failed to find 'recv' section"); return FR_IO_FAIL; } RDEBUG("Running 'recv' from file %s", cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_continue(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { /* * The module has a number of OK return codes. */ case RLM_MODULE_OK: case RLM_MODULE_UPDATED: switch (request->packet->code) { case FR_CODE_ACCOUNTING_REQUEST: request->reply->code = FR_CODE_ACCOUNTING_RESPONSE; break; case FR_CODE_COA_REQUEST: request->reply->code = FR_CODE_COA_ACK; break; case FR_CODE_DISCONNECT_REQUEST: request->reply->code = FR_CODE_DISCONNECT_ACK; break; default: request->reply->code = 0; break; } break; case RLM_MODULE_HANDLED: break; /* * The module failed, or said the request is * invalid, therefore we stop here. */ case RLM_MODULE_NOOP: case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: request->reply->code = 0; break; } /* * Allow for over-ride of reply code. */ vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY); if (vp) request->reply->code = vp->vp_uint32; if (request->reply->code == FR_CODE_DO_NOT_RESPOND) { RWARN("Ignoring 'do_not_respond' as it does not apply to detail files"); } unlang = cf_section_find(request->server_cs, "send", "ok"); if (!unlang) goto send_reply; RDEBUG("Running 'send %s { ... }' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_continue(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: case RLM_MODULE_HANDLED: /* reply is already set */ break; default: request->reply->code = 0; break; } send_reply: /* * Failed, but we still reply with a magic code, * so that the reader can retransmit. */ if (!request->reply->code) { REDEBUG("Failed ID %i", request->reply->id); } else { RDEBUG("Sent %s ID %i", fr_dict_enum_alias_by_value(attr_packet_type, fr_box_uint32(request->reply->code)), request->reply->id); } log_request_proto_pair_list(L_DBG_LVL_1, request, request->reply->vps, ""); break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, UNUSED fr_io_action_t action) { rlm_rcode_t rcode; CONF_SECTION *unlang; fr_dict_enum_t const *dv; fr_dict_attr_t const *da = NULL; VALUE_PAIR *vp; REQUEST_VERIFY(request); rad_assert(request->packet->code > 0); rad_assert(request->packet->code <= FR_DHCP_INFORM); switch (request->request_state) { case REQUEST_INIT: RDEBUG("Received %s ID %08x", dhcp_message_types[request->packet->code], request->packet->id); log_request_proto_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); request->component = "dhcpv4"; dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->packet->code)); if (!dv) { REDEBUG("Failed to find value for &request:DHCP-Message-Type"); return FR_IO_FAIL; } unlang = cf_section_find(request->server_cs, "recv", dv->alias); if (!unlang) { RWDEBUG("Failed to find 'recv %s' section", dv->alias); request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND; goto send_reply; } RDEBUG("Running 'recv %s' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_continue(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); /* * Allow the admin to explicitly set the reply * type. */ vp = fr_pair_find_by_da(request->reply->vps, attr_message_type, TAG_ANY); if (vp) { request->reply->code = vp->vp_uint8; } else switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = reply_ok[request->packet->code]; break; default: case RLM_MODULE_REJECT: case RLM_MODULE_FAIL: request->reply->code = reply_fail[request->packet->code]; break; case RLM_MODULE_HANDLED: if (!request->reply->code) request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND; break; } /* * DHCP-Release / Decline doesn't send a reply, and doesn't run "send DHCP-Do-Not-Respond" */ if (!request->reply->code) { return FR_IO_DONE; } /* * Offer and ACK MUST have YIADDR. */ if ((request->reply->code == FR_DHCP_OFFER) || (request->reply->code == FR_DHCP_ACK)) { vp = fr_pair_find_by_da(request->reply->vps, attr_yiaddr, TAG_ANY); if (!vp) { REDEBUG("%s packet does not have YIADDR. The client will not receive an IP address.", dhcp_message_types[request->reply->code]); } } dv = fr_dict_enum_by_value(da, fr_box_uint8(request->reply->code)); unlang = NULL; if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias); if (!unlang) goto send_reply; rerun_nak: RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_continue(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: case RLM_MODULE_HANDLED: /* reply is already set */ break; default: /* * If we over-ride an ACK with a NAK, run * the NAK section. */ if (request->reply->code != FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND) { dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->reply->code)); RWDEBUG("Failed running 'send %s', trying 'send Do-Not-Respond'", dv->alias); request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND; dv = fr_dict_enum_by_value(da, fr_box_uint8(request->reply->code)); unlang = NULL; if (!dv) goto send_reply; unlang = cf_section_find(request->server_cs, "send", dv->alias); if (unlang) goto rerun_nak; RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias); } break; } send_reply: /* * Check for "do not respond". */ if (request->reply->code == FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND) { RDEBUG("Not sending reply to client"); return FR_IO_DONE; } if (RDEBUG_ENABLED) common_packet_debug(request, request->reply, false); break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }