1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krad/client.c - Client request code for libkrad */
4 * Copyright 2013 Red Hat, Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 K5_LIST_HEAD(server_head, server_st);
41 typedef struct remote_state_st remote_state;
42 typedef struct request_st request;
43 typedef struct server_st server;
45 struct remote_state_st {
46 const krad_packet *packet;
60 remote_state *remotes;
68 K5_LIST_ENTRY(server_st) list;
71 struct krad_client_st {
74 struct server_head servers;
77 /* Return either a pre-existing server that matches the address info and the
78 * secret, or create a new one. */
79 static krb5_error_code
80 get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
83 krb5_error_code retval;
87 if (time(&currtime) == (time_t)-1)
90 K5_LIST_FOREACH(srv, &rc->servers, list) {
91 if (kr_remote_equals(srv->serv, ai, secret)) {
98 srv = calloc(1, sizeof(server));
101 srv->last = currtime;
103 retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
109 K5_LIST_INSERT_HEAD(&rc->servers, srv, list);
114 /* Free a request. */
116 request_free(request *req)
118 krad_attrset_free(req->attrs);
123 /* Create a request. */
124 static krb5_error_code
125 request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
126 const struct addrinfo *ai, const char *secret, int timeout,
127 size_t retries, krad_cb cb, void *data, request **req)
129 const struct addrinfo *tmp;
130 krb5_error_code retval;
137 rqst = calloc(1, sizeof(request));
141 for (tmp = ai; tmp != NULL; tmp = tmp->ai_next)
148 rqst->timeout = timeout / rqst->count;
149 rqst->retries = retries;
151 retval = krad_attrset_copy(attrs, &rqst->attrs);
157 rqst->remotes = calloc(rqst->count + 1, sizeof(remote_state));
158 if (rqst->remotes == NULL) {
164 for (tmp = ai; tmp != NULL; tmp = tmp->ai_next) {
165 retval = get_server(rc, tmp, secret, &rqst->remotes[i++].remote);
176 /* Close remotes that haven't been used in a while. */
178 age(struct server_head *head, time_t currtime)
182 K5_LIST_FOREACH_SAFE(srv, head, list, tmp) {
183 if (currtime == (time_t)-1 || currtime - srv->last > 60 * 60) {
184 K5_LIST_REMOVE(srv, list);
185 kr_remote_free(srv->serv);
191 /* Handle a response from a server (or related errors). */
193 on_response(krb5_error_code retval, const krad_packet *reqp,
194 const krad_packet *rspp, void *data)
200 /* Do nothing if we are already completed. */
204 /* If we have timed out and have more remotes to try, do so. */
205 if (retval == ETIMEDOUT && req->remotes[++req->current].remote != NULL) {
206 retval = kr_remote_send(req->remotes[req->current].remote, req->code,
207 req->attrs, on_response, req, req->timeout,
209 &req->remotes[req->current].packet);
214 /* Mark the request as complete. */
217 /* Inform the callback. */
218 req->cb(retval, reqp, rspp, req->data);
220 /* Cancel the outstanding packets. */
221 for (i = 0; req->remotes[i].remote != NULL; i++)
222 kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);
224 /* Age out servers that haven't been used in a while. */
225 if (time(&currtime) != (time_t)-1)
226 age(&req->rc->servers, currtime);
232 krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)
236 tmp = calloc(1, sizeof(krad_client));
248 krad_client_free(krad_client *rc)
253 age(&rc->servers, -1);
257 static krb5_error_code
258 resolve_remote(const char *remote, struct addrinfo **ai)
260 const char *svc = "radius";
261 krb5_error_code retval;
262 struct addrinfo hints;
265 /* Isolate the port number if it exists. */
266 srv = strdup(remote);
272 sep = strrchr(srv, ']');
273 if (sep != NULL && sep[1] == ':') {
279 sep = strrchr(srv, ':');
280 if (sep != NULL && sep[1] != '\0') {
286 /* Perform the lookup. */
287 memset(&hints, 0, sizeof(hints));
288 hints.ai_socktype = SOCK_DGRAM;
289 retval = gai_error_code(getaddrinfo(srv, svc, &hints, ai));
295 krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
296 const char *remote, const char *secret, int timeout,
297 size_t retries, krad_cb cb, void *data)
299 struct addrinfo usock, *ai = NULL;
300 krb5_error_code retval;
301 struct sockaddr_un ua;
304 if (remote[0] == '/') {
305 ua.sun_family = AF_UNIX;
306 snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);
307 memset(&usock, 0, sizeof(usock));
308 usock.ai_family = AF_UNIX;
309 usock.ai_socktype = SOCK_STREAM;
310 usock.ai_addr = (struct sockaddr *)&ua;
311 usock.ai_addrlen = sizeof(ua);
313 retval = request_new(rc, code, attrs, &usock, secret, timeout, retries,
316 retval = resolve_remote(remote, &ai);
318 retval = request_new(rc, code, attrs, ai, secret, timeout, retries,
326 retval = kr_remote_send(req->remotes[req->current].remote, req->code,
327 req->attrs, on_response, req, req->timeout,
328 req->retries, &req->remotes[req->current].packet);