2 * client.c Read clients into memory.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2000 Alan DeKok <aland@ox.org>
25 static const char rcsid[] = "$Id$";
31 #ifdef HAVE_NETINET_IN_H
32 # include <netinet/in.h>
43 #include "rad_assert.h"
45 struct radclient_list {
47 * FIXME: One set of trees for IPv4, and another for IPv6?
49 rbtree_t *trees[129]; /* for 0..128, inclusive. */
52 rbtree_t *num; /* client numbers 0..N */
58 * Callback for freeing a client.
60 void client_free(RADCLIENT *client)
62 free(client->longname);
64 free(client->shortname);
65 free(client->nastype);
67 free(client->password);
74 * Callback for comparing two clients.
76 static int client_ipaddr_cmp(const void *one, const void *two)
78 const RADCLIENT *a = one;
79 const RADCLIENT *b = two;
81 if (a->ipaddr.af < b->ipaddr.af) return -1;
82 if (a->ipaddr.af > b->ipaddr.af) return +1;
84 rad_assert(a->prefix == b->prefix);
86 switch (a->ipaddr.af) {
88 return memcmp(&a->ipaddr.ipaddr.ip4addr,
89 &b->ipaddr.ipaddr.ip4addr,
90 sizeof(a->ipaddr.ipaddr.ip4addr));
94 return memcmp(&a->ipaddr.ipaddr.ip6addr,
95 &b->ipaddr.ipaddr.ip6addr,
96 sizeof(a->ipaddr.ipaddr.ip6addr));
104 * Something bad happened...
106 rad_assert("Internal sanity check failed");
111 static int client_num_cmp(const void *one, const void *two)
113 const RADCLIENT *a = one;
114 const RADCLIENT *b = two;
116 return (a->number - b->number);
121 * Free a RADCLIENT list.
123 void clients_free(RADCLIENT_LIST *clients)
127 if (!clients) return;
129 for (i = 0; i <= 128; i++) {
130 if (clients->trees[i]) rbtree_free(clients->trees[i]);
131 clients->trees[i] = NULL;
134 if (clients->num) rbtree_free(clients->num);
139 * Return a new, initialized, set of clients.
141 RADCLIENT_LIST *clients_init(void)
143 RADCLIENT_LIST *clients = calloc(1, sizeof(RADCLIENT_LIST));
145 if (!clients) return NULL;
147 clients->min_prefix = 128;
154 * Sanity check a client.
156 static int client_sane(RADCLIENT *client)
158 switch (client->ipaddr.af) {
160 if (client->prefix > 32) {
165 * Zero out the subnet bits.
167 if (client->prefix < 32) {
170 mask <<= (32 - client->prefix);
171 client->ipaddr.ipaddr.ip4addr.s_addr &= mask;
176 if (client->prefix > 128) return 0;
178 if (client->prefix < 128) {
180 uint32_t mask, *addr;
182 addr = (uint32_t *) &client->ipaddr.ipaddr.ip6addr;
184 for (i = client->prefix; i < 128; i += 32) {
186 mask <<= ((128 - i) & 0x1f);
187 addr[i / 32] &= mask;
201 * Add a client to the tree.
203 int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
205 if (!clients || !client) {
209 if (client->prefix < 0) {
213 if (!client_sane(client)) return 0;
216 * Create a tree for it.
218 if (!clients->trees[client->prefix]) {
219 clients->trees[client->prefix] = rbtree_create(client_ipaddr_cmp,
221 if (!clients->trees[client->prefix]) {
229 if (!rbtree_insert(clients->trees[client->prefix], client)) {
234 if (!clients->num) rbtree_create(client_num_cmp, NULL, 0);
236 client->number = clients->max;
238 if (clients->num) rbtree_insert(clients->num, client);
241 if (client->prefix < clients->min_prefix) {
242 clients->min_prefix = client->prefix;
250 * Find a client in the RADCLIENTS list by number.
251 * This is a support function for the SNMP code.
253 RADCLIENT *client_findbynumber(const RADCLIENT_LIST *clients,
257 if (!clients) return NULL;
262 myclient.number = number;
264 return rbtree_finddata(clients->num, &myclient);
272 * Find a client in the RADCLIENTS list.
274 RADCLIENT *client_find(const RADCLIENT_LIST *clients,
275 const lrad_ipaddr_t *ipaddr)
280 if (!clients || !ipaddr) return NULL;
282 switch (ipaddr->af) {
295 for (i = max_prefix; i >= clients->min_prefix; i--) {
299 myclient.ipaddr = *ipaddr;
300 client_sane(&myclient); /* clean up the ipaddress */
302 if (!clients->trees[i]) continue;
304 data = rbtree_finddata(clients->trees[i], &myclient);
315 * Old wrapper for client_find
317 RADCLIENT *client_find_old(const lrad_ipaddr_t *ipaddr)
319 return client_find(mainconfig.clients, ipaddr);
324 * Read the clients file.
326 int read_clients_file(RADCLIENT_LIST *clients, const char *file)
337 int got_clients = FALSE;
339 if ((fp = fopen(file, "r")) == NULL) {
340 /* The clients file is no longer required. All configuration
341 information is read from radiusd.conf and friends. If
342 clients exists it will be used, but if it doesn't no harm
347 while(fgets(buffer, 256, fp) != NULL) {
349 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
350 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
359 ((*p == ' ') || (*p == '\t')))
363 * Skip comments and blank lines.
365 if ((*p == '#') || (*p == '\n') || (*p == '\r'))
368 if (!getword(&p, hostnm, sizeof(hostnm)) ||
369 !getword(&p, secret, sizeof(secret))) {
370 radlog(L_ERR, "%s[%d]: unexpected end of line",
375 (void)getword(&p, shortnm, sizeof(shortnm));
378 * Look for a mask in the hostname
380 p = strchr(hostnm, '/');
387 if ((prefix < 0) || (prefix > 128)) {
388 radlog(L_ERR, "%s[%d]: Invalid value '%s' for IP network mask.",
395 * It should be OK now, let's create the buffer.
398 c = rad_malloc(sizeof(RADCLIENT));
399 memset(c, 0, sizeof(*c));
401 if (ip_hton(hostnm, AF_UNSPEC, &c->ipaddr) < 0) {
402 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
403 file, lineno, hostnm);
407 c->secret = strdup(secret);
408 c->shortname = strdup(shortnm);
410 switch (c->ipaddr.af) {
413 if ((prefix < 0) || (prefix > 32)) {
414 radlog(L_ERR, "%s[%d]: Invalid value '%s' for IP network mask.",
420 hostnm[strlen(hostnm)] = '/';
421 /* Long Name includes prefix too */
422 c->longname = strdup(hostnm);
426 * Only do DNS lookups for machines. Just print
427 * the network as the long name.
429 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
430 c->longname = strdup(buffer);
434 * Pull information over from the NAS.
436 nas = nas_find(c->ipaddr.ipaddr.ip4addr.s_addr);
439 * No short name in the
440 * 'clients' file, try
441 * copying one over from
442 * the 'naslist' file.
445 c->shortname = strdup(nas->shortname);
449 * Copy the nastype over, too.
451 c->nastype = strdup(nas->nastype);
457 hostnm[strlen(hostnm)] = '/';
458 c->longname = strdup(hostnm);
462 * Only do DNS lookups for machines. Just print
463 * the network as the long name.
465 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
466 c->longname = strdup(buffer);
468 /* TODO: NAS info as in IPv4 above */
475 * Failed to add the client: ignore the error
478 if (!client_add(clients, c)) {
485 radlog(L_INFO, "Using deprecated clients file. Support for this will go away soon.");
493 * Find the name of a client (prefer short name).
495 const char *client_name(const RADCLIENT_LIST *clients,
496 const lrad_ipaddr_t *ipaddr)
498 /* We don't call this unless we should know about the client. */
500 char host_ipaddr[128];
502 if ((cl = client_find(clients, ipaddr)) != NULL) {
503 if (cl->shortname && cl->shortname[0])
504 return cl->shortname;
510 * this isn't normally reachable, but if a loggable event happens just
511 * after a client list change and a HUP, then we may not know this
512 * information any more.
514 * If you see lots of these, then there's something wrong.
516 radlog(L_ERR, "Trying to look up name of unknown client %s.\n",
517 ip_ntoh(ipaddr, host_ipaddr, sizeof(host_ipaddr)));
519 return "UNKNOWN-CLIENT";
522 const char *client_name_old(const lrad_ipaddr_t *ipaddr)
524 return client_name(mainconfig.clients, ipaddr);
527 static const CONF_PARSER client_config[] = {
528 { "secret", PW_TYPE_STRING_PTR,
529 offsetof(RADCLIENT, secret), 0, NULL },
530 { "shortname", PW_TYPE_STRING_PTR,
531 offsetof(RADCLIENT, shortname), 0, NULL },
532 { "nastype", PW_TYPE_STRING_PTR,
533 offsetof(RADCLIENT, nastype), 0, NULL },
534 { "login", PW_TYPE_STRING_PTR,
535 offsetof(RADCLIENT, login), 0, NULL },
536 { "password", PW_TYPE_STRING_PTR,
537 offsetof(RADCLIENT, password), 0, NULL },
539 { NULL, -1, 0, NULL, NULL }
544 * Create the linked list of clients from the new configuration
545 * type. This way we don't have to change too much in the other
548 RADCLIENT_LIST *clients_parse_section(const char *filename,
549 CONF_SECTION *section)
553 char *hostnm, *prefix_ptr = NULL;
555 RADCLIENT_LIST *clients;
558 * Be forgiving. If there's already a clients, return
559 * it. Otherwise create a new one.
561 clients = cf_data_find(section, "clients");
562 if (clients) return clients;
564 clients = clients_init();
565 if (!clients) return NULL;
568 * Associate the clients structure with the section, where
569 * it will be freed once the section is freed.
571 if (cf_data_add(section, "clients", clients, clients_free) < 0) {
572 radlog(L_ERR, "%s[%d]: Failed to associate clients with section %s",
573 filename, cf_section_lineno(section),
574 cf_section_name1(section));
575 clients_free(clients);
579 for (cs = cf_subsection_find_next(section, NULL, "client");
581 cs = cf_subsection_find_next(section, cs, "client")) {
582 name2 = cf_section_name2(cs);
584 radlog(L_CONS|L_ERR, "%s[%d]: Missing client name",
585 filename, cf_section_lineno(cs));
589 * Check the lengths, we don't want any core dumps
592 prefix_ptr = strchr(hostnm, '/');
595 * The size is fine.. Let's create the buffer
597 c = rad_malloc(sizeof(RADCLIENT));
598 memset(c, 0, sizeof(RADCLIENT));
600 if (cf_section_parse(cs, c, client_config) < 0) {
601 radlog(L_CONS|L_ERR, "%s[%d]: Error parsing client section.",
602 filename, cf_section_lineno(cs));
611 c->prefix = atoi(prefix_ptr + 1);
612 if ((c->prefix < 0) || (c->prefix > 128)) {
613 radlog(L_ERR, "%s[%d]: Invalid Prefix value '%s' for IP.",
614 filename, cf_section_lineno(cs), prefix_ptr + 1);
617 /* Replace '/' with '\0' */
622 * Always get the numeric representation of IP
624 if (ip_hton(hostnm, AF_UNSPEC, &c->ipaddr) < 0) {
625 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s: %s",
626 filename, cf_section_lineno(cs),
627 hostnm, librad_errstr);
631 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
632 c->longname = strdup(buffer);
636 * This makes later life easier.
638 if (!c->shortname) c->shortname = strdup(c->longname);
640 if (c->prefix < 0) switch (c->ipaddr.af) {
652 * FIXME: Add the client as data via cf_data_add,
653 * for migration issues.
656 if (!client_add(clients, c)) {
657 radlog(L_CONS|L_ERR, "%s[%d]: Failed to add client %s",
658 filename, cf_section_lineno(cs), hostnm);