Radcli library 2.0.0
A simple radius library -- new API reference
Loading...
Searching...
No Matches
radexample-async-dae.c
1/*
2 * Copyright (C) 2015,2026 Nikos Mavrogiannopoulos
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
31
32#include <config.h>
33#include <stdio.h>
34#include <syslog.h>
35#include <poll.h>
36#include <errno.h>
37#include <arpa/inet.h>
38#include <radcli/radcli2.h>
39
40static void
41print_reply(radcli_ctx *ctx, const radcli_avp_list *recvd)
42{
43 uint32_t timeout, service_type;
44 struct in6_addr framed_ipv6;
45 char buf[INET6_ADDRSTRLEN];
46
47 if (radcli_avp_get_uint32_by_num(recvd, ctx, PW_SESSION_TIMEOUT, 0, &timeout) == 0)
48 fprintf(stderr, "Session-Timeout: %u seconds\n", (unsigned)timeout);
49
50 if (radcli_avp_get_ip6_by_num(recvd, ctx, PW_FRAMED_IPV6_ADDRESS, 0, &framed_ipv6, NULL) == 0 &&
51 inet_ntop(AF_INET6, &framed_ipv6, buf, sizeof(buf)) != NULL)
52 fprintf(stderr, "Framed-IPv6-Address: %s\n", buf);
53
54 if (radcli_avp_get_uint32_by_num(recvd, ctx, PW_SERVICE_TYPE, 0, &service_type) == 0)
55 fprintf(stderr, "Service-Type: %u\n", (unsigned)service_type);
56}
57
58static void
59handle_dae_request(radcli_dae_request *req, void *user)
60{
61 const char *session_id = radcli_dae_req_session_id(req);
62 const char *user_name = radcli_dae_req_user_name(req);
63
64 (void)user;
65
66 /* This example only implements Disconnect-Request; CoA-Request
67 * (session attribute changes) is not applied, so it must be NAKed
68 * rather than ACKed -- a CoA-ACK asserts the change was applied. */
69 if (radcli_dae_req_code(req) != RADCLI_DISCONNECT_REQUEST) {
70 radcli_dae_reply_error(req, RADCLI_ERROR_UNSUPPORTED_SERVICE);
72 return;
73 }
74
75 printf("Disconnect-Request for user '%s', session '%s'\n",
76 user_name ? user_name : "?", session_id ? session_id : "?");
77
78 radcli_dae_reply(req, 1);
80}
81
82int
83main (int argc, char **argv)
84{
85 radcli_ctx *ctx;
86 radcli_dae *dae = NULL;
87 radcli_avp_list *send = NULL;
88 radcli_request *r;
89 int result = 1;
90
91 openlog("my-prog-name", LOG_PID, LOG_DAEMON);
92
93 ctx = radcli_ctx_read_config(RC_CONFIG_FILE, 0);
94 if (ctx == NULL)
95 return 1;
96
97 dae = radcli_dae_new(ctx, 0);
98 if (dae == NULL) {
99 fprintf(stderr, "dynamic authorization is not enabled or is "
100 "misconfigured (check dae-accept/dae-server/dae-secret)\n");
101 radcli_ctx_free(ctx);
102 return 1;
103 }
104 radcli_dae_set_handler(dae, handle_dae_request, NULL);
105 if (radcli_dae_start(dae) != 0) {
106 fprintf(stderr, "cannot start the DAE listener\n");
107 radcli_dae_free(dae);
108 radcli_ctx_free(ctx);
109 return 1;
110 }
111
112 send = radcli_avp_list_new();
113 radcli_avp_add_str_by_num(send, ctx, PW_USER_NAME, 0, "my-username");
114 radcli_avp_add_str_by_num(send, ctx, PW_USER_PASSWORD, 0, "my-password");
115 radcli_avp_add_uint32_by_num(send, ctx, PW_SERVICE_TYPE, 0, PW_AUTHENTICATE_ONLY);
116
117 if (radcli_avp_list_error(send)) {
118 fprintf(stderr, "error constructing the Access-Request\n");
120 radcli_ctx_free(ctx);
121 return 1;
122 }
123
124 r = radcli_request_new(ctx, RADCLI_CODE_ACCESS_REQUEST, send);
126 if (r == NULL) {
127 radcli_ctx_free(ctx);
128 return 1;
129 }
130
132 fprintf(stderr, "cannot send the Access-Request\n");
134 radcli_ctx_free(ctx);
135 return 1;
136 }
137
138 /* Single main loop, from the very first iteration: services the
139 * pending Access-Request (r) alongside the already-running watchdog
140 * and DAE listener (dae) -- all through one ctx-level descriptor set.
141 * radcli_ctx_get_poll() already folds r's own retransmit/timeout
142 * deadline into timeout_ms (it shares ctx's own request-registry
143 * socket/session, REQ-NET2-SEND-016), and radcli_ctx_dispatch()
144 * already drains and resolves r's reply along with DAE traffic and
145 * the watchdog -- so radcli_request_done(r) below is a pure,
146 * no-I/O status check, not a second read. */
147 for (;;) {
148 struct pollfd pfds[RADCLI_CTX_MAX_POLLFDS];
149 size_t nfds;
150 int timeout_ms;
151 int ret;
152
153 if (radcli_ctx_get_poll(ctx, pfds, RADCLI_CTX_MAX_POLLFDS, &nfds, &timeout_ms) != 0)
154 break;
155 if (nfds == 0)
156 break; /* nothing left to wait on */
157
158 ret = poll(pfds, (nfds_t)nfds, timeout_ms);
159 if (ret < 0) {
160 /* EINTR (e.g. a signal the embedding application handles)
161 * is not a timeout: retry rather than misfiring the
162 * watchdog or the request's retransmit. Any other errno
163 * is a genuine poll() failure. */
164 if (errno == EINTR)
165 continue;
166 break;
167 }
168
169 /* Unconditional and non-blocking, like every other radcli
170 * dispatch call: services whichever of pfds actually turned
171 * out to be ready without the caller needing to check revents
172 * itself, and equally correct on the ret == 0 (timeout) case,
173 * where it is the watchdog and/or r's retransmit that are due. */
175
176 if (r != NULL) {
177 int rc = radcli_request_done(r);
178
179 if (rc != RADCLI_AGAIN) {
180 if (rc == RADCLI_OK && radcli_request_code(r) == RADCLI_CODE_ACCESS_ACCEPT) {
181 fprintf(stderr, "\"my-username\" RADIUS Authentication OK\n");
182 print_reply(ctx, radcli_request_attrs(r));
183 result = 0;
184 } else {
185 fprintf(stderr, "\"my-username\" RADIUS Authentication failure\n");
186 }
188 r = NULL;
189 }
190 }
191 }
192
193 if (dae != NULL)
194 radcli_dae_free(dae);
195 if (r != NULL)
197 radcli_ctx_free(ctx);
198
199 return result;
200}
void radcli_dae_free(radcli_dae *dae)
Release a listener, closing its socket if radcli_dae_start() opened one.
Definition dae.c:978
const char * radcli_dae_req_session_id(const radcli_dae_request *req)
Return the request's Acct-Session-Id, if it carried one.
Definition dae.c:1621
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()/...
Definition dae.c:1091
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 ...
Definition dae.c:852
radcli_code radcli_dae_req_code(const radcli_dae_request *req)
Return the received packet's RADIUS code.
Definition dae.c:1593
int radcli_ctx_dispatch(radcli_ctx *ctx)
Read what is ready on ctx's descriptor(s), validate it, and invoke the registered handler for anythin...
Definition dae.c:2324
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,...
Definition dae.c:1806
struct radcli_dae_st radcli_dae
Definition radcli2.h:678
const char * radcli_dae_req_user_name(const radcli_dae_request *req)
Return the request's User-Name, if it carried one.
Definition dae.c:1634
int radcli_dae_start(radcli_dae *dae)
Start receiving: binds the socket described by dae-listen.
Definition dae.c:896
#define RADCLI_CTX_MAX_POLLFDS
Definition radcli2.h:764
int radcli_dae_reply_error(radcli_dae_request *req, uint32_t error_cause)
Answer a request with a NAK carrying the given Error-Cause.
Definition dae.c:1821
radcli_dae * radcli_dae_new(radcli_ctx *ctx, unsigned flags)
Validate dae-* configuration and build a dynamic-authorization listener. Opens no socket – see radcli...
Definition dae.c:638
struct radcli_dae_request_st radcli_dae_request
Definition radcli2.h:688
void radcli_dae_request_free(radcli_dae_request *req)
Release a request.
Definition dae.c:1890
int radcli_avp_list_error(const radcli_avp_list *list)
Check whether any radcli_avp_add_*()/_by_num() call on list has ever failed.
Definition avp.c:1230
void radcli_avp_list_free(radcli_avp_list *list)
Free a list and every attribute it holds.
Definition avp.c:159
int radcli_avp_add_uint32_by_num(radcli_avp_list *list, const radcli_ctx *ctx, uint32_t attrid, uint32_t vendor, uint32_t value)
Look up an integer/IPv4-address/date-typed attribute by legacy numeric ID and append it.
Definition avp.c:503
int radcli_avp_add_str_by_num(radcli_avp_list *list, const radcli_ctx *ctx, uint32_t attrid, uint32_t vendor, const char *value)
Look up a string-typed attribute by legacy numeric ID and append it.
Definition avp.c:485
int radcli_avp_get_uint32_by_num(const radcli_avp_list *list, const radcli_ctx *ctx, uint32_t attrid, uint32_t vendor, uint32_t *out)
Look up an integer/IPv4-address/date-typed attribute by legacy numeric ID and read its first occurren...
Definition avp.c:970
radcli_avp_list * radcli_avp_list_new(void)
Create an empty attribute-value pair list.
Definition avp.c:145
int radcli_avp_get_ip6_by_num(const radcli_avp_list *list, const radcli_ctx *ctx, uint32_t attrid, uint32_t vendor, struct in6_addr *out, unsigned *prefix)
Look up an IPv6-address or IPv6-prefix-typed attribute by legacy numeric ID and read its first occurr...
Definition avp.c:1007
radcli_ctx * radcli_ctx_read_config(const char *filename, unsigned flags)
Create a context by parsing a config file – the main, recommended way to configure radcli.
Definition config2.c:108
void radcli_ctx_free(radcli_ctx *ctx)
Release a context.
Definition config2.c:173
int radcli_request_perform(radcli_request *r, unsigned flags)
Send a request, optionally waiting for the reply.
Definition request.c:364
const radcli_avp_list * radcli_request_attrs(const radcli_request *r)
Return the reply's decoded attributes.
Definition request.c:486
int radcli_request_done(radcli_request *r)
Report r's outcome once radcli_ctx_dispatch() has resolved it – a pure state query,...
Definition request.c:444
radcli_request * radcli_request_new(radcli_ctx *ctx, radcli_code code, const radcli_avp_list *send)
Create a request to send.
Definition request.c:97
void radcli_request_free(radcli_request *r)
Release a request.
Definition request.c:517
radcli_code radcli_request_code(const radcli_request *r)
Return the reply's RADIUS code.
Definition request.c:473
@ RADCLI_AGAIN
Definition radcli2.h:570
@ RADCLI_OK
A validated reply was received; see radcli_request_code() for which one.
Definition radcli2.h:563
@ RADCLI_REQUEST_SENDONLY
Definition radcli2.h:603