|
| radcli_code | radcli_dae_req_code (const radcli_dae_request *req) |
| | Return the received packet's RADIUS code.
|
| const radcli_avp_list * | radcli_dae_req_attrs (const radcli_dae_request *req) |
| | Return the request's decoded attributes.
|
| const char * | radcli_dae_req_session_id (const radcli_dae_request *req) |
| | Return the request's Acct-Session-Id, if it carried one.
|
| const char * | radcli_dae_req_user_name (const radcli_dae_request *req) |
| | Return the request's User-Name, if it carried one.
|
| int | radcli_dae_req_framed_ip (const radcli_dae_request *req, struct sockaddr_storage *out) |
| | Return the request's Framed-IP-Address or Framed-IPv6-Address.
|
| int | radcli_dae_req_nas_port (const radcli_dae_request *req, uint32_t *out) |
| | Return the request's NAS-Port.
|
| int | radcli_dae_reply (radcli_dae_request *req, int ack) |
| | Answer a request with an ACK or NAK, selecting 41/42 or 44/45 from the request's own code, mirroring its Proxy-State attributes, and computing the Response Authenticator over the request's Authenticator.
|
| int | radcli_dae_reply_error (radcli_dae_request *req, uint32_t error_cause) |
| | Answer a request with a NAK carrying the given Error-Cause.
|
| int | radcli_dae_reply_to_buffer (radcli_dae_request *req, int ack, uint32_t error_cause, void *buf, size_t *len) |
| | Produce a reply as bytes instead of sending it – the L0 counterpart of radcli_dae_reply()/radcli_dae_reply_error(), for a request that came from radcli_dae_process().
|
| void | radcli_dae_request_free (radcli_dae_request *req) |
| | Release a request.
|
| int | radcli_dae_process (radcli_dae *dae, const void *buf, size_t len, const struct sockaddr *from, socklen_t fromlen, radcli_dae_request **req) |
| | Validate a caller-supplied packet, without a radcli-owned socket – the L0 counterpart of radcli_ctx_dispatch(), running the identical validation pipeline (REQ-DAE-NET-003) on a buffer and source address the caller supplies instead of reading them from radcli_dae_start()'s socket. A request this produces is otherwise indistinguishable from one radcli_ctx_dispatch() would have delivered to a handler.
|
| radcli_dae * | radcli_dae_new (radcli_ctx *ctx, unsigned flags) |
| | Validate dae-* configuration and build a dynamic-authorization listener. Opens no socket – see radcli_dae_start().
|
| void | radcli_dae_set_handler (radcli_dae *dae, radcli_dae_handler cb, void *user) |
| | Register the callback radcli_ctx_dispatch() invokes for each validated request. May be called before or after radcli_dae_start().
|
| int | radcli_dae_start (radcli_dae *dae) |
| | Start receiving: binds the socket described by dae-listen.
|
| void | radcli_dae_free (radcli_dae *dae) |
| | Release a listener, closing its socket if radcli_dae_start() opened one.
|
| int | radcli_ctx_get_poll (radcli_ctx *ctx, struct pollfd *pfds, size_t max_pfds, size_t *nfds, int *timeout_ms) |
| | Report what to wait for on ctx's behalf, for the caller's own event loop – radcli never calls poll()/select()/epoll_wait() itself.
|
| int | radcli_ctx_dispatch (radcli_ctx *ctx) |
| | Read what is ready on ctx's descriptor(s), validate it, and invoke the registered handler for anything that passes – see radcli_dae_set_handler(). Also: drains any in-flight RADCLI_REQUEST_SENDONLY exchange's reply and services its retransmit/ timeout deadline (net2.md's REQ-NET2-SEND-013/016), and, once due, sends the RFC 5997 watchdog on an established RadSec session (watchdog.md's REQ-WATCHDOG-NET-001) – all unconditionally, every call, regardless of whether an active radcli_dae exists at all: this is now the single entry point radcli_ctx_get_poll() drives, for everything ctx owns, not a DAE-only call.
|
Receive-only support for RFC 5176: radcli answers CoA-Request and Disconnect-Request packets from a Dynamic Authorization Client (DAC); there is no exported function to send them (radcli implements the server/receiving role only – see doc/requirements/dae.md).
radcli never exposes the listener's own descriptor and never offers a blocking read call: instead, the application registers a handler with radcli_dae_set_handler() and polls radcli_ctx_get_poll()/dispatches with radcli_ctx_dispatch(), both at the radcli_ctx level rather than the radcli_dae level. That is deliberate, not incidental: a future dynamic authorization transport carried over the same session as ordinary requests (rather than a separate UDP/3799 listener) shares one descriptor between the two, and an accessor on radcli_dae alone would let an application watch a descriptor that silently stops meaning what it thinks – see radcli_ctx_dispatch()'s doc comment.
radcli_dae_process()/radcli_dae_reply_to_buffer() are the L0 entry point: the same validation and reply machinery, without a radcli-owned socket, for an application that owns its own transport – e.g. a privileged listener process handing validated requests to workers over IPC.
radcli itself never forks and never installs an atfork handler, so it cannot protect an application that binds the listener and then forks (e.g. a worker/main/privilege-separated process shape): every child inherits a working copy of the same UDP socket, races the others for datagrams, and – since the duplicate-suppression table is per-process – can end up acting on the same Disconnect-Request more than once. A process that forks after radcli_dae_start() must call radcli_dae_free() in every child that does not itself own the listener before running its own event loop.
When the nas-identifier config option is set, every request's own NAS-Identifier (if it carries one) is checked against it automatically, before radcli_dae_handler ever sees the request: a mismatch is NAKed with RADCLI_ERROR_NAS_IDENTIFICATION_MISMATCH and the handler is not invoked. Pass RADCLI_DAE_NO_NAS_CHECK to radcli_dae_new() to disable this check. NAS-IP-Address/NAS-IPv6-Address are never compared: a value a DAC observed for a NAS routinely differs from what the NAS itself is configured with (NAT, containers, a proxy/load balancer in front of the NAS), unlike NAS-Identifier, which both sides are explicitly, statically configured with.
| int radcli_ctx_dispatch |
( |
radcli_ctx * | ctx | ) |
|
Read what is ready on ctx's descriptor(s), validate it, and invoke the registered handler for anything that passes – see radcli_dae_set_handler(). Also: drains any in-flight RADCLI_REQUEST_SENDONLY exchange's reply and services its retransmit/ timeout deadline (net2.md's REQ-NET2-SEND-013/016), and, once due, sends the RFC 5997 watchdog on an established RadSec session (watchdog.md's REQ-WATCHDOG-NET-001) – all unconditionally, every call, regardless of whether an active radcli_dae exists at all: this is now the single entry point radcli_ctx_get_poll() drives, for everything ctx owns, not a DAE-only call.
On the DAE side specifically, reads exactly one datagram per call (non-blocking), so a burst of requests re-arms the caller's loop rather than starving it, and runs it through process_packet()'s validation pipeline before ever invoking the registered handler. Every rejection short of "authorized sender" is silent: no reply, no Error-Cause, no log of the secret or either authenticator (REQ-DAE-SEC-009).
Not reentrant: calling this, radcli_dae_start(), or radcli_dae_free() from within a handler radcli_ctx_dispatch() itself invoked is undefined.
- Parameters
-
- Returns
- 0 on success (including "nothing was ready"), -1 on failure (e.g. ctx is NULL, or called reentrantly).
Definition at line 2324 of file dae.c.
| int radcli_ctx_get_poll |
( |
radcli_ctx * | ctx, |
|
|
struct pollfd * | pfds, |
|
|
size_t | max_pfds, |
|
|
size_t * | nfds, |
|
|
int * | timeout_ms ) |
Report what to wait for on ctx's behalf, for the caller's own event loop – radcli never calls poll()/select()/epoll_wait() itself.
There is no per-object descriptor accessor (e.g. no radcli_dae_fd(), no per-radcli_request one either – net2.md's REQ-NET2-SEND-013): every descriptor this reports belongs to ctx, not to any one radcli_dae or radcli_request, so that a transport sharing one descriptor between dynamic authorization and ordinary requests (already true for TLS/DTLS) never leaves an application holding a watcher on a descriptor that has quietly started meaning something else.
For TLS/DTLS, or for a UDP ctx with no active radcli_dae, this is always exactly one descriptor: the session fd (TLS/DTLS, also carrying any in-flight RADCLI_REQUEST_SENDONLY traffic) or the request-registry socket (UDP, REQ-NET2-SEND-016). A UDP ctx with an active radcli_dae reports a second, independent descriptor for the DAE listener alongside it – the two are genuinely different local sockets/ports and cannot be merged into one without changing the wire protocol; RADCLI_CTX_MAX_POLLFDS (2) is the maximum this API ever needs. *timeout_ms folds together every deadline source that applies (DAE queued-work, RadSec watchdog, and any in-flight RADCLI_REQUEST_SENDONLY exchange's own retransmit/timeout) into one caller-facing value, so the caller never computes a min() itself.
A descriptor is closed or replaced only during a call the application itself makes (radcli_ctx_dispatch(), radcli_dae_free(), radcli_request_perform(), rc_destroy()), never asynchronously – but re-query after any of those regardless, since one may replace it.
- Parameters
-
| ctx | a context, with or without an active radcli_dae. |
| [out] | pfds | filled with up to RADCLI_CTX_MAX_POLLFDS entries (fd/events; revents is left for the caller's poll() to fill in). |
| max_pfds | pfds's capacity; MUST be at least RADCLI_CTX_MAX_POLLFDS. |
| [out] | nfds | set to how many of pfds were filled in (0 if there is nothing to watch yet). |
| [out] | timeout_ms | milliseconds after which to call radcli_ctx_dispatch() even without I/O readiness; -1 for "no timeout
needed". |
- Returns
- 0 on success, -1 if ctx or an out-parameter is NULL, or max_pfds is too small.
Definition at line 1091 of file dae.c.