#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/vqp.h>
-#include <freeradius-devel/dhcp.h>
#include <freeradius-devel/process.h>
#include <freeradius-devel/protocol.h>
+#include <freeradius-devel/modpriv.h>
#include <freeradius-devel/detail.h>
/*
* Xlat for %{listen:foo}
*/
-static size_t xlat_listen(UNUSED void *instance, REQUEST *request,
- const char *fmt, char *out,
- size_t outlen)
+static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out,
+ size_t outlen)
{
- const char *value = NULL;
+ char const *value = NULL;
CONF_PAIR *cp;
if (!fmt || !out || (outlen < 1)) return 0;
- if (!request || !request->listener) {
+ if (!request->listener) {
+ RWDEBUG("No listener associated with this request");
*out = '\0';
return 0;
}
cp = cf_pair_find(request->listener->cs, fmt);
if (!cp || !(value = cf_pair_value(cp))) {
+ RDEBUG("Listener does not contain config item \"%s\"", fmt);
*out = '\0';
return 0;
}
-
+
strlcpy(out, value, outlen);
return strlen(out);
* Find a per-socket client.
*/
RADCLIENT *client_listener_find(rad_listen_t *listener,
- const fr_ipaddr_t *ipaddr, int src_port)
+ fr_ipaddr_t const *ipaddr, uint16_t src_port)
{
#ifdef WITH_DYNAMIC_CLIENTS
int rcode;
client = client_find(clients, ipaddr,sock->proto);
if (!client) {
char name[256], buffer[128];
-
+
#ifdef WITH_DYNAMIC_CLIENTS
unknown: /* used only for dynamic clients */
#endif
now = time(NULL);
if (last_printed == now) return NULL;
-
+
last_printed = now;
}
listener->print(listener, name, sizeof(name));
- radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d"
+ ERROR("Ignoring request to %s from unknown client %s port %d"
#ifdef WITH_TCP
" proto %s"
#endif
if (!client->client_server && !client->dynamic) return client;
now = time(NULL);
-
+
/*
* It's a dynamically generated client, check it.
*/
* Lives forever. Return it.
*/
if (client->lifetime == 0) return client;
-
+
/*
* Rate-limit the deletion of known clients.
* This makes them last a little longer, but
* It's not dead yet. Return it.
*/
if ((client->created + client->lifetime) > now) return client;
-
+
/*
* This really puts them onto a queue for later
* deletion.
client->last_new_client = now;
- request = request_alloc();
+ request = request_alloc(NULL);
if (!request) goto unknown;
request->listener = listener;
request->client = client;
request->packet = rad_recv(listener->fd, 0x02); /* MSG_PEEK */
if (!request->packet) { /* badly formed, etc */
- request_free(&request);
+ talloc_free(request);
goto unknown;
}
request->reply = rad_alloc_reply(request, request->packet);
if (!request->reply) {
- request_free(&request);
+ talloc_free(request);
goto unknown;
}
gettimeofday(&request->packet->timestamp, NULL);
request->number = 0;
request->priority = listener->type;
request->server = client->client_server;
- request->root = &mainconfig;
+ request->root = &main_config;
/*
* Run a fake request through the given virtual server.
DEBUG("} # server %s", request->server);
if (rcode != RLM_MODULE_OK) {
- request_free(&request);
+ talloc_free(request);
goto unknown;
}
* don't create the client from attribute-value pairs.
*/
if (request->client == client) {
- created = client_create(clients, request);
+ created = client_from_request(clients, request);
} else {
created = request->client;
}
request->server = client->server;
- exec_trigger(request, NULL, "server.client.add", FALSE);
+ exec_trigger(request, NULL, "server.client.add", false);
- request_free(&request);
+ talloc_free(request);
if (!created) goto unknown;
switch (rcode) {
case RLM_MODULE_OK:
case RLM_MODULE_UPDATED:
- request->reply->code = PW_AUTHENTICATION_ACK;
+ request->reply->code = PW_CODE_ACCESS_ACCEPT;
break;
case RLM_MODULE_FAIL:
default:
case RLM_MODULE_REJECT:
- request->reply->code = PW_AUTHENTICATION_REJECT;
+ request->reply->code = PW_CODE_ACCESS_REJECT;
break;
}
break;
switch (rcode) {
case RLM_MODULE_OK:
case RLM_MODULE_UPDATED:
- request->reply->code = PW_ACCOUNTING_RESPONSE;
+ request->reply->code = PW_CODE_ACCOUNTING_RESPONSE;
break;
default:
switch (rcode) {
case RLM_MODULE_OK:
case RLM_MODULE_UPDATED:
- request->reply->code = PW_COA_ACK;
+ request->reply->code = PW_CODE_COA_ACK;
break;
default:
listen_socket_t *sock = listener->data;
RADCLIENT *client = sock->client;
+ rad_assert(client != NULL);
+
+ if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
+
/*
* Allocate a packet for partial reads.
*/
if (!sock->packet) {
- sock->packet = rad_alloc(NULL, 0);
+ sock->packet = rad_alloc(sock, false);
if (!sock->packet) return 0;
sock->packet->sockfd = listener->fd;
sock->packet->src_port = sock->other_port;
sock->packet->dst_ipaddr = sock->my_ipaddr;
sock->packet->dst_port = sock->my_port;
+ sock->packet->proto = sock->proto;
}
-
+
/*
* Grab the packet currently being processed.
*/
if (rcode == -1) { /* error reading packet */
char buffer[256];
- radlog(L_ERR, "Invalid packet from %s port %d: closing socket",
+ ERROR("Invalid packet from %s port %d, closing socket: %s",
ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
- packet->src_port);
+ packet->src_port, fr_strerror());
}
if (rcode < 0) { /* error or connection reset */
- listener->status = RAD_LISTEN_STATUS_REMOVE_FD;
-
- /*
- * Decrement the number of connections.
- */
- if (sock->parent->limit.num_connections > 0) {
- sock->parent->limit.num_connections--;
- }
- if (sock->client->limit.num_connections > 0) {
- sock->client->limit.num_connections--;
- }
+ listener->status = RAD_LISTEN_STATUS_EOL;
/*
* Tell the event handler that an FD has disappeared.
*/
DEBUG("Client has closed connection");
- event_new_fd(listener);
+ radius_update_listener(listener);
/*
* Do NOT free the listener here. It's in use by
* Some sanity checks, based on the packet code.
*/
switch(packet->code) {
- case PW_AUTHENTICATION_REQUEST:
+ case PW_CODE_ACCESS_REQUEST:
if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
FR_STATS_INC(auth, total_requests);
fun = rad_authenticate;
break;
#ifdef WITH_ACCOUNTING
- case PW_ACCOUNTING_REQUEST:
+ case PW_CODE_ACCOUNTING_REQUEST:
if (listener->type != RAD_LISTEN_ACCT) goto bad_packet;
FR_STATS_INC(acct, total_requests);
fun = rad_accounting;
break;
#endif
- case PW_STATUS_SERVER:
- if (!mainconfig.status_server) {
+ case PW_CODE_STATUS_SERVER:
+ if (!main_config.status_server) {
FR_STATS_INC(auth, total_unknown_types);
- DEBUGW("Ignoring Status-Server request due to security configuration");
+ WARN("Ignoring Status-Server request due to security configuration");
rad_free(&sock->packet);
return 0;
}
static int dual_tcp_accept(rad_listen_t *listener)
{
- int newfd, src_port;
+ int newfd;
+ uint16_t src_port;
rad_listen_t *this;
socklen_t salen;
struct sockaddr_storage src;
listen_socket_t *sock;
fr_ipaddr_t src_ipaddr;
RADCLIENT *client = NULL;
-
+
salen = sizeof(src);
- DEBUG2(" ... new connection request on TCP socket.");
-
+ DEBUG2(" ... new connection request on TCP socket");
+
newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
if (newfd < 0) {
/*
}
#endif
- DEBUG2(" ... failed to accept connection.");
+ DEBUG2(" ... failed to accept connection");
return -1;
}
if (!fr_sockaddr2ipaddr(&src, salen, &src_ipaddr, &src_port)) {
close(newfd);
- DEBUG2(" ... unknown address family.");
+ DEBUG2(" ... unknown address family");
return 0;
}
return 0;
}
+#ifdef WITH_TLS
+ /*
+ * Enforce security restrictions.
+ *
+ * This shouldn't be necessary in practice. However, it
+ * serves as a double-check on configurations. Marking a
+ * client as "tls required" means that any accidental
+ * exposure of the client to non-TLS traffic is
+ * prevented.
+ */
+ if (client->tls_required && !listener->tls) {
+ INFO("Ignoring connection to TLS socket from non-TLS client");
+ close(newfd);
+ return 0;
+ }
+#endif
+
/*
* Enforce max_connections on client && listen section.
*/
/*
* FIXME: Print client IP/port, and server IP/port.
*/
- radlog(L_INFO, "Ignoring new connection due to client max_connections (%d)", client->limit.max_connections);
+ INFO("Ignoring new connection due to client max_connections (%d)", client->limit.max_connections);
close(newfd);
return 0;
}
/*
* FIXME: Print client IP/port, and server IP/port.
*/
- radlog(L_INFO, "Ignoring new connection due to socket max_connections");
+ INFO("Ignoring new connection due to socket max_connections");
close(newfd);
return 0;
}
{
this->recv = dual_tcp_recv;
-
+
#ifdef WITH_TLS
if (this->tls) {
this->recv = dual_tls_recv;
- this->send = dual_tls_send;
+ this->send = dual_tls_send;
}
#endif
}
* Tell the event loop that we have a new FD.
* This can be called from a child thread...
*/
- event_new_fd(this);
+ radius_update_listener(this);
return 0;
}
#endif
+/*
+ * Ensure that we always keep the correct counters.
+ */
+#ifdef WITH_TCP
+static void common_socket_free(rad_listen_t *this)
+{
+ listen_socket_t *sock = this->data;
+
+ if (sock->proto != IPPROTO_TCP) return;
+
+ /*
+ * Decrement the number of connections.
+ */
+ if (sock->parent && (sock->parent->limit.num_connections > 0)) {
+ sock->parent->limit.num_connections--;
+ }
+ if (sock->client && sock->client->limit.num_connections > 0) {
+ sock->client->limit.num_connections--;
+ }
+ if (sock->home && sock->home->limit.num_connections > 0) {
+ sock->home->limit.num_connections--;
+ }
+}
+#else
+static void common_socket_free(UNUSED rad_listen_t *this)
+{
+ return;
+}
+#endif
/*
* This function is stupid and complicated.
*/
-int common_socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
+int common_socket_print(rad_listen_t const *this, char *buffer, size_t bufsize)
{
size_t len;
listen_socket_t *sock = this->data;
- const char *name = master_listen[this->type].name;
+ char const *name = master_listen[this->type].name;
#define FORWARD len = strlen(buffer); if (len >= (bufsize + 1)) return 0;buffer += len;bufsize -= len
#define ADDSTRING(_x) strlcpy(buffer, _x, bufsize);FORWARD
ADDSTRING(name);
+ if (this->dual) {
+ ADDSTRING("+acct");
+ }
+
if (sock->interface) {
ADDSTRING(" interface ");
ADDSTRING(sock->interface);
ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
}
FORWARD;
-
+
ADDSTRING(", ");
snprintf(buffer, bufsize, "%d", sock->my_port);
FORWARD;
ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
}
FORWARD;
-
+
ADDSTRING(", ");
snprintf(buffer, bufsize, "%d", sock->other_port);
FORWARD;
#endif /* WITH_TCP */
ADDSTRING(" address ");
-
+
if ((sock->my_ipaddr.af == AF_INET) &&
(sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
strlcpy(buffer, "*", bufsize);
return 1;
}
-extern int check_config; /* radiusd.c */
+extern bool check_config; /* radiusd.c */
-#ifdef WITH_TCP
-static CONF_PARSER limit_config[] = {
- { "max_connections", PW_TYPE_INTEGER,
- offsetof(listen_socket_t, limit.max_connections), NULL, "16" },
+static CONF_PARSER performance_config[] = {
+ { "skip_duplicate_checks", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rad_listen_t, nodup), NULL },
- { "lifetime", PW_TYPE_INTEGER,
- offsetof(listen_socket_t, limit.lifetime), NULL, "0" },
+ { "synchronous", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rad_listen_t, synchronous), NULL },
- { "idle_timeout", PW_TYPE_INTEGER,
- offsetof(listen_socket_t, limit.idle_timeout), NULL, "30" },
+ { "workers", FR_CONF_OFFSET(PW_TYPE_INTEGER, rad_listen_t, workers), NULL },
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
+
+
+static CONF_PARSER limit_config[] = {
+ { "max_pps", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, max_rate), NULL },
+
+#ifdef WITH_TCP
+ { "max_connections", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.max_connections), "16" },
+ { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.lifetime), "0" },
+ { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.idle_timeout), STRINGIFY(30) },
#endif
+ { NULL, -1, 0, NULL, NULL } /* end the list */
+};
+
/*
* Parse an authentication or accounting socket.
*/
int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
{
int rcode;
- int listen_port, max_pps;
+ uint16_t listen_port;
fr_ipaddr_t ipaddr;
listen_socket_t *sock = this->data;
- char *section_name = NULL;
+ char const *section_name = NULL;
CONF_SECTION *client_cs, *parentcs;
+ CONF_SECTION *subcs;
this->cs = cs;
*/
memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR,
- &ipaddr.ipaddr.ip4addr, NULL);
- if (rcode < 0) return -1;
- if (rcode == 0) { /* successfully parsed IPv4 */
- ipaddr.af = AF_INET;
-
- } else { /* maybe IPv6? */
- rcode = cf_item_parse(cs, "ipv6addr", PW_TYPE_IPV6ADDR,
- &ipaddr.ipaddr.ip6addr, NULL);
- if (rcode < 0) return -1;
-
- if (rcode == 1) {
- cf_log_err_cs(cs,
- "No address specified in listen section");
- return -1;
- }
- ipaddr.af = AF_INET6;
- }
-
- rcode = cf_item_parse(cs, "port", PW_TYPE_INTEGER,
- &listen_port, "0");
+ rcode = cf_item_parse(cs, "ipaddr", FR_ITEM_POINTER(PW_TYPE_IP_ADDR, &ipaddr), NULL);
if (rcode < 0) return -1;
-
- if ((listen_port < 0) || (listen_port > 65535)) {
- cf_log_err_cs(cs,
- "Invalid value for \"port\"");
- return -1;
+ if (rcode != 0) rcode = cf_item_parse(cs, "ipv4addr", FR_ITEM_POINTER(PW_TYPE_IPV4_ADDR, &ipaddr), NULL);
+ if (rcode < 0) return -1;
+ if (rcode != 0) rcode = cf_item_parse(cs, "ipv6addr", FR_ITEM_POINTER(PW_TYPE_IPV6_ADDR, &ipaddr), NULL);
+ if (rcode < 0) return -1;
+ if (rcode != 0) {
+ cf_log_err_cs(cs, "No address specified in listen section");
+ return -1;
}
- rcode = cf_item_parse(cs, "max_pps", PW_TYPE_INTEGER,
- &max_pps, "0");
+ rcode = cf_item_parse(cs, "port", FR_ITEM_POINTER(PW_TYPE_SHORT, &listen_port), "0");
if (rcode < 0) return -1;
- if (max_pps && ((max_pps < 10) || (max_pps > 1000000))) {
- cf_log_err_cs(cs,
- "Invalid value for \"max_pps\"");
- return -1;
- }
-
sock->proto = IPPROTO_UDP;
if (cf_pair_find(cs, "proto")) {
#ifndef WITH_TCP
cf_log_err_cs(cs,
- "System does not support the TCP protocol. Delete this line from the configuration file.");
+ "System does not support the TCP protocol. Delete this line from the configuration file");
return -1;
#else
- char *proto = NULL;
+ char const *proto = NULL;
#ifdef WITH_TLS
CONF_SECTION *tls;
#endif
- rcode = cf_item_parse(cs, "proto", PW_TYPE_STRING_PTR,
- &proto, "udp");
+ rcode = cf_item_parse(cs, "proto", FR_ITEM_POINTER(PW_TYPE_STRING, &proto), "udp");
if (rcode < 0) return -1;
if (strcmp(proto, "udp") == 0) {
} else if (strcmp(proto, "tcp") == 0) {
sock->proto = IPPROTO_TCP;
- CONF_SECTION *limit;
-
- limit = cf_section_sub_find(cs, "limit");
- if (limit) {
- rcode = cf_section_parse(limit, sock,
- limit_config);
- if (rcode < 0) return -1;
- } else {
- sock->limit.max_connections = 60;
- sock->limit.idle_timeout = 30;
- sock->limit.lifetime = 0;
- }
-
- if ((sock->limit.idle_timeout > 0) && (sock->limit.idle_timeout < 5))
- sock->limit.idle_timeout = 5;
- if ((sock->limit.lifetime > 0) && (sock->limit.lifetime < 5))
- sock->limit.lifetime = 5;
- if ((sock->limit.lifetime > 0) && (sock->limit.idle_timeout > sock->limit.lifetime))
- sock->limit.idle_timeout = 0;
} else {
cf_log_err_cs(cs,
"Unknown proto name \"%s\"", proto);
#ifdef WITH_TLS
tls = cf_section_sub_find(cs, "tls");
- /*
- * Don't allow TLS configurations for UDP sockets.
- */
- if (sock->proto != IPPROTO_TCP) {
- cf_log_err_cs(cs,
- "TLS transport is not available for UDP sockets.");
- return -1;
- }
-
if (tls) {
/*
- * FIXME: Make this better.
+ * Don't allow TLS configurations for UDP sockets.
*/
- if (listen_port == 0) listen_port = 2083;
+ if (sock->proto != IPPROTO_TCP) {
+ cf_log_err_cs(cs,
+ "TLS transport is not available for UDP sockets");
+ return -1;
+ }
+
+ /*
+ * If unset, set to default.
+ */
+ if (listen_port == 0) listen_port = PW_RADIUS_TLS_PORT;
this->tls = tls_server_conf_parse(tls);
if (!this->tls) {
}
#endif
- }
+ }
#else /* WITH_TLS */
/*
* Built without TLS. Disallow it.
*/
if (cf_section_sub_find(cs, "tls")) {
cf_log_err_cs(cs,
- "TLS transport is not available in this executable.");
+ "TLS transport is not available in this executable");
return -1;
}
#endif /* WITH_TLS */
*/
} else if (cf_section_sub_find(cs, "tls")) {
cf_log_err_cs(cs,
- "TLS transport is not available in this \"listen\" section.");
+ "TLS transport is not available in this \"listen\" section");
return -1;
}
+ /*
+ * Magical tuning methods!
+ */
+ subcs = cf_section_sub_find(cs, "performance");
+ if (subcs) {
+ rcode = cf_section_parse(subcs, this,
+ performance_config);
+ if (rcode < 0) return -1;
+
+ if (this->synchronous && sock->max_rate) {
+ WARN("Setting 'max_pps' is incompatible with 'synchronous'. Disabling 'max_pps'");
+ sock->max_rate = 0;
+ }
+
+ if (!this->synchronous && this->workers) {
+ WARN("Setting 'workers' requires 'synchronous'. Disabling 'workers'");
+ this->workers = 0;
+ }
+ }
+
+ subcs = cf_section_sub_find(cs, "limit");
+ if (subcs) {
+ rcode = cf_section_parse(subcs, sock,
+ limit_config);
+ if (rcode < 0) return -1;
+
+ if (sock->max_rate && ((sock->max_rate < 10) || (sock->max_rate > 1000000))) {
+ cf_log_err_cs(cs,
+ "Invalid value for \"max_pps\"");
+ return -1;
+ }
+
+#ifdef WITH_TCP
+ if ((sock->limit.idle_timeout > 0) && (sock->limit.idle_timeout < 5)) {
+ WARN("Setting idle_timeout to 5");
+ sock->limit.idle_timeout = 5;
+ }
+
+ if ((sock->limit.lifetime > 0) && (sock->limit.lifetime < 5)) {
+ WARN("Setting lifetime to 5");
+ sock->limit.lifetime = 5;
+ }
+
+ if ((sock->limit.lifetime > 0) && (sock->limit.idle_timeout > sock->limit.lifetime)) {
+ WARN("Setting idle_timeout to 0");
+ sock->limit.idle_timeout = 0;
+ }
+
+ /*
+ * Force no duplicate detection for TCP sockets.
+ */
+ if (sock->proto == IPPROTO_TCP) {
+ this->nodup = true;
+ }
+
+ } else {
+ sock->limit.max_connections = 60;
+ sock->limit.idle_timeout = 30;
+ sock->limit.lifetime = 0;
+#endif
+ }
+
sock->my_ipaddr = ipaddr;
sock->my_port = listen_port;
- sock->max_rate = max_pps;
#ifdef WITH_PROXY
if (check_config) {
+ /*
+ * Until there is a side effects free way of forwarding a
+ * request to another virtual server, this check is invalid,
+ * and should be left disabled.
+ */
+#if 0
if (home_server_find(&sock->my_ipaddr, sock->my_port, sock->proto)) {
char buffer[128];
-
- DEBUGE("We have been asked to listen on %s port %d, which is also listed as a home server. This can create a proxy loop.",
- ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)),
- sock->my_port);
+
+ ERROR("We have been asked to listen on %s port %d, which is also listed as a "
+ "home server. This can create a proxy loop",
+ ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)), sock->my_port);
return -1;
}
-
+#endif
return 0; /* don't do anything */
}
#endif
* else don't.
*/
if (cf_pair_find(cs, "interface")) {
- const char *value;
+ char const *value;
CONF_PAIR *cp = cf_pair_find(cs, "interface");
rad_assert(cp != NULL);
if (cf_pair_find(cs, "broadcast")) {
#ifndef SO_BROADCAST
cf_log_err_cs(cs,
- "System does not support broadcast sockets. Delete this line from the configuration file.");
+ "System does not support broadcast sockets. Delete this line from the configuration file");
return -1;
#else
- const char *value;
+ char const *value;
CONF_PAIR *cp = cf_pair_find(cs, "broadcast");
if (this->type != RAD_LISTEN_DHCP) {
cf_log_err_cp(cp,
- "Broadcast can only be set for DHCP listeners. Delete this line from the configuration file.");
+ "Broadcast can only be set for DHCP listeners. Delete this line from the configuration file");
return -1;
}
-
+
rad_assert(cp != NULL);
value = cf_pair_value(cp);
if (!value) {
*/
if (this->type == RAD_LISTEN_PROXY) return 0;
#endif
-
+
/*
* The more specific configurations are preferred to more
* generic ones.
*/
client_cs = NULL;
parentcs = cf_top_section(cs);
- rcode = cf_item_parse(cs, "clients", PW_TYPE_STRING_PTR,
- §ion_name, NULL);
+ rcode = cf_item_parse(cs, "clients", FR_ITEM_POINTER(PW_TYPE_STRING, §ion_name), NULL);
if (rcode < 0) return -1; /* bad string */
if (rcode == 0) {
/*
* Explicit list given: use it.
*/
- client_cs = cf_section_sub_find_name2(parentcs,
- "clients",
- section_name);
+ client_cs = cf_section_sub_find_name2(parentcs, "clients", section_name);
if (!client_cs) {
client_cs = cf_section_find(section_name);
}
*/
if (!client_cs) client_cs = parentcs;
- sock->clients = clients_parse_section(client_cs);
+#ifdef WITH_TLS
+ sock->clients = clients_parse_section(client_cs, (this->tls != NULL));
+#else
+ sock->clients = clients_parse_section(client_cs, false);
+#endif
if (!sock->clients) {
cf_log_err_cs(cs,
"Failed to load clients for this listen section");
rad_assert(request->listener == listener);
rad_assert(listener->send == auth_socket_send);
+ if (request->reply->code == 0) return 0;
+
#ifdef WITH_UDPFROMTO
/*
* Overwrite the src ip address on the outbound packet
request->reply->src_ipaddr = request->client->src_ipaddr;
}
#endif
-
+
if (rad_send(request->reply, request->packet,
request->client->secret) < 0) {
- radlog_request(L_ERR, 0, request, "Failed sending reply: %s",
+ RERROR("Failed sending reply: %s",
fr_strerror());
return -1;
}
request->reply->src_ipaddr = request->client->src_ipaddr;
}
#endif
-
+
if (rad_send(request->reply, request->packet,
request->client->secret) < 0) {
- radlog_request(L_ERR, 0, request, "Failed sending reply: %s",
+ RERROR("Failed sending reply: %s",
fr_strerror());
return -1;
}
if (rad_send(request->proxy, NULL,
request->home_server->secret) < 0) {
- radlog_request(L_ERR, 0, request, "Failed sending proxied request: %s",
+ RERROR("Failed sending proxied request: %s",
fr_strerror());
return -1;
}
static int stats_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
- int code, src_port;
+ int code;
+ uint16_t src_port;
RADIUS_PACKET *packet;
RADCLIENT *client = NULL;
fr_ipaddr_t src_ipaddr;
/*
* We only understand Status-Server on this socket.
*/
- if (code != PW_STATUS_SERVER) {
+ if (code != PW_CODE_STATUS_SERVER) {
DEBUG("Ignoring packet code %d sent to Status-Server port",
code);
rad_recv_discard(listener->fd);
static int auth_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
- int code, src_port;
+ int code;
+ uint16_t src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
RADCLIENT *client = NULL;
* Some sanity checks, based on the packet code.
*/
switch(code) {
- case PW_AUTHENTICATION_REQUEST:
+ case PW_CODE_ACCESS_REQUEST:
fun = rad_authenticate;
break;
- case PW_STATUS_SERVER:
- if (!mainconfig.status_server) {
+ case PW_CODE_STATUS_SERVER:
+ if (!main_config.status_server) {
rad_recv_discard(listener->fd);
FR_STATS_INC(auth, total_unknown_types);
- DEBUGW("Ignoring Status-Server request due to security configuration");
+ WARN("Ignoring Status-Server request due to security configuration");
return 0;
}
fun = rad_status_server;
return 0;
}
+#ifdef __APPLE__
+#ifdef WITH_UDPFROMTO
+ /*
+ * This is a NICE Mac OSX bug. Create an interface with
+ * two IP address, and then configure one listener for
+ * each IP address. Send thousands of packets to one
+ * address, and some will show up on the OTHER socket.
+ *
+ * This hack works ONLY if the clients are global. If
+ * each listener has the same client IP, but with
+ * different secrets, then it will fail the rad_recv()
+ * check above, and there's nothing you can do.
+ */
+ {
+ listen_socket_t *sock = listener->data;
+ rad_listen_t *other;
+
+ other = listener_find_byipaddr(&packet->dst_ipaddr,
+ packet->dst_port, sock->proto);
+ if (other) listener = other;
+ }
+#endif
+#endif
+
+
if (!request_receive(listener, packet, client, fun)) {
FR_STATS_INC(auth, total_packets_dropped);
rad_free(&packet);
static int acct_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
- int code, src_port;
+ int code;
+ uint16_t src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
RADCLIENT *client = NULL;
* Some sanity checks, based on the packet code.
*/
switch(code) {
- case PW_ACCOUNTING_REQUEST:
+ case PW_CODE_ACCOUNTING_REQUEST:
fun = rad_accounting;
break;
- case PW_STATUS_SERVER:
- if (!mainconfig.status_server) {
+ case PW_CODE_STATUS_SERVER:
+ if (!main_config.status_server) {
rad_recv_discard(listener->fd);
FR_STATS_INC(acct, total_unknown_types);
- DEBUGW("Ignoring Status-Server request due to security configuration");
+ WARN("Ignoring Status-Server request due to security configuration");
return 0;
}
fun = rad_status_server;
packet = rad_recv(listener->fd, 0);
if (!packet) {
FR_STATS_INC(acct, total_malformed_requests);
- radlog(L_ERR, "%s", fr_strerror());
+ ERROR("%s", fr_strerror());
return 0;
}
vp = pairfind(request->config_items, PW_HOME_SERVER_POOL, 0, TAG_ANY);
if (!vp) return 0;
-
+
if (!home_pool_byname(vp->vp_strvalue, HOME_TYPE_COA)) {
- RDEBUG2E("Cannot proxy to unknown pool %s",
+ REDEBUG2("Cannot proxy to unknown pool %s",
vp->vp_strvalue);
return 0;
}
* Get the correct response
*/
switch (request->packet->code) {
- case PW_COA_REQUEST:
- ack = PW_COA_ACK;
- nak = PW_COA_NAK;
+ case PW_CODE_COA_REQUEST:
+ ack = PW_CODE_COA_ACK;
+ nak = PW_CODE_COA_NAK;
break;
- case PW_DISCONNECT_REQUEST:
- ack = PW_DISCONNECT_ACK;
- nak = PW_DISCONNECT_NAK;
+ case PW_CODE_DISCONNECT_REQUEST:
+ ack = PW_CODE_DISCONNECT_ACK;
+ nak = PW_CODE_DISCONNECT_NAK;
break;
default: /* shouldn't happen */
* have a State attribute in it.
*/
vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0, TAG_ANY);
- if (request->packet->code == PW_COA_REQUEST) {
+ if (request->packet->code == PW_CODE_COA_REQUEST) {
if (vp && (vp->vp_integer == 17)) {
vp = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
if (!vp || (vp->length == 0)) {
- RDEBUGE("CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
- request->reply->code = PW_COA_NAK;
+ REDEBUG("CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
+ request->reply->code = PW_CODE_COA_NAK;
return RLM_MODULE_FAIL;
}
}
/*
* RFC 5176, Section 3.2.
*/
- RDEBUGE("Disconnect-Request MUST NOT contain a Service-Type attribute");
- request->reply->code = PW_DISCONNECT_NAK;
+ REDEBUG("Disconnect-Request MUST NOT contain a Service-Type attribute");
+ request->reply->code = PW_CODE_DISCONNECT_NAK;
return RLM_MODULE_FAIL;
}
default:
request->reply->code = nak;
break;
-
+
case RLM_MODULE_HANDLED:
return rcode;
-
+
case RLM_MODULE_NOOP:
case RLM_MODULE_NOTFOUND:
case RLM_MODULE_OK:
*/
request->reply->code = nak;
break;
-
+
case RLM_MODULE_HANDLED:
return rcode;
-
+
case RLM_MODULE_NOOP:
case RLM_MODULE_NOTFOUND:
case RLM_MODULE_OK:
static int coa_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
- int code, src_port;
+ int code;
+ uint16_t src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
RADCLIENT *client = NULL;
* Some sanity checks, based on the packet code.
*/
switch(code) {
- case PW_COA_REQUEST:
+ case PW_CODE_COA_REQUEST:
FR_STATS_INC(coa, total_requests);
fun = rad_coa_recv;
break;
- case PW_DISCONNECT_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
FR_STATS_INC(dsc, total_requests);
fun = rad_coa_recv;
break;
packet = rad_recv(listener->fd, 0);
if (!packet) {
- radlog(L_ERR, "%s", fr_strerror());
+ ERROR("%s", fr_strerror());
return 0;
}
* FIXME: Client MIB updates?
*/
switch(packet->code) {
- case PW_AUTHENTICATION_ACK:
- case PW_ACCESS_CHALLENGE:
- case PW_AUTHENTICATION_REJECT:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_REJECT:
break;
#ifdef WITH_ACCOUNTING
- case PW_ACCOUNTING_RESPONSE:
+ case PW_CODE_ACCOUNTING_RESPONSE:
break;
#endif
#ifdef WITH_COA
- case PW_DISCONNECT_ACK:
- case PW_DISCONNECT_NAK:
- case PW_COA_ACK:
- case PW_COA_NAK:
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ case PW_CODE_COA_ACK:
+ case PW_CODE_COA_NAK:
break;
#endif
/*
* FIXME: Update MIB for packet types?
*/
- radlog(L_ERR, "Invalid packet code %d sent to a proxy port "
+ ERROR("Invalid packet code %d sent to a proxy port "
"from home server %s port %d - ID %d : IGNORED",
packet->code,
ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
listen_socket_t *sock = listener->data;
char buffer[128];
+ if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
+
packet = fr_tcp_recv(listener->fd, 0);
if (!packet) {
- listener->status = RAD_LISTEN_STATUS_REMOVE_FD;
- event_new_fd(listener);
+ listener->status = RAD_LISTEN_STATUS_EOL;
+ radius_update_listener(listener);
return 0;
}
* FIXME: Client MIB updates?
*/
switch(packet->code) {
- case PW_AUTHENTICATION_ACK:
- case PW_ACCESS_CHALLENGE:
- case PW_AUTHENTICATION_REJECT:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_REJECT:
break;
#ifdef WITH_ACCOUNTING
- case PW_ACCOUNTING_RESPONSE:
+ case PW_CODE_ACCOUNTING_RESPONSE:
break;
#endif
/*
* FIXME: Update MIB for packet types?
*/
- radlog(L_ERR, "Invalid packet code %d sent to a proxy port "
+ ERROR("Invalid packet code %d sent to a proxy port "
"from home server %s port %d - ID %d : IGNORED",
packet->code,
ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
if (rad_encode(request->reply, request->packet,
request->client->secret) < 0) {
- radlog_request(L_ERR, 0, request, "Failed encoding packet: %s",
+ RERROR("Failed encoding packet: %s",
fr_strerror());
return -1;
}
if (rad_sign(request->reply, request->packet,
request->client->secret) < 0) {
- radlog_request(L_ERR, 0, request, "Failed signing packet: %s",
+ RERROR("Failed signing packet: %s",
fr_strerror());
return -1;
}
static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
{
if (rad_encode(request->proxy, NULL, request->home_server->secret) < 0) {
- radlog_request(L_ERR, 0, request, "Failed encoding proxied packet: %s",
+ RERROR("Failed encoding proxied packet: %s",
fr_strerror());
return -1;
}
if (rad_sign(request->proxy, NULL, request->home_server->secret) < 0) {
- radlog_request(L_ERR, 0, request, "Failed signing proxied packet: %s",
+ RERROR("Failed signing proxied packet: %s",
fr_strerror());
return -1;
}
}
#endif
-#include "dhcpd.c"
-
#include "command.c"
/*
/* authentication */
{ RLM_MODULE_INIT, "auth", sizeof(listen_socket_t), NULL,
- common_socket_parse, NULL,
+ common_socket_parse, common_socket_free,
auth_socket_recv, auth_socket_send,
common_socket_print, client_socket_encode, client_socket_decode },
#ifdef WITH_ACCOUNTING
/* accounting */
{ RLM_MODULE_INIT, "acct", sizeof(listen_socket_t), NULL,
- common_socket_parse, NULL,
+ common_socket_parse, common_socket_free,
acct_socket_recv, acct_socket_send,
common_socket_print, client_socket_encode, client_socket_decode},
#endif
#ifdef WITH_DHCP
/* dhcp query protocol */
- { RLM_MODULE_INIT, "dhcp", sizeof(dhcp_socket_t), NULL,
- dhcp_socket_parse, NULL,
- dhcp_socket_recv, dhcp_socket_send,
- common_socket_print, dhcp_socket_encode, dhcp_socket_decode },
+ { 0, "dhcp", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
#endif
#ifdef WITH_COMMAND_SOCKET
#define proto_for_port "udp"
#define sock_type SOCK_DGRAM
#else
- const char *proto_for_port = "udp";
+ char const *proto_for_port = "udp";
int sock_type = SOCK_DGRAM;
-
+
if (sock->proto == IPPROTO_TCP) {
#ifdef WITH_VMPS
if (this->type == RAD_LISTEN_VQP) {
- radlog(L_ERR, "VQP does not support TCP transport");
+ ERROR("VQP does not support TCP transport");
return -1;
}
#endif
proto_for_port = "tcp";
- sock_type = SOCK_STREAM;
+ sock_type = SOCK_STREAM;
}
#endif
#endif
default:
- DEBUGW("Internal sanity check failed in binding to socket. Ignoring problem.");
+ WARN("Internal sanity check failed in binding to socket. Ignoring problem");
return -1;
}
}
this->print(this, buffer, sizeof(buffer));
- radlog(L_ERR, "Failed opening %s: %s", buffer, strerror(errno));
+ ERROR("Failed opening %s: %s", buffer, fr_syserror(errno));
return -1;
}
if (rcode >= 0) {
if (fcntl(this->fd, F_SETFD, rcode | FD_CLOEXEC) < 0) {
close(this->fd);
- radlog(L_ERR, "Failed setting close on exec: %s", strerror(errno));
+ ERROR("Failed setting close on exec: %s", fr_syserror(errno));
return -1;
}
}
#endif
-
+
/*
* Bind to a device BEFORE touching IP addresses.
*/
fr_suid_down();
if (rcode < 0) {
close(this->fd);
- radlog(L_ERR, "Failed binding to interface %s: %s",
- sock->interface, strerror(errno));
+ ERROR("Failed binding to interface %s: %s",
+ sock->interface, fr_syserror(errno));
return -1;
} /* else it worked. */
#else
sock->my_ipaddr.scope = if_nametoindex(sock->interface);
if (sock->my_ipaddr.scope == 0) {
close(this->fd);
- radlog(L_ERR, "Failed finding interface %s: %s",
- sock->interface, strerror(errno));
+ ERROR("Failed finding interface %s: %s",
+ sock->interface, fr_syserror(errno));
return -1;
}
} /* else scope was defined: we're OK. */
*/
{
close(this->fd);
- radlog(L_ERR, "Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface);
+ ERROR("Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface);
return -1;
}
#endif
if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
close(this->fd);
- radlog(L_ERR, "Failed to reuse address: %s", strerror(errno));
+ ERROR("Failed to reuse address: %s", fr_syserror(errno));
return -1;
}
}
* Initialize udpfromto for all sockets.
*/
if (udpfromto_init(this->fd) != 0) {
- radlog(L_ERR, "Failed initializing udpfromto: %s",
- strerror(errno));
+ ERROR("Failed initializing udpfromto: %s",
+ fr_syserror(errno));
close(this->fd);
return -1;
}
close(this->fd);
return -1;
}
-
+
#ifdef HAVE_STRUCT_SOCKADDR_IN6
if (sock->my_ipaddr.af == AF_INET6) {
/*
* design a little simpler.
*/
#ifdef IPV6_V6ONLY
-
+
if (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr)) {
int on = 1;
-
+
if (setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&on, sizeof(on)) < 0) {
- radlog(L_ERR, "Failed setting socket to IPv6 "
- "only: %s", strerror(errno));
-
- close(this->fd);
+ ERROR("Failed setting socket to IPv6 "
+ "only: %s", fr_syserror(errno));
+
+ close(this->fd);
return -1;
}
}
if (sock->my_ipaddr.af == AF_INET) {
UNUSED int flag;
-
+
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
/*
* Disable PMTU discovery. On Linux, this
flag = IP_PMTUDISC_DONT;
if (setsockopt(this->fd, IPPROTO_IP, IP_MTU_DISCOVER,
&flag, sizeof(flag)) < 0) {
- radlog(L_ERR, "Failed disabling PMTU discovery: %s",
- strerror(errno));
-
+ ERROR("Failed disabling PMTU discovery: %s",
+ fr_syserror(errno));
+
close(this->fd);
- return -1;
+ return -1;
}
#endif
flag = 0;
if (setsockopt(this->fd, IPPROTO_IP, IP_DONTFRAG,
&flag, sizeof(flag)) < 0) {
- radlog(L_ERR, "Failed setting don't fragment flag: %s",
- strerror(errno));
-
+ ERROR("Failed setting don't fragment flag: %s",
+ fr_syserror(errno));
+
close(this->fd);
return -1;
}
#ifdef SO_BROADCAST
if (sock->broadcast) {
int on = 1;
-
+
if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
- radlog(L_ERR, "Can't set broadcast option: %s",
- strerror(errno));
+ ERROR("Can't set broadcast option: %s",
+ fr_syserror(errno));
return -1;
}
}
* May be binding to priviledged ports.
*/
if (sock->my_port != 0) {
-#ifdef SO_REUSEADDR
- int on = 1;
-
- if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
- radlog(L_ERR, "Can't set re-use address option: %s",
- strerror(errno));
- return -1;
- }
-#endif
-
fr_suid_up();
rcode = bind(this->fd, (struct sockaddr *) &salocal, salen);
fr_suid_down();
if (rcode < 0) {
char buffer[256];
close(this->fd);
-
+
this->print(this, buffer, sizeof(buffer));
- radlog(L_ERR, "Failed binding to %s: %s\n",
- buffer, strerror(errno));
+ ERROR("Failed binding to %s: %s\n",
+ buffer, fr_syserror(errno));
return -1;
}
-
+
/*
* FreeBSD jail issues. We bind to 0.0.0.0, but the
* kernel instead binds us to a 1.2.3.4. If this
{
struct sockaddr_storage src;
socklen_t sizeof_src = sizeof(src);
-
+
memset(&src, 0, sizeof_src);
if (getsockname(this->fd, (struct sockaddr *) &src,
&sizeof_src) < 0) {
- radlog(L_ERR, "Failed getting socket name: %s",
- strerror(errno));
+ ERROR("Failed getting socket name: %s",
+ fr_syserror(errno));
return -1;
}
-
+
if (!fr_sockaddr2ipaddr(&src, sizeof_src,
&sock->my_ipaddr, &sock->my_port)) {
- radlog(L_ERR, "Socket has unsupported address family");
+ ERROR("Socket has unsupported address family");
return -1;
}
}
#ifdef WITH_TCP
if (sock->proto == IPPROTO_TCP) {
+ /*
+ * If there are hard-coded worker threads, OR
+ * it's a TLS connection, it's blocking.
+ *
+ * Otherwise, they're non-blocking.
+ */
+ if (!this->workers
+#ifdef WITH_PROXY
+#ifdef WITH_TLS
+ && (this->type == RAD_LISTEN_PROXY) && !this->tls
+#endif
+#endif
+ ) {
+ if (fr_nonblock(this->fd) < 0) {
+ close(this->fd);
+ ERROR("Failed setting non-blocking on socket: %s",
+ fr_syserror(errno));
+ return -1;
+ }
+ }
+
+ /*
+ * Allow a backlog of 8 listeners, but only for incoming interfaces.
+ */
+#ifdef WITH_PROXY
+ if (this->type != RAD_LISTEN_PROXY)
+#endif
if (listen(this->fd, 8) < 0) {
close(this->fd);
- radlog(L_ERR, "Failed in listen(): %s", strerror(errno));
+ ERROR("Failed in listen(): %s", fr_syserror(errno));
return -1;
}
- } else
+ }
#endif
- if (fr_nonblock(this->fd) < 0) {
- close(this->fd);
- radlog(L_ERR, "Failed setting non-blocking on socket: %s",
- strerror(errno));
- return -1;
- }
-
/*
* Mostly for proxy sockets.
*/
}
-static int listener_free(void *ctx)
+static int _listener_free(rad_listen_t *this)
{
- rad_listen_t *this;
-
- this = talloc_get_type_abort(ctx, rad_listen_t);
-
/*
* Other code may have eaten the FD.
*/
) {
listen_socket_t *sock = this->data;
+ rad_free(&sock->packet);
+
+ rad_assert(sock->ev == NULL);
+
#ifdef WITH_TLS
- if (sock->request) {
+ /*
+ * Note that we do NOT free this->tls, as the
+ * pointer is parented by its CONF_SECTION. It
+ * may be used by multiple listeners.
+ */
+ if (this->tls) {
+ TALLOC_FREE(sock->ssn);
+ TALLOC_FREE(sock->request);
+#ifdef HAVE_PTHREAD_H
pthread_mutex_destroy(&(sock->mutex));
- request_free(&sock->request);
- sock->packet = NULL;
-
- if (sock->ssn) session_free(sock->ssn);
- request_free(&sock->request);
- } else
#endif
- rad_free(&sock->packet);
+ }
+#endif /* WITH_TLS */
}
#endif /* WITH_TCP */
this->encode = master_listen[this->type].encode;
this->decode = master_listen[this->type].decode;
- talloc_set_destructor((void *) this, listener_free);
+ talloc_set_destructor(this, _listener_free);
this->data = talloc_zero_array(this, uint8_t, master_listen[this->type].inst_size);
* Not thread-safe, but all calls to it are protected by the
* proxy mutex in event.c
*/
-rad_listen_t *proxy_new_listener(home_server *home, int src_port)
+rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port)
{
+ time_t now;
rad_listen_t *this;
listen_socket_t *sock;
char buffer[256];
if (!home) return NULL;
+ rad_assert(home->server == NULL); /* we only open real sockets */
+
if ((home->limit.max_connections > 0) &&
(home->limit.num_connections >= home->limit.max_connections)) {
- DEBUGW("Home server has too many open connections (%d)",
- home->limit.max_connections);
+ RATE_LIMIT(INFO("Home server %s has too many open connections (%d)",
+ home->name, home->limit.max_connections));
+ return NULL;
+ }
+
+ now = time(NULL);
+ if (home->last_failed_open == now) {
+ WARN("Suppressing attempt to open socket to 'down' home server");
return NULL;
}
- this = listen_alloc(mainconfig.config, RAD_LISTEN_PROXY);
+ this = listen_alloc(main_config.config, RAD_LISTEN_PROXY);
sock = this->data;
sock->other_ipaddr = home->ipaddr;
sock->my_port = src_port;
sock->proto = home->proto;
+ /*
+ * For error messages.
+ */
+ this->print(this, buffer, sizeof(buffer));
+
if (debug_flag >= 2) {
- this->print(this, buffer, sizeof(buffer));
- DEBUG("Opening new %s", buffer);
+ DEBUG("Opening new proxy socket '%s'", buffer);
}
#ifdef WITH_TCP
- sock->opened = sock->last_packet = time(NULL);
+ sock->opened = sock->last_packet = now;
if (home->proto == IPPROTO_TCP) {
this->recv = proxy_socket_tcp_recv;
*/
this->fd = fr_tcp_client_socket(&home->src_ipaddr,
&home->ipaddr, home->port);
-#ifdef WITH_TLS
- if (home->tls) {
- DEBUG("Trying SSL to port %d\n", home->port);
- sock->ssn = tls_new_client_session(home->tls, this->fd);
- if (!sock->ssn) {
- listen_free(&this);
- return NULL;
- }
-
- this->recv = proxy_tls_recv;
- this->send = proxy_tls_send;
- }
-#endif
} else
#endif
this->fd = fr_socket(&home->src_ipaddr, src_port);
if (this->fd < 0) {
this->print(this, buffer,sizeof(buffer));
- DEBUG("Failed opening client socket ::%s:: : %s",
+ ERROR("Failed opening proxy socket '%s' : %s",
buffer, fr_strerror());
+ home->last_failed_open = now;
listen_free(&this);
return NULL;
}
+
+#ifdef WITH_TCP
+#ifdef WITH_TLS
+ if ((home->proto == IPPROTO_TCP) && home->tls) {
+ DEBUG("Trying SSL to port %d\n", home->port);
+ sock->ssn = tls_new_client_session(home->tls, this->fd);
+ if (!sock->ssn) {
+ ERROR("Failed starting SSL to '%s'", buffer);
+ home->last_failed_open = now;
+ listen_free(&this);
+ return NULL;
+ }
+
+ this->recv = proxy_tls_recv;
+ this->send = proxy_tls_send;
+ }
+#endif
+#endif
/*
* Figure out which port we were bound to.
*/
if (sock->my_port == 0) {
struct sockaddr_storage src;
socklen_t sizeof_src = sizeof(src);
-
+
memset(&src, 0, sizeof_src);
if (getsockname(this->fd, (struct sockaddr *) &src,
&sizeof_src) < 0) {
- radlog(L_ERR, "Failed getting socket name: %s",
- strerror(errno));
+ ERROR("Failed getting socket name for '%s': %s",
+ buffer, fr_syserror(errno));
+ home->last_failed_open = now;
listen_free(&this);
return NULL;
}
-
+
if (!fr_sockaddr2ipaddr(&src, sizeof_src,
&sock->my_ipaddr, &sock->my_port)) {
- radlog(L_ERR, "Socket has unsupported address family");
+ ERROR("Socket has unsupported address family for '%s'", buffer);
+ home->last_failed_open = now;
listen_free(&this);
return NULL;
}
}
+ home->limit.num_connections++;
+
return this;
}
#endif
-
static const FR_NAME_NUMBER listen_compare[] = {
#ifdef WITH_STATS
{ "status", RAD_LISTEN_NONE },
{ "auth", RAD_LISTEN_AUTH },
#ifdef WITH_ACCOUNTING
{ "acct", RAD_LISTEN_ACCT },
+ { "auth+acct", RAD_LISTEN_AUTH },
#endif
#ifdef WITH_DETAIL
{ "detail", RAD_LISTEN_DETAIL },
{ NULL, 0 },
};
+static int _free_proto_handle(lt_dlhandle *handle)
+{
+ dlclose(*handle);
+ return 0;
+}
-static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server)
+static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
{
int type, rcode;
- char *listen_type;
+ char const *listen_type;
rad_listen_t *this;
CONF_PAIR *cp;
- const char *value;
+ char const *value;
lt_dlhandle handle;
char buffer[32];
handle = lt_dlopenext(buffer);
if (handle) {
fr_protocol_t *proto;
+ lt_dlhandle *marker;
proto = dlsym(handle, buffer);
if (!proto) {
memcpy(&master_listen[type], proto, sizeof(*proto));
/*
- * And throw away the handle.
- * @todo: fix it later
+ * Ensure handle gets closed if config section gets freed
*/
- }
+ marker = talloc(cs, lt_dlhandle);
+ *marker = handle;
+ talloc_set_destructor(marker, _free_proto_handle);
- if (master_listen[type].magic != RLM_MODULE_INIT) {
- radlog(L_ERR, "Failed to load protocol '%s' due to internal sanity check problem",
- master_listen[type].name);
- return NULL;
+ if (master_listen[type].magic != RLM_MODULE_INIT) {
+ ERROR("Failed to load protocol '%s' due to internal sanity check problem",
+ master_listen[type].name);
+ return NULL;
+ }
}
cf_log_info(cs, "listen {");
listen_type = NULL;
- rcode = cf_item_parse(cs, "type", PW_TYPE_STRING_PTR,
- &listen_type, "");
+ rcode = cf_item_parse(cs, "type", FR_ITEM_POINTER(PW_TYPE_STRING, &listen_type), "");
if (rcode < 0) return NULL;
if (rcode == 1) {
cf_log_err_cs(cs,
* refer to a server.
*/
if (!server) {
- rcode = cf_item_parse(cs, "virtual_server", PW_TYPE_STRING_PTR,
- &server, NULL);
- if (rcode == 1) { /* compatiblity with 2.0-pre */
- rcode = cf_item_parse(cs, "server", PW_TYPE_STRING_PTR,
- &server, NULL);
- }
+ rcode = cf_item_parse(cs, "virtual_server", FR_ITEM_POINTER(PW_TYPE_STRING, &server), NULL);
if (rcode < 0) return NULL;
}
* This isn't allowed right now.
*/
else if (type == RAD_LISTEN_PROXY) {
- radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section");
+ ERROR("Error: listen type \"proxy\" Cannot appear in a virtual server section");
return NULL;
}
#endif
this->server = server;
this->fd = -1;
+#ifdef WITH_TCP
+ /*
+ * Special-case '+' for "auth+acct".
+ */
+ if (strchr(listen_type, '+') != NULL) {
+ this->dual = true;
+ }
+#endif
+
/*
* Call per-type parser.
*/
return this;
}
-#ifdef WITH_PROXY
-static int is_loopback(const fr_ipaddr_t *ipaddr)
+#ifdef HAVE_PTHREAD_H
+/*
+ * A child thread which does NOTHING other than read and process
+ * packets.
+ */
+static void *recv_thread(void *arg)
{
- /*
- * We shouldn't proxy on loopback.
- */
- if ((ipaddr->af == AF_INET) &&
- (ipaddr->ipaddr.ip4addr.s_addr == htonl(INADDR_LOOPBACK))) {
- return 1;
- }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- if ((ipaddr->af == AF_INET6) &&
- (IN6_IS_ADDR_LINKLOCAL(&ipaddr->ipaddr.ip6addr))) {
- return 1;
+ rad_listen_t *this = arg;
+
+ while (1) {
+ this->recv(this);
+ DEBUG("%p", &this);
}
-#endif
- return 0;
+ return NULL;
}
#endif
+
/*
* Generate a list of listeners. Takes an input list of
* listeners, too, so we don't close sockets with waiting packets.
*/
int listen_init(CONF_SECTION *config, rad_listen_t **head,
#ifdef WITH_TLS
- int spawn_flag
+ bool spawn_flag
#else
- UNUSED int spawn_flag
+ UNUSED bool spawn_flag
#endif
- )
+ )
{
- int override = FALSE;
+ bool override = false;
CONF_SECTION *cs = NULL;
rad_listen_t **last;
rad_listen_t *this;
fr_ipaddr_t server_ipaddr;
- int auth_port = 0;
+ uint16_t auth_port = 0;
#ifdef WITH_PROXY
- int defined_proxy = 0;
+ bool defined_proxy = false;
#endif
/*
*/
rad_assert(head && (*head == NULL));
+ memset(&server_ipaddr, 0, sizeof(server_ipaddr));
+
last = head;
server_ipaddr.af = AF_UNSPEC;
*
* FIXME: If argv[0] == "vmpsd", then don't listen on auth/acct!
*/
- if (mainconfig.port >= 0) {
- auth_port = mainconfig.port;
+ if (main_config.port > 0) {
+ auth_port = main_config.port;
/*
* -p X but no -i Y on the command-line.
*/
- if ((mainconfig.port > 0) &&
- (mainconfig.myip.af == AF_UNSPEC)) {
- radlog(L_ERR, "The command-line says \"-p %d\", but there is no associated IP address to use",
- mainconfig.port);
+ if (main_config.myip.af == AF_UNSPEC) {
+ ERROR("The command-line says \"-p %d\", but there is no associated IP address to use",
+ main_config.port);
return -1;
}
}
* If the IP address was configured on the command-line,
* use that as the "bind_address"
*/
- if (mainconfig.myip.af != AF_UNSPEC) {
+ if (main_config.myip.af != AF_UNSPEC) {
listen_socket_t *sock;
- memcpy(&server_ipaddr, &mainconfig.myip,
+ memcpy(&server_ipaddr, &main_config.myip,
sizeof(server_ipaddr));
- override = TRUE;
+ override = true;
#ifdef WITH_VMPS
if (strcmp(progname, "vmpsd") == 0) {
sock->my_ipaddr = server_ipaddr;
sock->my_port = auth_port;
- sock->clients = clients_parse_section(config);
+ sock->clients = clients_parse_section(config, false);
if (!sock->clients) {
cf_log_err_cs(config,
"Failed to find any clients for this listen section");
if (listen_bind(this) < 0) {
listen_free(head);
- radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->my_port);
+ ERROR("There appears to be another RADIUS server running on the authentication port %d", sock->my_port);
listen_free(&this);
return -1;
}
auth_port = sock->my_port; /* may have been updated in listen_bind */
if (override) {
cs = cf_section_sub_find_name2(config, "server",
- mainconfig.name);
- if (cs) this->server = mainconfig.name;
+ main_config.name);
+ if (cs) this->server = main_config.name;
}
*last = this;
sock->my_ipaddr = server_ipaddr;
sock->my_port = auth_port + 1;
- sock->clients = clients_parse_section(config);
+ sock->clients = clients_parse_section(config, false);
if (!sock->clients) {
cf_log_err_cs(config,
"Failed to find any clients for this listen section");
if (listen_bind(this) < 0) {
listen_free(&this);
listen_free(head);
- radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->my_port);
+ ERROR("There appears to be another RADIUS server running on the accounting port %d", sock->my_port);
return -1;
}
if (override) {
cs = cf_section_sub_find_name2(config, "server",
- mainconfig.name);
- if (cs) this->server = mainconfig.name;
+ main_config.name);
+ if (cs) this->server = main_config.name;
}
*last = this;
* They specified an IP on the command-line, ignore
* all listen sections except the one in '-n'.
*/
- if (mainconfig.myip.af != AF_UNSPEC) {
+ if (main_config.myip.af != AF_UNSPEC) {
CONF_SECTION *subcs;
- const char *name2 = cf_section_name2(cs);
+ char const *name2 = cf_section_name2(cs);
cs = cf_section_sub_find_name2(config, "server",
- mainconfig.name);
+ main_config.name);
if (!cs) goto add_sockets;
/*
cs != NULL;
cs = cf_subsection_find_next(config, cs, "server")) {
CONF_SECTION *subcs;
- const char *name2 = cf_section_name2(cs);
-
+ char const *name2 = cf_section_name2(cs);
+
for (subcs = cf_subsection_find_next(cs, NULL, "listen");
subcs != NULL;
subcs = cf_subsection_find_next(cs, subcs, "listen")) {
listen_free(head);
return -1;
}
-
+
*last = this;
last = &(this->next);
} /* loop over "listen" directives in virtual servers */
* proxying is pointless.
*/
if (!*head) {
- radlog(L_ERR, "The server is not configured to listen on any ports. Cannot start.");
+ ERROR("The server is not configured to listen on any ports. Cannot start");
return -1;
}
for (this = *head; this != NULL; this = this->next) {
#ifdef WITH_PROXY
if (this->type == RAD_LISTEN_PROXY) {
- defined_proxy = 1;
+ defined_proxy = true;
}
#endif
#ifdef WITH_TLS
- if (!spawn_flag && this->tls) {
- cf_log_err_cs(this->cs, "Threading must be enabled for TLS sockets to function properly.");
- cf_log_err_cs(this->cs, "You probably need to do 'radiusd -fxx -l stdout' for debugging");
+ if (!check_config && !spawn_flag && this->tls) {
+ cf_log_err_cs(this->cs, "Threading must be enabled for TLS sockets to function properly");
+ cf_log_err_cs(this->cs, "You probably need to do '%s -fxx -l stdout' for debugging",
+ progname);
return -1;
}
#endif
- if (!check_config) event_new_fd(this);
+ if (!check_config) {
+ if (this->workers && !spawn_flag) {
+ WARN("Setting 'workers' requires 'synchronous'. Disabling 'workers'");
+ this->workers = 0;
+ }
+
+ if (this->workers) {
+#ifdef HAVE_PTHREAD_H
+ int rcode;
+ uint32_t i;
+ char buffer[256];
+
+ this->print(this, buffer, sizeof(buffer));
+
+ for (i = 0; i < this->workers; i++) {
+ pthread_t id;
+
+ /*
+ * FIXME: create detached?
+ */
+ rcode = pthread_create(&id, 0, recv_thread, this);
+ if (rcode != 0) {
+ ERROR("Thread create failed: %s",
+ fr_syserror(rcode));
+ fr_exit(1);
+ }
+
+ DEBUG("Thread %d for %s\n", i, buffer);
+ }
+#else
+ WARN("Setting 'workers' requires 'synchronous'. Disabling 'workers'");
+ this->workers = 0;
+#endif
+
+ } else {
+ radius_update_listener(this);
+ }
+
+ }
}
/*
* Otherwise, don't do anything.
*/
#ifdef WITH_PROXY
- if ((mainconfig.proxy_requests == TRUE) &&
+ if ((main_config.proxy_requests == true) &&
!check_config &&
(*head != NULL) && !defined_proxy) {
- listen_socket_t *sock = NULL;
- int port = 0;
- home_server home;
+ uint16_t port = 0;
+ home_server_t home;
memset(&home, 0, sizeof(home));
/*
- *
+ *
*/
home.proto = IPPROTO_UDP;
home.src_ipaddr = server_ipaddr;
-
- /*
- * Find the first authentication port,
- * and use it
- */
- for (this = *head; this != NULL; this = this->next) {
- if (this->type == RAD_LISTEN_AUTH) {
- sock = this->data;
-
- if (is_loopback(&sock->my_ipaddr)) continue;
-
- if (home.src_ipaddr.af == AF_UNSPEC) {
- home.src_ipaddr = sock->my_ipaddr;
- }
- port = sock->my_port + 2;
- break;
- }
-#ifdef WITH_ACCT
- if (this->type == RAD_LISTEN_ACCT) {
- sock = this->data;
-
- if (is_loopback(&sock->my_ipaddr)) continue;
-
- if (home.src_ipaddr.af == AF_UNSPEC) {
- home.src_ipaddr = sock->my_ipaddr;
- }
- port = sock->my_port + 1;
- break;
- }
-#endif
- }
+ port = 0;
/*
* Address is still unspecified, use IPv4.
return -1;
}
- if (!event_new_fd(this)) {
- listen_free(&this);
- listen_free(head);
- return -1;
- }
+ radius_update_listener(this);
}
#endif
*/
if (!*head) return -1;
- xlat_register("listen", xlat_listen, NULL);
+ xlat_register("listen", xlat_listen, NULL, NULL);
return 0;
}
}
#ifdef WITH_STATS
-RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
- int port)
+RADCLIENT_LIST *listener_find_client_list(fr_ipaddr_t const *ipaddr, uint16_t port)
{
rad_listen_t *this;
- for (this = mainconfig.listen; this != NULL; this = this->next) {
+ for (this = main_config.listen; this != NULL; this = this->next) {
listen_socket_t *sock;
if ((this->type != RAD_LISTEN_AUTH)
&& (this->type != RAD_LISTEN_ACCT)
#endif
) continue;
-
+
sock = this->data;
if ((sock->my_port == port) &&
}
#endif
-rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port, int proto)
+rad_listen_t *listener_find_byipaddr(fr_ipaddr_t const *ipaddr, uint16_t port, int proto)
{
rad_listen_t *this;
- for (this = mainconfig.listen; this != NULL; this = this->next) {
+ for (this = main_config.listen; this != NULL; this = this->next) {
listen_socket_t *sock;
sock = this->data;
/*
* Failed to find a specific one. Find INADDR_ANY
*/
- for (this = mainconfig.listen; this != NULL; this = this->next) {
+ for (this = main_config.listen; this != NULL; this = this->next) {
listen_socket_t *sock;
sock = this->data;