/*
- * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
+ * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* If TLS peers are configured, there will initially be 2 * #peers TLS threads
* For each TLS peer connecting to us there will be 2 more TLS threads
* This is only for connected peers
- * Example: With 3 UDP peer and 30 TLS peers, there will be a max of
+ * Example: With 3 UDP peers and 30 TLS peers, there will be a max of
* 1 + (2 + 2 * 3) + (2 * 30) + (2 * 30) = 129 threads
*/
#endif
#include <sys/time.h>
#include <sys/types.h>
-#include <sys/select.h>
#include <ctype.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include "list.h"
#include "hash.h"
#include "util.h"
+#include "hostport.h"
#include "radsecproxy.h"
#include "udp.h"
#include "tcp.h"
static const struct protodefs *protodefs[RAD_PROTOCOUNT];
/* minimum required declarations to avoid reordering code */
-struct realm *adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id);
+struct realm *adddynamicrealmserver(struct realm *realm, char *id);
int dynamicconfig(struct server *server);
int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val);
void freerealm(struct realm *realm);
pthread_mutex_unlock(&ssl_locks[type]);
}
-int resolvepeer(struct clsrvconf *conf, int ai_flags) {
- struct addrinfo hints, *addrinfo, *res;
- char *slash, *s;
- int plen = 0;
-
- slash = conf->host ? strchr(conf->host, '/') : NULL;
- if (slash) {
- s = slash + 1;
- if (!*s) {
- debug(DBG_WARN, "resolvepeer: prefix length must be specified after the / in %s", conf->host);
- return 0;
- }
- for (; *s; s++)
- if (*s < '0' || *s > '9') {
- debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host);
- return 0;
- }
- plen = atoi(slash + 1);
- if (plen < 0 || plen > 128) {
- debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host);
- return 0;
- }
- *slash = '\0';
- }
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = conf->pdef->socktype;
- hints.ai_family = AF_UNSPEC;
- hints.ai_flags = ai_flags;
- if (!conf->host && !conf->port) {
- /* getaddrinfo() doesn't like host and port to be NULL */
- if (getaddrinfo(conf->host, conf->pdef->portdefault, &hints, &addrinfo)) {
- debug(DBG_WARN, "resolvepeer: can't resolve (null) port (null)");
- return 0;
- }
- for (res = addrinfo; res; res = res->ai_next)
- port_set(res->ai_addr, 0);
- } else {
- if (slash)
- hints.ai_flags |= AI_NUMERICHOST;
- if (getaddrinfo(conf->host, conf->port, &hints, &addrinfo)) {
- debug(DBG_WARN, "resolvepeer: can't resolve %s port %s", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
- return 0;
- }
- if (slash) {
- *slash = '/';
- switch (addrinfo->ai_family) {
- case AF_INET:
- if (plen > 32) {
- debug(DBG_WARN, "resolvepeer: prefix length must be <= 32 in %s", conf->host);
- freeaddrinfo(addrinfo);
- return 0;
- }
- break;
- case AF_INET6:
- break;
- default:
- debug(DBG_WARN, "resolvepeer: prefix must be IPv4 or IPv6 in %s", conf->host);
- freeaddrinfo(addrinfo);
- return 0;
- }
- conf->prefixlen = (uint8_t)plen;
- } else
- conf->prefixlen = 255;
- }
- if (conf->addrinfo)
- freeaddrinfo(conf->addrinfo);
- conf->addrinfo = addrinfo;
- return 1;
-}
-
-char *parsehostport(char *s, struct clsrvconf *conf, char *default_port) {
- char *p, *field;
- int ipv6 = 0;
-
- p = s;
- /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
- if (*p == '[') {
- p++;
- field = p;
- for (; *p && *p != ']' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
- if (*p != ']')
- debugx(1, DBG_ERR, "no ] matching initial [");
- ipv6 = 1;
- } else {
- field = p;
- for (; *p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
- }
- if (field == p)
- debugx(1, DBG_ERR, "missing host/address");
-
- conf->host = stringcopy(field, p - field);
- if (ipv6) {
- p++;
- if (*p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n')
- debugx(1, DBG_ERR, "unexpected character after ]");
- }
- if (*p == ':') {
- /* port number or service name is specified */;
- field = ++p;
- for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
- if (field == p)
- debugx(1, DBG_ERR, "syntax error, : but no following port");
- conf->port = stringcopy(field, p - field);
- } else
- conf->port = default_port ? stringcopy(default_port, 0) : NULL;
- return p;
-}
-
-struct clsrvconf *resolve_hostport(uint8_t type, char *lconf, char *default_port) {
- struct clsrvconf *conf;
-
- conf = malloc(sizeof(struct clsrvconf));
- if (!conf)
- debugx(1, DBG_ERR, "malloc failed");
- memset(conf, 0, sizeof(struct clsrvconf));
- conf->type = type;
- conf->pdef = protodefs[conf->type];
- if (lconf) {
- parsehostport(lconf, conf, default_port);
- if (!strcmp(conf->host, "*")) {
- free(conf->host);
- conf->host = NULL;
- }
- } else
- conf->port = default_port ? stringcopy(default_port, 0) : NULL;
- if (!resolvepeer(conf, AI_PASSIVE))
- debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
- return conf;
-}
-
-void freeclsrvres(struct clsrvconf *res) {
- free(res->host);
- free(res->port);
- if (res->addrinfo)
- freeaddrinfo(res->addrinfo);
- free(res);
-}
-
-struct addrinfo *resolve_hostport_addrinfo(uint8_t type, char *hostport) {
- struct addrinfo *ai;
- struct clsrvconf *res;
-
- res = resolve_hostport(type, hostport, NULL);
- ai = res->addrinfo;
- res->addrinfo = NULL;
- freeclsrvres(res);
- return ai;
-}
-
/* returns 1 if the len first bits are equal, else 0 */
int prefixmatch(void *a1, void *a2, uint8_t len) {
static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
/* returns next config with matching address, or NULL */
struct clsrvconf *find_conf(uint8_t type, struct sockaddr *addr, struct list *confs, struct list_node **cur) {
- struct sockaddr_in6 *sa6 = NULL;
- struct in_addr *a4 = NULL;
- struct addrinfo *res;
struct list_node *entry;
struct clsrvconf *conf;
- if (addr->sa_family == AF_INET6) {
- sa6 = (struct sockaddr_in6 *)addr;
- if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
- a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
- sa6 = NULL;
- }
- } else
- a4 = &((struct sockaddr_in *)addr)->sin_addr;
-
for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
conf = (struct clsrvconf *)entry->data;
- if (conf->type == type) {
- if (conf->prefixlen == 255) {
- for (res = conf->addrinfo; res; res = res->ai_next)
- if ((a4 && res->ai_family == AF_INET &&
- !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
- (sa6 && res->ai_family == AF_INET6 &&
- !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) {
- if (cur)
- *cur = entry;
- return conf;
- }
- } else {
- res = conf->addrinfo;
- if (res &&
- ((a4 && res->ai_family == AF_INET &&
- prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, conf->prefixlen)) ||
- (sa6 && res->ai_family == AF_INET6 &&
- prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen)))) {
- if (cur)
- *cur = entry;
- return conf;
- }
- }
+ if (conf->type == type && addressmatches(conf->hostports, addr)) {
+ if (cur)
+ *cur = entry;
+ return conf;
}
}
return NULL;
goto errexit;
}
- debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->host);
+ debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->name);
to->requests[i].rq = rq;
pthread_mutex_unlock(to->requests[i].lock);
if (i >= start) /* i is not reserved for statusserver */
return NULL;
}
+int hasdynamicserver(struct list *srvconfs) {
+ struct list_node *entry;
+
+ for (entry = list_first(srvconfs); entry; entry = list_next(entry))
+ if (((struct clsrvconf *)entry->data)->dynamiclookupcommand)
+ return 1;
+ return 0;
+}
+
/* helper function, only used by removeserversubrealms() */
void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
struct list_node *entry, *entry2;
if (entry2->data == srv)
freerealm(realm);
list_removedata(realm->srvconfs, srv);
- if (!list_first(realm->srvconfs)) {
- list_destroy(realm->srvconfs);
- realm->srvconfs = NULL;
- }
}
if (realm->accsrvconfs) {
for (entry2 = list_first(realm->accsrvconfs); entry2; entry2 = list_next(entry2))
if (entry2->data == srv)
freerealm(realm);
list_removedata(realm->accsrvconfs, srv);
- if (!list_first(realm->accsrvconfs)) {
- list_destroy(realm->accsrvconfs);
- realm->accsrvconfs = NULL;
- }
}
- /* remove subrealm if no servers */
- if (!realm->srvconfs && !realm->accsrvconfs)
+ /* remove subrealm if no dynamic servers left */
+ if (!hasdynamicserver(realm->srvconfs) && !hasdynamicserver(realm->accsrvconfs)) {
+ while (list_shift(realm->srvconfs))
+ freerealm(realm);
+ list_destroy(realm->srvconfs);
+ realm->srvconfs = NULL;
+ while (list_shift(realm->accsrvconfs))
+ freerealm(realm);
+ list_destroy(realm->accsrvconfs);
+ realm->accsrvconfs = NULL;
list_removedata(realmlist, realm);
-
+ }
pthread_mutex_unlock(&realm->mutex);
freerealm(realm);
}
return a;
}
-void acclog(struct radmsg *msg, char *host) {
+void acclog(struct radmsg *msg, struct client *from) {
struct tlv *attr;
uint8_t *username;
-
+
attr = radmsg_gettype(msg, RAD_Attr_User_Name);
if (!attr) {
- debug(DBG_INFO, "acclog: accounting-request from %s without username attribute", host);
+ debug(DBG_INFO, "acclog: accounting-request from client %s (%s) without username attribute", from->conf->name, addr2string(from->addr));
return;
}
username = radattr2ascii(attr);
if (username) {
- debug(DBG_INFO, "acclog: accounting-request from %s with username: %s", host, username);
+ debug(DBG_INFO, "acclog: accounting-request from client %s (%s) with username: %s", from->conf->name, addr2string(from->addr), username);
+
free(username);
}
}
radmsg_free(rq->msg);
rq->msg = msg;
- debug(DBG_DBG, "respond: sending %s to %s", radmsgtype2string(msg->code), rq->from->conf->host);
+ debug(DBG_DBG, "respond: sending %s to %s (%s)", radmsgtype2string(msg->code), rq->from->conf->name, addr2string(rq->from->addr));
sendreply(newrqref(rq));
}
return server;
if (!first)
first = server;
- if (!server->servers->connectionok)
+ if (!server->servers->connectionok && !server->servers->dynstartup)
continue;
if (!server->servers->lostrqs)
return server;
goto exit;
debug(DBG_DBG, "found matching realm: %s", (*realm)->name);
srvconf = choosesrvconf(acc ? (*realm)->accsrvconfs : (*realm)->srvconfs);
- if (!srvconf)
- goto exit;
- server = srvconf->servers;
- if (!acc && !(*realm)->parent && !srvconf->servers) {
- subrealm = adddynamicrealmserver(*realm, srvconf, id);
+ if (srvconf && !(*realm)->parent && !srvconf->servers && srvconf->dynamiclookupcommand) {
+ subrealm = adddynamicrealmserver(*realm, id);
if (subrealm) {
pthread_mutex_lock(&subrealm->mutex);
pthread_mutex_unlock(&(*realm)->mutex);
freerealm(*realm);
*realm = subrealm;
- server = ((struct clsrvconf *)subrealm->srvconfs->first->data)->servers;
+ srvconf = choosesrvconf(acc ? (*realm)->accsrvconfs : (*realm)->srvconfs);
}
}
+ if (srvconf)
+ server = srvconf->servers;
+
exit:
free(id);
return server;
attr = radmsg_gettype(msg, RAD_Attr_User_Name);
if (!attr) {
if (msg->code == RAD_Accounting_Request) {
- acclog(msg, from->conf->host);
+ acclog(msg, from);
respond(rq, RAD_Accounting_Response, NULL);
} else
debug(DBG_WARN, "radsrv: ignoring access request, no username attribute");
if (!to) {
if (realm->message && msg->code == RAD_Access_Request) {
- debug(DBG_INFO, "radsrv: sending reject to %s for %s", from->conf->host, userascii);
+ debug(DBG_INFO, "radsrv: sending reject to %s (%s) for %s", from->conf->name, addr2string(from->addr), userascii);
respond(rq, RAD_Access_Reject, realm->message);
} else if (realm->accresp && msg->code == RAD_Accounting_Request) {
- acclog(msg, from->conf->host);
+ acclog(msg, from);
respond(rq, RAD_Accounting_Response, NULL);
}
goto exit;
if (rqout->rq->msg->code == RAD_Status_Server) {
freerqoutdata(rqout);
- debug(DBG_DBG, "replyh: got status server response from %s", server->conf->host);
+ debug(DBG_DBG, "replyh: got status server response from %s", server->conf->name);
goto errunlock;
}
ttlres = checkttl(msg, options.ttlattrtype);
if (!ttlres) {
- debug(DBG_WARN, "replyh: ignoring reply from server %s, ttl exceeded", server->conf->host);
+ debug(DBG_WARN, "replyh: ignoring reply from server %s, ttl exceeded", server->conf->name);
goto errunlock;
}
if (stationid) {
if (replymsg) {
debug(DBG_INFO, "%s for user %s stationid %s from %s (%s)",
- radmsgtype2string(msg->code), username, stationid, server->conf->host, replymsg);
+ radmsgtype2string(msg->code), username, stationid, server->conf->name, replymsg);
free(replymsg);
} else
debug(DBG_INFO, "%s for user %s stationid %s from %s",
- radmsgtype2string(msg->code), username, stationid, server->conf->host);
+ radmsgtype2string(msg->code), username, stationid, server->conf->name);
free(stationid);
} else {
if (replymsg) {
debug(DBG_INFO, "%s for user %s from %s (%s)",
- radmsgtype2string(msg->code), username, server->conf->host, replymsg);
+ radmsgtype2string(msg->code), username, server->conf->name, replymsg);
free(replymsg);
} else
debug(DBG_INFO, "%s for user %s from %s",
- radmsgtype2string(msg->code), username, server->conf->host);
+ radmsgtype2string(msg->code), username, server->conf->name);
}
free(username);
}
if (server->dynamiclookuparg && !dynamicconfig(server)) {
dynconffail = 1;
+ server->dynstartup = 0;
+ sleep(900);
goto errexit;
}
- if (!conf->addrinfo && !resolvepeer(conf, 0)) {
- debug(DBG_WARN, "failed to resolve host %s port %s", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
+ if (!resolvehostports(conf->hostports, conf->pdef->socktype)) {
+ debug(DBG_WARN, "clientwr: resolve failed");
+ server->dynstartup = 0;
+ sleep(900);
goto errexit;
}
}
if (conf->pdef->connecter) {
- if (!conf->pdef->connecter(server, NULL, server->dynamiclookuparg ? 6 : 0, "clientwr"))
+ if (!conf->pdef->connecter(server, NULL, server->dynamiclookuparg ? 6 : 0, "clientwr")) {
+ if (server->dynamiclookuparg) {
+ server->dynstartup = 0;
+ sleep(900);
+ }
goto errexit;
+ }
server->connectionok = 1;
if (pthread_create(&clientrdth, NULL, conf->pdef->clientconnreader, (void *)server)) {
debug(DBG_ERR, "clientwr: pthread_create failed");
}
} else
server->connectionok = 1;
+ server->dynstartup = 0;
for (;;) {
pthread_mutex_lock(&server->newrq_mutex);
debug(DBG_DBG, "clientwr: removing expired packet from queue");
if (conf->statusserver) {
if (*rqout->rq->buf == RAD_Status_Server) {
- debug(DBG_WARN, "clientwr: no status server response, %s dead?", conf->host);
+ debug(DBG_WARN, "clientwr: no status server response, %s dead?", conf->name);
if (server->lostrqs < 255)
server->lostrqs++;
}
} else {
- debug(DBG_WARN, "clientwr: no server response, %s dead?", conf->host);
+ debug(DBG_WARN, "clientwr: no server response, %s dead?", conf->name);
if (server->lostrqs < 255)
server->lostrqs++;
}
statsrvrq = createstatsrvrq();
if (statsrvrq) {
statsrvrq->to = server;
- debug(DBG_DBG, "clientwr: sending status server to %s", conf->host);
+ debug(DBG_DBG, "clientwr: sending status server to %s", conf->name);
sendrq(statsrvrq);
}
}
void createlistener(uint8_t type, char *arg) {
pthread_t th;
- struct clsrvconf *listenres;
struct addrinfo *res;
int s = -1, on = 1, *sp = NULL;
+ struct hostportres *hp = newhostport(arg, protodefs[type]->portdefault, 0);
- listenres = resolve_hostport(type, arg, protodefs[type]->portdefault);
- if (!listenres)
+ if (!hp || !resolvehostport(hp, protodefs[type]->socktype, 1))
debugx(1, DBG_ERR, "createlistener: failed to resolve %s", arg);
- for (res = listenres->addrinfo; res; res = res->ai_next) {
+ for (res = hp->addrinfo; res; res = res->ai_next) {
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s < 0) {
debug(DBG_WARN, "createlistener: socket failed");
#ifdef IPV6_V6ONLY
if (res->ai_family == AF_INET6)
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
-#endif
+#endif
if (bind(s, res->ai_addr, res->ai_addrlen)) {
debug(DBG_WARN, "createlistener: bind failed");
close(s);
if (!sp)
debugx(1, DBG_ERR, "createlistener: socket/bind failed");
- debug(DBG_WARN, "createlistener: listening for %s on %s:%s", protodefs[type]->name,
- listenres->host ? listenres->host : "*", listenres->port);
- freeclsrvres(listenres);
+ debug(DBG_WARN, "createlistener: listening for %s on %s:%s", protodefs[type]->name, hp->host ? hp->host : "*", hp->port);
+ freehostport(hp);
}
void createlisteners(uint8_t type) {
return newrealmref(realm);
}
-struct realm *adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id) {
- struct clsrvconf *srvconf;
+struct list *createsubrealmservers(struct realm *realm, struct list *srvconfs) {
+ struct list_node *entry;
+ struct clsrvconf *conf, *srvconf;
+ struct list *subrealmservers = NULL;
+ pthread_t clientth;
+
+ if (list_first(srvconfs)) {
+ subrealmservers = list_create();
+ if (!subrealmservers)
+ return NULL;
+ }
+
+ for (entry = list_first(srvconfs); entry; entry = list_next(entry)) {
+ conf = (struct clsrvconf *)entry->data;
+ if (!conf->servers && conf->dynamiclookupcommand) {
+ srvconf = malloc(sizeof(struct clsrvconf));
+ if (!srvconf) {
+ debug(DBG_ERR, "malloc failed");
+ continue;
+ }
+ *srvconf = *conf;
+ if (addserver(srvconf)) {
+ srvconf->servers->dynamiclookuparg = stringcopy(realm->name, 0);
+ srvconf->servers->dynstartup = 1;
+ if (pthread_create(&clientth, NULL, clientwr, (void *)(srvconf->servers))) {
+ debug(DBG_ERR, "pthread_create failed");
+ freeserver(srvconf->servers, 1);
+ srvconf->servers = NULL;
+ } else
+ pthread_detach(clientth);
+ }
+ conf = srvconf;
+ }
+ if (conf->servers) {
+ if (list_push(subrealmservers, conf))
+ newrealmref(realm);
+ else
+ debug(DBG_ERR, "malloc failed");
+ }
+ }
+ return subrealmservers;
+}
+
+struct realm *adddynamicrealmserver(struct realm *realm, char *id) {
struct realm *newrealm = NULL;
char *realmname, *s;
- pthread_t clientth;
- if (!conf->dynamiclookupcommand)
- return NULL;
-
/* create dynamic for the realm (string after last @, exit if nothing after @ */
realmname = strrchr(id, '@');
if (!realmname)
if (*s != '.' && *s != '-' && !isalnum((int)*s))
return NULL;
- srvconf = malloc(sizeof(struct clsrvconf));
- if (!srvconf) {
- debug(DBG_ERR, "malloc failed");
- return NULL;
- }
- *srvconf = *conf;
- if (!addserver(srvconf))
- goto errexit;
-
if (!realm->subrealms)
realm->subrealms = list_create();
if (!realm->subrealms)
- goto errexit;
- newrealm = addrealm(realm->subrealms, realmname, NULL, NULL, NULL, 0);
- if (!newrealm)
- goto errexit;
- newrealm->parent = newrealmref(realm);
+ return NULL;
- /* add server and accserver to newrealm */
- newrealm->srvconfs = list_create();
- if (!newrealm->srvconfs || !list_push(newrealm->srvconfs, srvconf)) {
- debug(DBG_ERR, "malloc failed");
- goto errexit;
- }
- newrealmref(newrealm);
- newrealm->accsrvconfs = list_create();
- if (!newrealm->accsrvconfs || !list_push(newrealm->accsrvconfs, srvconf)) {
- debug(DBG_ERR, "malloc failed");
- list_shift(realm->srvconfs);
- freerealm(newrealm);
- goto errexit;
+ newrealm = addrealm(realm->subrealms, realmname, NULL, NULL, stringcopy(realm->message, 0), realm->accresp);
+ if (!newrealm) {
+ list_destroy(realm->subrealms);
+ realm->subrealms = NULL;
+ return NULL;
}
- newrealmref(newrealm);
- srvconf->servers->dynamiclookuparg = stringcopy(realmname, 0);
- if (pthread_create(&clientth, NULL, clientwr, (void *)(srvconf->servers))) {
- debug(DBG_ERR, "pthread_create failed");
- list_shift(realm->srvconfs);
- freerealm(newrealm);
- list_shift(realm->accsrvconfs);
- freerealm(newrealm);
- goto errexit;
- }
- pthread_detach(clientth);
+ newrealm->parent = newrealmref(realm);
+ /* add server and accserver to newrealm */
+ newrealm->srvconfs = createsubrealmservers(newrealm, realm->srvconfs);
+ newrealm->accsrvconfs = createsubrealmservers(newrealm, realm->accsrvconfs);
return newrealm;
-
- errexit:
- if (newrealm) {
- list_removedata(realm->subrealms, newrealm);
- freerealm(newrealm);
- if (!list_first(realm->subrealms)) {
- list_destroy(realm->subrealms);
- realm->subrealms = NULL;
- }
- }
- freeserver(srvconf->servers, 1);
- free(srvconf);
- debug(DBG_ERR, "failed to create dynamic server");
- return NULL;
}
int dynamicconfig(struct server *server) {
void freeclsrvconf(struct clsrvconf *conf) {
free(conf->name);
- free(conf->host);
- free(conf->port);
+ if (conf->hostsrc)
+ freegconfmstr(conf->hostsrc);
+ free(conf->portsrc);
free(conf->secret);
free(conf->tls);
free(conf->matchcertattr);
free(conf->dynamiclookupcommand);
free(conf->rewritein);
free(conf->rewriteout);
- if (conf->addrinfo)
- freeaddrinfo(conf->addrinfo);
+ if (conf->hostports)
+ freehostports(conf->hostports);
if (conf->lock) {
pthread_mutex_destroy(conf->lock);
free(conf->lock);
return 1;
}
+char **mstringcopy(char **in) {
+ char **out;
+ int n;
+
+ if (!in)
+ return NULL;
+
+ for (n = 0; in[n]; n++);
+ out = malloc((n + 1) * sizeof(char *));
+ if (!out)
+ return NULL;
+ for (n = 0; in[n]; n++) {
+ out[n] = stringcopy(in[n], 0);
+ if (!out[n]) {
+ freegconfmstr(out);
+ return NULL;
+ }
+ }
+ out[n] = NULL;
+ return out;
+}
+
+int mergeconfmstring(char ***dst, char ***src) {
+ char **t;
+
+ if (*src) {
+ *dst = *src;
+ *src = NULL;
+ return 1;
+ }
+ if (*dst) {
+ t = mstringcopy(*dst);
+ if (!t) {
+ debug(DBG_ERR, "malloc failed");
+ return 0;
+ }
+ *dst = t;
+ }
+ return 1;
+}
+
/* assumes dst is a shallow copy */
int mergesrvconf(struct clsrvconf *dst, struct clsrvconf *src) {
if (!mergeconfstring(&dst->name, &src->name) ||
- !mergeconfstring(&dst->host, &src->host) ||
- !mergeconfstring(&dst->port, &src->port) ||
+ !mergeconfmstring(&dst->hostsrc, &src->hostsrc) ||
+ !mergeconfstring(&dst->portsrc, &src->portsrc) ||
!mergeconfstring(&dst->secret, &src->secret) ||
!mergeconfstring(&dst->tls, &src->tls) ||
!mergeconfstring(&dst->matchcertattr, &src->matchcertattr) ||
if (!getgenericconfig(cf, block,
"type", CONF_STR, &conftype,
- "host", CONF_STR, &conf->host,
+ "host", CONF_MSTR, &conf->hostsrc,
"secret", CONF_STR, &conf->secret,
#if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
"tls", CONF_STR, &conf->tls,
debugx(1, DBG_ERR, "configuration error");
conf->name = stringcopy(val, 0);
- if (!conf->host)
- conf->host = stringcopy(val, 0);
- if (!conf->name || !conf->host)
+ if (conf->name && !conf->hostsrc) {
+ conf->hostsrc = malloc(2 * sizeof(char *));
+ if (conf->hostsrc) {
+ conf->hostsrc[0] = stringcopy(val, 0);
+ conf->hostsrc[1] = NULL;
+ }
+ }
+ if (!conf->name || !conf->hostsrc || !conf->hostsrc[0])
debugx(1, DBG_ERR, "malloc failed");
if (!conftype)
if (!conf->rewriteusername)
debugx(1, DBG_ERR, "error in block %s, invalid RewriteAttributeValue", block);
}
-
- if (!resolvepeer(conf, 0))
- debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
+
+ if (!addhostport(&conf->hostports, conf->hostsrc, conf->pdef->portdefault, 1) ||
+ !resolvehostports(conf->hostports, conf->pdef->socktype))
+ debugx(1, DBG_ERR, "resolve failed, exiting");
if (!conf->secret) {
if (!conf->pdef->secretdefault)
}
#endif
- if (!conf->port) {
- conf->port = stringcopy(conf->pdef->portdefault, 0);
- if (!conf->port) {
+ if (!conf->portsrc) {
+ conf->portsrc = stringcopy(conf->pdef->portdefault, 0);
+ if (!conf->portsrc) {
debug(DBG_ERR, "malloc failed");
return 0;
}
conf->rewritein = conf->confrewritein ? getrewrite(conf->confrewritein, NULL) : getrewrite("defaultserver", "default");
if (conf->confrewriteout)
conf->rewriteout = getrewrite(conf->confrewriteout, NULL);
+
+ if (!addhostport(&conf->hostports, conf->hostsrc, conf->portsrc, 0)) {
+ debug(DBG_ERR, "error in block %s, failed to parse %s", block, conf->hostsrc);
+ return 0;
+ }
- if (!conf->dynamiclookupcommand && !resolvepeer(conf, 0)) {
- debug(DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
+ if (!conf->dynamiclookupcommand && !resolvehostports(conf->hostports, conf->pdef->socktype)) {
+ debug(DBG_ERR, "resolve failed, exiting");
return 0;
}
return 1;
if (!getgenericconfig(cf, block,
"type", CONF_STR, &conftype,
- "host", CONF_STR, &conf->host,
- "port", CONF_STR, &conf->port,
+ "host", CONF_MSTR, &conf->hostsrc,
+ "port", CONF_STR, &conf->portsrc,
"secret", CONF_STR, &conf->secret,
#if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
"tls", CONF_STR, &conf->tls,
}
conf->name = stringcopy(val, 0);
- if (!conf->name) {
+ if (conf->name && !conf->hostsrc) {
+ conf->hostsrc = malloc(2 * sizeof(char *));
+ if (conf->hostsrc) {
+ conf->hostsrc[0] = stringcopy(val, 0);
+ conf->hostsrc[1] = NULL;
+ }
+ }
+ if (!conf->name || !conf->hostsrc || !conf->hostsrc[0]) {
debug(DBG_ERR, "malloc failed");
goto errexit;
}
- if (!conf->host) {
- conf->host = stringcopy(val, 0);
- if (!conf->host) {
- debug(DBG_ERR, "malloc failed");
- goto errexit;
- }
- }
- if (!conftype)
- debugx(1, DBG_ERR, "error in block %s, option type missing", block);
+ if (!conftype) {
+ debug(DBG_ERR, "error in block %s, option type missing", block);
+ goto errexit;
+ }
conf->type = protoname2int(conftype);
if (conf->type == 255) {
debug(DBG_ERR, "error in block %s, unknown transport %s", block, conftype);