* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2005 The FreeRADIUS server project
+ * Copyright 2005,2006 The FreeRADIUS server project
* Copyright 2005 Alan DeKok <aland@ox.org>
*/
-#include <freeradius-devel/autoconf.h>
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include <stdlib.h>
-#include <string.h>
+#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>
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
+#include <freeradius-devel/vmps.h>
+#include <freeradius-devel/detail.h>
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#ifdef WITH_UDPFROMTO
+#include <freeradius-devel/udpfromto.h>
#endif
+#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
+#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
#endif
-#ifdef WITH_UDPFROMTO
-#include <freeradius-devel/udpfromto.h>
-#endif
-#include <fcntl.h>
+void print_packet(RADIUS_PACKET *packet)
+{
+ char src[256], dst[256];
-#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/conffile.h>
-#include <freeradius-devel/token.h>
+ ip_ntoh(&packet->src_ipaddr, src, sizeof(src));
+ ip_ntoh(&packet->dst_ipaddr, dst, sizeof(dst));
-#include <freeradius-devel/radius_snmp.h>
-#include <freeradius-devel/request_list.h>
+ fprintf(stderr, "ID %d: %s %d -> %s %d\n", packet->id,
+ src, packet->src_port, dst, packet->dst_port);
-static time_t start_time = 0;
+}
-/*
- * FIXME: Delete this crap!
- */
-extern time_t time_now;
/*
* We'll use this below.
*/
-typedef int (*rad_listen_parse_t)(const char *, int, const CONF_SECTION *, rad_listen_t *);
+typedef int (*rad_listen_parse_t)(CONF_SECTION *, rad_listen_t *);
typedef void (*rad_listen_free_t)(rad_listen_t *);
typedef struct rad_listen_master_t {
rad_listen_free_t free;
rad_listen_recv_t recv;
rad_listen_send_t send;
- rad_listen_update_t update;
rad_listen_print_t print;
+ rad_listen_encode_t encode;
+ rad_listen_decode_t decode;
} rad_listen_master_t;
-typedef struct listen_socket_t {
- /*
- * For normal sockets.
- */
- lrad_ipaddr_t ipaddr;
- int port;
- RADCLIENT_LIST *clients;
-} listen_socket_t;
-
-typedef struct listen_detail_t {
- const char *detail;
- VALUE_PAIR *vps;
- FILE *fp;
- int state;
- time_t timestamp;
- lrad_ipaddr_t client_ip;
- int max_outstanding;
- int *outstanding;
-} listen_detail_t;
-
+static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
/*
* Find a per-socket client.
*/
-static RADCLIENT *client_listener_find(const rad_listen_t *listener,
- const lrad_ipaddr_t *ipaddr)
+RADCLIENT *client_listener_find(const rad_listen_t *listener,
+ const fr_ipaddr_t *ipaddr, int src_port)
{
- const RADCLIENT_LIST *clients;
+#ifdef WITH_DYNAMIC_CLIENTS
+ int rcode;
+ REQUEST *request;
+ RADCLIENT *created;
+#endif
+ time_t now;
+ RADCLIENT *client;
+ RADCLIENT_LIST *clients;
+ listen_socket_t *sock;
rad_assert(listener != NULL);
rad_assert(ipaddr != NULL);
- rad_assert((listener->type == RAD_LISTEN_AUTH) ||
- (listener->type == RAD_LISTEN_ACCT));
-
- clients = ((listen_socket_t *)listener->data)->clients;
- if (!clients) clients = mainconfig.clients;
-
- rad_assert(clients != NULL);
-
- return client_find(clients, ipaddr);
-}
-
-static int listen_bind(rad_listen_t *this);
-
-/*
- * FIXME: have the detail reader use another config "exit when done",
- * so that it can be used as a one-off tool to update stuff.
- */
-
-/*
- * Process and reply to a server-status request.
- * Like rad_authenticate and rad_accounting this should
- * live in it's own file but it's so small we don't bother.
- */
-static int rad_status_server(REQUEST *request)
-{
- char reply_msg[64];
- time_t t;
- VALUE_PAIR *vp;
+ sock = listener->data;
+ clients = sock->clients;
/*
- * Reply with an ACK. We might want to add some more
- * interesting reply attributes, such as server uptime.
+ * This HAS to have been initialized previously.
*/
- t = request->timestamp - start_time;
- sprintf(reply_msg, "FreeRADIUS up %d day%s, %02d:%02d",
- (int)(t / 86400), (t / 86400) == 1 ? "" : "s",
- (int)((t / 3600) % 24), (int)(t / 60) % 60);
- request->reply->code = PW_AUTHENTICATION_ACK;
+ rad_assert(clients != NULL);
- vp = pairmake("Reply-Message", reply_msg, T_OP_SET);
- pairadd(&request->reply->vps, vp); /* don't need to check if !vp */
+ 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
- return 0;
-}
+ /*
+ * DoS attack quenching, but only in daemon mode.
+ * If they're running in debug mode, show them
+ * every packet.
+ */
+ if (debug_flag == 0) {
+ static time_t last_printed = 0;
-static int request_num_counter = 0;
+ now = time(NULL);
+ if (last_printed == now) return NULL;
+
+ last_printed = now;
+ }
-/*
- * Check for dups, etc. Common to Access-Request &&
- * Accounting-Request packets.
- */
-static int common_checks(rad_listen_t *listener,
- RADIUS_PACKET *packet, REQUEST **prequest,
- const RADCLIENT *client)
-{
- REQUEST *curreq;
- char buffer[128];
+ listener->print(listener, name, sizeof(name));
+
+ radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d"
+#ifdef WITH_TCP
+ " proto %s"
+#endif
+ , name, inet_ntop(ipaddr->af, &ipaddr->ipaddr,
+ buffer, sizeof(buffer)), src_port
+#ifdef WITH_TCP
+ , (sock->proto == IPPROTO_UDP) ? "udp" : "tcp"
+#endif
+ );
+ return NULL;
+ }
- rad_assert(listener->rl != NULL);
+#ifndef WITH_DYNAMIC_CLIENTS
+ return client; /* return the found client. */
+#else
/*
- * If there is no existing request of id, code, etc.,
- * then we can return, and let it be processed.
+ * No server defined, and it's not dynamic. Return it.
*/
- if ((curreq = rl_find(listener->rl, packet)) == NULL) {
- /*
- * Count the total number of requests, to see if
- * there are too many. If so, return with an
- * error.
- */
- if (mainconfig.max_requests) {
- /*
- * FIXME: This is now per-socket,
- * when it should really be global
- * to the server!
- */
- int request_count = rl_num_requests(listener->rl);
-
- /*
- * This is a new request. Let's see if
- * it makes us go over our configured
- * bounds.
- */
- if (request_count > mainconfig.max_requests) {
- radlog(L_ERR, "Dropping request (%d is too many): "
- "from client %s port %d - ID: %d", request_count,
- client->shortname,
- packet->src_port, packet->id);
- radlog(L_INFO, "WARNING: Please check the %s file.\n"
- "\tThe value for 'max_requests' is probably set too low.\n", mainconfig.radiusd_conf);
- return 0;
- } /* else there were a small number of requests */
- } /* else there was no configured limit for requests */
-
- /*
- * FIXME: Add checks for system load. If the
- * system is busy, start dropping requests...
- *
- * We can probably keep some statistics
- * ourselves... if there are more requests
- * coming in than we can handle, start dropping
- * some.
- */
+ if (!client->client_server && !client->dynamic) return client;
+ now = time(NULL);
+
/*
- * The current request isn't finished, which
- * means that the NAS sent us a new packet, while
- * we are still processing the old request.
+ * It's a dynamically generated client, check it.
*/
- } else if (!curreq->finished) {
+ if (client->dynamic && (src_port != 0)) {
/*
- * If the authentication vectors are identical,
- * then the NAS is re-transmitting it, trying to
- * kick us into responding to the request.
+ * Lives forever. Return it.
*/
- if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
- /*
- * It's not finished because the request
- * was proxied, but there was no reply
- * from the home server.
- *
- * This code will never get hit for
- * accounting packets, as they're always
- * updated, and never re-transmitted.
- */
- if (curreq->proxy && !curreq->proxy_reply) {
- DEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
- inet_ntop(curreq->proxy->dst_ipaddr.af,
- &curreq->proxy->dst_ipaddr.ipaddr,
- buffer, sizeof(buffer)), curreq->proxy->dst_port,
-
- curreq->proxy->id);
- listener->send(curreq->proxy_listener, curreq);
- return 0;
- } /* else the packet was not proxied */
-
- /*
- * Someone's still working on it, so we
- * ignore the duplicate request.
- */
- radlog(L_ERR, "Discarding duplicate request from "
- "client %s port %d - ID: %d due to unfinished request %d",
- client->shortname,
- packet->src_port, packet->id,
- curreq->number);
- return 0;
- } /* else the authentication vectors were different */
+ if (client->lifetime == 0) return client;
+
+ /*
+ * Rate-limit the deletion of known clients.
+ * This makes them last a little longer, but
+ * prevents the server from melting down if (say)
+ * 10k clients all expire at once.
+ */
+ if (now == client->last_new_client) return client;
/*
- * We're waiting for a proxy reply, but no one is
- * currently processing the request. We can
- * discard the old request, and ignore any reply
- * from the home server, because the NAS will
- * never care...
+ * It's not dead yet. Return it.
*/
- if (curreq->proxy && !curreq->proxy_reply &&
- (curreq->child_pid == NO_SUCH_CHILD_PID)) {
- radlog(L_ERR, "Discarding old proxied request %d from "
- "client %s port %d - ID: %d due to new request from the client",
- curreq->number, client->shortname,
- packet->src_port, packet->id);
- } else {
- /*
- * The authentication vectors are different, so
- * the NAS has given up on us, as we've taken too
- * long to process the request. This is a
- * SERIOUS problem!
- */
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
- radlog(L_ERR, "Dropping conflicting packet from "
- "client %s port %d - ID: %d due to unfinished request %d",
- client->shortname,
- packet->src_port, packet->id,
- curreq->number);
- return 0;
- }
+ if ((client->created + client->lifetime) > now) return client;
/*
- * The old request is finished. We now check the
- * authentication vectors. If the client has sent us a
- * request with identical code && ID, but different
- * vector, then they MUST have gotten our response, so we
- * can delete the original request, and process the new
- * one.
- *
- * If the vectors are the same, then it's a duplicate
- * request, and we can send a duplicate reply.
+ * This really puts them onto a queue for later
+ * deletion.
*/
- } else if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
+ client_delete(clients, client);
/*
- * If the packet has been delayed, then silently
- * send a response, and clear the delayed flag.
- *
- * Note that this means if the NAS kicks us while
- * we're delaying a reject, then the reject may
- * be sent sooner than otherwise.
- *
- * This COULD be construed as a bug. Maybe what
- * we want to do is to ignore the duplicate
- * packet, and send the reject later.
+ * Go find the enclosing network again.
*/
- if (curreq->options & RAD_REQUEST_OPTION_DELAYED_REJECT) {
- curreq->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
- rad_assert(curreq->listener == listener);
- listener->send(listener, curreq);
- return 0;
- }
+ client = client_find(clients, ipaddr, sock->proto);
/*
- * Maybe we've saved a reply packet. If so,
- * re-send it. Otherwise, just complain.
+ * WTF?
*/
- if (curreq->reply->code != 0) {
- DEBUG2("Sending duplicate reply "
- "to client %s port %d - ID: %d",
- client->shortname,
- packet->src_port, packet->id);
- rad_assert(curreq->listener == listener);
- listener->send(listener, curreq);
- return 0;
- }
+ if (!client) goto unknown;
+ if (!client->client_server) goto unknown;
/*
- * Else we never sent a reply to the NAS,
- * as we decided somehow we didn't like the request.
- *
- * This shouldn't happen, in general...
+ * At this point, 'client' is the enclosing
+ * network that configures where dynamic clients
+ * can be defined.
*/
- DEBUG2("Discarding duplicate request from client %s port %d - ID: %d",
- client->shortname, packet->src_port, packet->id);
- return 0;
- } /* else the vectors were different, so we discard the old request. */
+ rad_assert(client->dynamic == 0);
- /*
- * 'packet' has the same source IP, source port, code,
- * and Id as 'curreq', but a different authentication
- * vector. We can therefore delete 'curreq', as we were
- * only keeping it around to send out duplicate replies,
- * if the first reply got lost in the network.
- */
- if (curreq) {
- rl_yank(listener->rl, curreq);
- request_free(&curreq);
+ } else if (!client->dynamic && client->rate_limit) {
+ /*
+ * The IP is unknown, so we've found an enclosing
+ * network. Enable DoS protection. We only
+ * allow one new client per second. Known
+ * clients aren't subject to this restriction.
+ */
+ if (now == client->last_new_client) goto unknown;
}
- /*
- * A unique per-request counter.
- */
- curreq = request_alloc(); /* never fails */
-
- if ((curreq->reply = rad_alloc(0)) == NULL) {
- radlog(L_ERR, "No memory");
- exit(1);
- }
- curreq->listener = listener;
- curreq->packet = packet;
- curreq->packet->timestamp = curreq->timestamp;
- curreq->number = request_num_counter++;
- strNcpy(curreq->secret, client->secret, sizeof(curreq->secret));
-
- /*
- * Remember the request in the list.
- */
- if (!rl_add(listener->rl, curreq)) {
- radlog(L_ERR, "Failed to insert request %d in the list of live requests: discarding", curreq->number);
- request_free(&curreq);
- return 0;
+ client->last_new_client = now;
+
+ request = request_alloc();
+ 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);
+ goto unknown;
}
-
- /*
- * The request passes many of our sanity checks.
- * From here on in, if anything goes wrong, we
- * send a reject message, instead of dropping the
- * packet.
- */
+ request->reply = rad_alloc_reply(request->packet);
+ if (!request->reply) {
+ request_free(&request);
+ goto unknown;
+ }
+ request->packet->timestamp = request->timestamp;
+ request->number = 0;
+ request->priority = listener->type;
+ request->server = client->client_server;
+ request->root = &mainconfig;
/*
- * Build the reply template from the request.
+ * Run a fake request through the given virtual server.
+ * Look for FreeRADIUS-Client-IP-Address
+ * FreeRADIUS-Client-Secret
+ * ...
+ *
+ * and create the RADCLIENT structure from that.
*/
+ DEBUG("server %s {", request->server);
- curreq->reply->sockfd = curreq->packet->sockfd;
- curreq->reply->dst_ipaddr = curreq->packet->src_ipaddr;
- curreq->reply->src_ipaddr = curreq->packet->dst_ipaddr;
- curreq->reply->dst_port = curreq->packet->src_port;
- curreq->reply->src_port = curreq->packet->dst_port;
- curreq->reply->id = curreq->packet->id;
- curreq->reply->code = 0; /* UNKNOWN code */
- memcpy(curreq->reply->vector, curreq->packet->vector,
- sizeof(curreq->reply->vector));
- curreq->reply->vps = NULL;
- curreq->reply->data = NULL;
- curreq->reply->data_len = 0;
-
- *prequest = curreq;
- return 1;
-}
+ rcode = module_authorize(0, request);
+ DEBUG("} # server %s", request->server);
-static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
-{
- size_t len;
- listen_socket_t *sock = this->data;
+ if (rcode != RLM_MODULE_OK) {
+ request_free(&request);
+ goto unknown;
+ }
- if ((sock->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
- strcpy(buffer, "*");
+ /*
+ * If the client was updated by rlm_dynamic_clients,
+ * don't create the client from attribute-value pairs.
+ */
+ if (request->client == client) {
+ created = client_create(clients, request);
} else {
- ip_ntoh(&sock->ipaddr, buffer, bufsize);
+ created = request->client;
+
+ /*
+ * This frees the client if it isn't valid.
+ */
+ if (!client_validate(clients, client, created)) goto unknown;
}
+ request_free(&request);
- len = strlen(buffer);
+ if (!created) goto unknown;
- return len + snprintf(buffer + len, bufsize - len, " port %d",
- sock->port);
+ return created;
+#endif
}
+static int listen_bind(rad_listen_t *this);
+
/*
- * Parse an authentication or accounting socket.
+ * Process and reply to a server-status request.
+ * Like rad_authenticate and rad_accounting this should
+ * live in it's own file but it's so small we don't bother.
*/
-static int common_socket_parse(const char *filename, int lineno,
- const CONF_SECTION *cs, rad_listen_t *this)
+static int rad_status_server(REQUEST *request)
{
- int rcode;
- int listen_port;
- lrad_ipaddr_t ipaddr;
- listen_socket_t *sock = this->data;
- const char *section_name = NULL;
- CONF_SECTION *client_cs;
+ int rcode = RLM_MODULE_OK;
+ DICT_VALUE *dval;
- /*
- * Try IPv4 first
- */
- 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) {
- radlog(L_ERR, "%s[%d]: No address specified in listen section",
- filename, lineno);
- return -1;
+ switch (request->listener->type) {
+#ifdef WITH_STATS
+ case RAD_LISTEN_NONE:
+#endif
+ case RAD_LISTEN_AUTH:
+ dval = dict_valbyname(PW_AUTZ_TYPE, 0, "Status-Server");
+ if (dval) {
+ rcode = module_authorize(dval->value, request);
+ } else {
+ rcode = RLM_MODULE_OK;
}
- ipaddr.af = AF_INET6;
- }
-
- rcode = cf_item_parse(cs, "port", PW_TYPE_INTEGER,
- &listen_port, "0");
- if (rcode < 0) return -1;
-
- sock->ipaddr = ipaddr;
- sock->port = listen_port;
- /*
- * And bind it to the port.
- */
- if (listen_bind(this) < 0) {
- char buffer[128];
- radlog(L_CONS|L_ERR, "%s[%d]: Error binding to port for %s port %d",
- filename, cf_section_lineno(cs),
- ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)),
- sock->port);
- return -1;
- }
+ switch (rcode) {
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ request->reply->code = PW_AUTHENTICATION_ACK;
+ break;
- /*
- * If we can bind to interfaces, do so,
- * else don't.
- */
- if (cf_pair_find(cs, "interface")) {
-#ifndef SO_BINDTODEVICE
- radlog(L_CONS|L_ERR, "%s[%d]: System does not support binding to interfaces, delete this line from the configuration file.",
- filename, cf_section_lineno(cs));
- return -1;
-#else
- const char *value;
- const CONF_PAIR *cp = cf_pair_find(cs, "interface");
- struct ifreq ifreq;
+ case RLM_MODULE_FAIL:
+ case RLM_MODULE_HANDLED:
+ request->reply->code = 0; /* don't reply */
+ break;
- rad_assert(cp != NULL);
- value = cf_pair_value(cp);
- rad_assert(value != NULL);
-
- strcpy(ifreq.ifr_name, value);
-
- if (setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE,
- (char *)&ifreq, sizeof(ifreq)) < 0) {
- radlog(L_CONS|L_ERR, "%s[%d]: Failed binding to interface %s: %s",
- filename, cf_section_lineno(cs),
- value, strerror(errno));
- return -1;
- } /* else it worked. */
+ default:
+ case RLM_MODULE_REJECT:
+ request->reply->code = PW_AUTHENTICATION_REJECT;
+ break;
+ }
+ break;
+
+#ifdef WITH_ACCOUNTING
+ case RAD_LISTEN_ACCT:
+ dval = dict_valbyname(PW_ACCT_TYPE, 0, "Status-Server");
+ if (dval) {
+ rcode = module_accounting(dval->value, request);
+ } else {
+ rcode = RLM_MODULE_OK;
+ }
+
+ switch (rcode) {
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ request->reply->code = PW_ACCOUNTING_RESPONSE;
+ break;
+
+ default:
+ request->reply->code = 0; /* don't reply */
+ break;
+ }
+ break;
#endif
- }
- /*
- * Look for the name of a section that holds a list
- * of clients.
- */
- rcode = cf_item_parse(cs, "clients", PW_TYPE_STRING_PTR,
- §ion_name, NULL);
- if (rcode < 0) return -1; /* bad string */
- if (rcode > 0) return 0; /* non-existent is OK. */
+#ifdef WITH_COA
+ /*
+ * This is a vendor extension. Suggested by Glen
+ * Zorn in IETF 72, and rejected by the rest of
+ * the WG. We like it, so it goes in here.
+ */
+ case RAD_LISTEN_COA:
+ dval = dict_valbyname(PW_RECV_COA_TYPE, 0, "Status-Server");
+ if (dval) {
+ rcode = module_recv_coa(dval->value, request);
+ } else {
+ rcode = RLM_MODULE_OK;
+ }
- client_cs = cf_section_find(section_name);
- if (!client_cs) {
- radlog(L_CONS|L_ERR, "%s[%d]: Failed to find client section %s",
- filename, cf_section_lineno(cs), section_name);
- return -1;
+ switch (rcode) {
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ request->reply->code = PW_COA_ACK;
+ break;
+
+ default:
+ request->reply->code = 0; /* don't reply */
+ break;
+ }
+ break;
+#endif
+
+ default:
+ return 0;
}
- sock->clients = clients_parse_section(filename, client_cs);
- if (!sock->clients) {
- return -1;
+#ifdef WITH_STATS
+ /*
+ * Full statistics are available only on a statistics
+ * socket.
+ */
+ if (request->listener->type == RAD_LISTEN_NONE) {
+ request_stats_reply(request);
}
+#endif
return 0;
}
-/*
- * Send an authentication response packet
- */
-static int auth_socket_send(rad_listen_t *listener, REQUEST *request)
+#ifdef WITH_TCP
+static int auth_tcp_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
{
- rad_assert(request->listener == listener);
- rad_assert(listener->send == auth_socket_send);
+ int rcode;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ listen_socket_t *sock = listener->data;
+ RADCLIENT *client = sock->client;
/*
- * Ensure that the reply is sane
+ * Allocate a packet for partial reads.
*/
- if (request->reply->code == 0) {
- DEBUG2("There was no response configured: rejecting request %d", request->number);
- request->reply->code = PW_AUTHENTICATION_REJECT;
+ if (!sock->packet) {
+ sock->packet = rad_alloc(0);
+ if (!sock->packet) return 0;
+
+ sock->packet->sockfd = listener->fd;
+ sock->packet->src_ipaddr = sock->other_ipaddr;
+ sock->packet->src_port = sock->other_port;
+ sock->packet->dst_ipaddr = sock->my_ipaddr;
+ sock->packet->dst_port = sock->my_port;
}
+
+ /*
+ * Grab the packet currently being processed.
+ */
+ packet = sock->packet;
+
+ rcode = fr_tcp_read_packet(packet, 0);
/*
- * If we're delaying authentication rejects, then
- * mark the request as delayed, and do NOT send a
- * response right now.
- *
- * However, if it's already marked as delayed, then
- * send it now.
- */
- if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
- ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) &&
- (mainconfig.reject_delay > 0) &&
- ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {
- DEBUG2("Delaying request %d for %d seconds",
- request->number, mainconfig.reject_delay);
- request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
+ * Still only a partial packet. Put it back, and return,
+ * so that we'll read more data when it's ready.
+ */
+ if (rcode == 0) {
return 0;
}
- return rad_send(request->reply, request->packet, request->secret);
-}
+ if (rcode == -1) { /* error reading packet */
+ char buffer[256];
+ radlog(L_ERR, "Invalid packet from %s port %d: closing socket",
+ ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
+ packet->src_port);
+ }
-/*
- * Send an accounting response packet (or not)
- */
-static int acct_socket_send(rad_listen_t *listener, REQUEST *request)
-{
- rad_assert(request->listener == listener);
- rad_assert(listener->send == acct_socket_send);
-
- /*
- * Accounting reject's are silently dropped.
- *
- * We do it here to avoid polluting the rest of the
- * code with this knowledge
- */
- if (request->reply->code == 0) return 0;
-
- return rad_send(request->reply, request->packet, request->secret);
-}
-
-
-/*
- * Send a packet to a home server.
- *
- * FIXME: have different code for proxy auth & acct!
- */
-static int proxy_socket_send(rad_listen_t *listener, REQUEST *request)
-{
- listen_socket_t *sock = listener->data;
-
- rad_assert(request->proxy_listener == listener);
- rad_assert(listener->send == proxy_socket_send);
-
- request->proxy->src_ipaddr = sock->ipaddr;
- request->proxy->src_port = sock->port;
-
- return rad_send(request->proxy, request->packet, request->proxysecret);
-}
+ if (rcode < 0) { /* error or connection reset */
+ listener->status = RAD_LISTEN_STATUS_REMOVE_FD;
+ /*
+ * Decrement the number of connections.
+ */
+ if (sock->parent->num_connections > 0) {
+ sock->parent->num_connections--;
+ }
+ if (sock->client->num_connections > 0) {
+ sock->client->num_connections--;
+ }
-/*
- * Check if an incoming request is "ok"
- *
- * It takes packets, not requests. It sees if the packet looks
- * OK. If so, it does a number of sanity checks on it.
- */
-static int auth_socket_recv(rad_listen_t *listener,
- RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
-{
- RADIUS_PACKET *packet;
- RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
- RADCLIENT *client;
+ /*
+ * Tell the event handler that an FD has disappeared.
+ */
+ DEBUG("Client has closed connection");
+ event_new_fd(listener);
- packet = rad_recv(listener->fd);
- if (!packet) {
- radlog(L_ERR, "%s", librad_errstr);
+ /*
+ * Do NOT free the listener here. It's in use by
+ * a request, and will need to hang around until
+ * all of the requests are done.
+ *
+ * It is instead free'd in remove_from_request_hash()
+ */
return 0;
}
- RAD_SNMP_TYPE_INC(listener, total_requests); /* FIXME: auth specific */
-
- if ((client = client_listener_find(listener,
- &packet->src_ipaddr)) == NULL) {
- RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
-
- radlog(L_ERR, "Ignoring request from unknown client %s port %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port);
- rad_free(&packet);
- return 0;
- }
+ RAD_STATS_TYPE_INC(listener, total_requests);
/*
* Some sanity checks, based on the packet code.
*/
switch(packet->code) {
case PW_AUTHENTICATION_REQUEST:
+ RAD_STATS_CLIENT_INC(listener, client, total_requests);
fun = rad_authenticate;
break;
-
+
case PW_STATUS_SERVER:
if (!mainconfig.status_server) {
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
- rad_free(&packet);
+ rad_free(&sock->packet);
return 0;
}
fun = rad_status_server;
break;
default:
- RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
-
- radlog(L_ERR, "Invalid packet code %d sent to authentication port from client %s port %d "
- "- ID %d : IGNORED",
- packet->code, client->shortname,
- packet->src_port, packet->id);
- rad_free(&packet);
+ RAD_STATS_INC(radius_auth_stats.total_unknown_types);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+
+ DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED",
+ packet->code, client->shortname, packet->src_port);
+ rad_free(&sock->packet);
return 0;
- break;
} /* switch over packet types */
-
- if (!common_checks(listener, packet, prequest, client)) {
- rad_free(&packet);
+
+ if (!received_request(listener, packet, prequest, sock->client)) {
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, sock->client, total_packets_dropped);
+ rad_free(&sock->packet);
return 0;
}
*pfun = fun;
+ sock->packet = NULL; /* we have no need for more partial reads */
return 1;
}
-
-/*
- * Receive packets from an accounting socket
- */
-static int acct_socket_recv(rad_listen_t *listener,
- RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int auth_tcp_accept(rad_listen_t *listener,
+ UNUSED RAD_REQUEST_FUNP *pfun,
+ UNUSED REQUEST **prequest)
{
- RADIUS_PACKET *packet;
- RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
- RADCLIENT *client;
-
- packet = rad_recv(listener->fd);
- if (!packet) {
- radlog(L_ERR, "%s", librad_errstr);
- return 0;
- }
+ int newfd, src_port;
+ rad_listen_t *this;
+ socklen_t salen;
+ struct sockaddr_storage src;
+ listen_socket_t *sock;
+ fr_ipaddr_t src_ipaddr;
+ RADCLIENT *client;
- RAD_SNMP_TYPE_INC(listener, total_requests); /* FIXME: acct-specific */
-
- if ((client = client_listener_find(listener,
- &packet->src_ipaddr)) == NULL) {
- RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
-
- radlog(L_ERR, "Ignoring request from unknown client %s port %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port);
- rad_free(&packet);
- return 0;
- }
+ salen = sizeof(src);
- switch(packet->code) {
- case PW_ACCOUNTING_REQUEST:
- fun = rad_accounting;
- break;
-
- default:
+ DEBUG2(" ... new connection request on TCP socket.");
+
+ newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
+ if (newfd < 0) {
/*
- * FIXME: Update MIB for packet types?
+ * Non-blocking sockets must handle this.
*/
- radlog(L_ERR, "Invalid packet code %d sent to a accounting port "
- "from client %s port %d - ID %d : IGNORED",
- packet->code, client->shortname,
- packet->src_port, packet->id);
- rad_free(&packet);
- return 0;
- }
+ if (errno == EWOULDBLOCK) {
+ return 0;
+ }
- /*
- * FIXME: Accounting duplicates should be handled
- * differently than authentication duplicates.
- */
- if (!common_checks(listener, packet, prequest, client)) {
- rad_free(&packet);
- return 0;
+ DEBUG2(" ... failed to accept connection.");
+ return -1;
}
- *pfun = fun;
- return 1;
-}
-
-
-/*
- * Recieve packets from a proxy socket.
- */
-static int proxy_socket_recv(rad_listen_t *listener,
- RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
-{
- REALM *cl;
- REQUEST *oldreq;
- RADIUS_PACKET *packet;
- RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
-
- packet = rad_recv(listener->fd);
- if (!packet) {
- radlog(L_ERR, "%s", librad_errstr);
+ if (!fr_sockaddr2ipaddr(&src, salen, &src_ipaddr, &src_port)) {
+ DEBUG2(" ... unknown address family.");
return 0;
}
/*
- * Unsupported stuff
+ * Enforce client IP address checks on accept, not on
+ * every packet.
*/
- if (packet->src_ipaddr.af != AF_INET) {
- rad_assert("PROXY IPV6 NOT SUPPORTED" == NULL);
- }
-
- /*
- * FIXME: Add support for home servers!
- */
- if ((cl = realm_findbyaddr(packet->src_ipaddr.ipaddr.ip4addr.s_addr,
- packet->src_port)) == NULL) {
- radlog(L_ERR, "Ignoring request from unknown home server %s port %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port);
- rad_free(&packet);
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ close(newfd);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
return 0;
}
/*
- * FIXME: Client MIB updates?
+ * Enforce max_connectionsx on client && listen section.
*/
- switch(packet->code) {
- case PW_AUTHENTICATION_ACK:
- case PW_ACCESS_CHALLENGE:
- case PW_AUTHENTICATION_REJECT:
- fun = rad_authenticate;
- break;
-
- case PW_ACCOUNTING_RESPONSE:
- fun = rad_accounting;
- break;
-
- default:
+ if ((client->max_connections != 0) &&
+ (client->max_connections == client->num_connections)) {
/*
- * FIXME: Update MIB for packet types?
+ * FIXME: Print client IP/port, and server IP/port.
*/
- radlog(L_ERR, "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)),
- packet->src_port, packet->id);
- rad_free(&packet);
+ radlog(L_INFO, "Ignoring new connection due to client max_connections (%d)", client->max_connections);
+ close(newfd);
return 0;
}
- /*
- * Find the original request in the request list
- */
- oldreq = rl_find_proxy(packet);
-
- /*
- * If we haven't found the original request which was
- * sent, to get this reply. Complain, and discard this
- * request, as there's no way for us to send it to a NAS.
- */
- if (!oldreq) {
- radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s port %d - ID %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port, packet->id);
- rad_free(&packet);
+ sock = listener->data;
+ if ((sock->max_connections != 0) &&
+ (sock->max_connections == sock->num_connections)) {
+ /*
+ * FIXME: Print client IP/port, and server IP/port.
+ */
+ radlog(L_INFO, "Ignoring new connection due to socket max_connections");
+ close(newfd);
return 0;
}
+ client->num_connections++;
+ sock->num_connections++;
/*
- * The proxy reply has arrived too late, as the original
- * (old) request has timed out, been rejected, and marked
- * as finished. The client has already received a
- * response, so there is nothing that can be done. Delete
- * the tardy reply from the home server, and return nothing.
+ * Add the new listener.
*/
- if ((oldreq->reply->code != 0) ||
- (oldreq->finished)) {
- radlog(L_ERR, "Reply from home server %s port %d - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port, packet->id,
- oldreq->number);
- rad_free(&packet);
- return 0;
- }
+ this = listen_alloc(listener->type);
+ if (!this) return -1;
/*
- * If there is already a reply, maybe this one is a
- * duplicate?
+ * Copy everything, including the pointer to the socket
+ * information.
*/
- if (oldreq->proxy_reply) {
- if (memcmp(oldreq->proxy_reply->vector,
- packet->vector,
- sizeof(oldreq->proxy_reply->vector)) == 0) {
- radlog(L_ERR, "Discarding duplicate reply from home server %s port %d - ID: %d for request %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port, packet->id,
- oldreq->number);
- } else {
- /*
- * ? The home server gave us a new proxy
- * reply, which doesn't match the old
- * one. Delete it.
- */
- DEBUG2("Ignoring conflicting proxy reply");
- }
+ sock = this->data;
+ memcpy(this->data, listener->data, sizeof(*sock));
+ memcpy(this, listener, sizeof(*this));
+ this->next = NULL;
+ this->data = sock; /* fix it back */
- /*
- * We've already received a reply, so
- * we discard this one, as we don't want
- * to do duplicate work.
- */
- rad_free(&packet);
- return 0;
- } /* else there wasn't a proxy reply yet, so we can process it */
+ sock->parent = listener->data;
+ sock->other_ipaddr = src_ipaddr;
+ sock->other_port = src_port;
+ sock->client = client;
- /*
- * Refresh the old request, and update it with the proxy
- * reply.
- *
- * ? Can we delete the proxy request here? * Is there
- * any more need for it?
- *
- * FIXME: we probably shouldn't be updating the time
- * stamp here.
- */
- oldreq->timestamp = time_now;
- oldreq->proxy_reply = packet;
+ this->fd = newfd;
+ this->status = RAD_LISTEN_STATUS_INIT;
+ this->recv = auth_tcp_recv;
/*
- * FIXME: we should really verify the digest here,
- * before marking this packet as a valid response.
- *
- * This is a security problem, I think...
+ * FIXME: set O_NONBLOCK on the accept'd fd.
+ * See djb's portability rants for details.
*/
/*
- * Now that we've verified the packet IS actually from
- * that home server, and not forged, we can go mark the
- * entries for this home server as active.
- *
- * If we had done this check in the 'find realm by IP address'
- * function, then an attacker could force us to use a home
- * server which was inactive, by forging reply packets
- * which didn't match any request. We would think that
- * the reply meant the home server was active, would
- * re-activate the realms, and THEN bounce the packet
- * as garbage.
- */
- for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
- if (oldreq->proxy_reply->src_ipaddr.af != cl->ipaddr.af) continue;
- if (cl->ipaddr.af != AF_INET) continue; /* FIXME */
-
- if (oldreq->proxy_reply->src_ipaddr.ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) {
- if (oldreq->proxy_reply->src_port == cl->auth_port) {
- cl->active = TRUE;
- cl->last_reply = oldreq->timestamp;
- } else if (oldreq->proxy_reply->src_port == cl->acct_port) {
- cl->acct_active = TRUE;
- cl->last_reply = oldreq->timestamp;
- }
- }
- }
-
- rad_assert(fun != NULL);
- *pfun = fun;
- *prequest = oldreq;
+ * Tell the event loop that we have a new FD.
+ * This can be called from a child thread...
+ */
+ event_new_fd(this);
- return 1;
+ return 0;
}
+#endif
-#define STATE_UNOPENED (0)
-#define STATE_UNLOCKED (1)
-#define STATE_HEADER (2)
-#define STATE_READING (3)
-#define STATE_DONE (4)
-#define STATE_WAITING (5)
/*
- * If we're limiting outstanding packets, then mark the response
- * as being sent.
+ * This function is stupid and complicated.
*/
-static int detail_send(rad_listen_t *listener, REQUEST *request)
+static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
{
- listen_detail_t *data = listener->data;
+ size_t len;
+ listen_socket_t *sock = this->data;
+ const char *name;
- rad_assert(request->listener == listener);
- rad_assert(listener->send == detail_send);
+ switch (this->type) {
+#ifdef WITH_STATS
+ case RAD_LISTEN_NONE: /* what a hack... */
+ name = "status";
+ break;
+#endif
+
+ case RAD_LISTEN_AUTH:
+ name = "authentication";
+ break;
+
+#ifdef WITH_ACCOUNTING
+ case RAD_LISTEN_ACCT:
+ name = "accounting";
+ break;
+#endif
+
+#ifdef WITH_PROXY
+ case RAD_LISTEN_PROXY:
+ name = "proxy";
+ break;
+#endif
+
+#ifdef WITH_VMPS
+ case RAD_LISTEN_VQP:
+ name = "vmps";
+ break;
+#endif
+
+#ifdef WITH_DHCP
+ case RAD_LISTEN_DHCP:
+ name = "dhcp";
+ break;
+#endif
- if (request->simul_max >= 0) {
- rad_assert(data->outstanding != NULL);
- rad_assert(request->simul_max < data->max_outstanding);
+#ifdef WITH_COA
+ case RAD_LISTEN_COA:
+ name = "coa";
+ break;
+#endif
- data->outstanding[request->simul_max] = 0;
+ default:
+ name = "??";
+ break;
}
- return 0;
-}
+#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);
-/*
- * Open the detail file..
- *
- * FIXME: create it, if it's not already there, so that the main
- * server select() will wake us up if there's anything to read.
- */
-static int detail_open(rad_listen_t *this)
-{
- struct stat st;
- char buffer[2048];
- listen_detail_t *data = this->data;
+ if (sock->interface) {
+ ADDSTRING(" interface ");
+ ADDSTRING(sock->interface);
+ }
- rad_assert(data->state == STATE_UNOPENED);
- snprintf(buffer, sizeof(buffer), "%s.work", data->detail);
-
- /*
- * FIXME: Have "one-shot" configuration, where it
- * will read the detail file, and exit once it's
- * done.
- *
- * FIXME: Try harder to open the detail file.
- * Maybe sleep for X usecs if it doesn't exist?
- */
+#ifdef WITH_TCP
+ if (this->recv == auth_tcp_accept) {
+ ADDSTRING(" proto tcp");
+ }
+#endif
+#ifdef WITH_TCP
/*
- * Open detail.work first, so we don't lose
- * accounting packets. It's probably better to
- * duplicate them than to lose them.
- *
- * Note that we're not writing to the file, but
- * we've got to open it for writing in order to
- * establish the lock, to prevent rlm_detail from
- * writing to it.
+ * TCP sockets get printed a little differently, to make
+ * it clear what's going on.
*/
- this->fd = open(buffer, O_RDWR);
- if (this->fd < 0) {
- /*
- * Try reading the detail file. If it
- * doesn't exist, we can't do anything.
- *
- * Doing the stat will tell us if the file
- * exists, even if we don't have permissions
- * to read it.
- */
- if (stat(data->detail, &st) < 0) {
- return 0;
- }
-
- /*
- * Open it BEFORE we rename it, just to
- * be safe...
- */
- this->fd = open(data->detail, O_RDWR);
- if (this->fd < 0) {
- radlog(L_ERR, "Failed to open %s: %s",
- data->detail, strerror(errno));
- return 0;
+ if (sock->client) {
+ ADDSTRING(" from client (");
+ ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
+ FORWARD;
+
+ ADDSTRING(", ");
+ snprintf(buffer, bufsize, "%d", sock->other_port);
+ FORWARD;
+ ADDSTRING(") -> (");
+
+ if ((sock->my_ipaddr.af == AF_INET) &&
+ (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+ strlcpy(buffer, "*", bufsize);
+ } else {
+ ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
}
+ FORWARD;
- /*
- * Rename detail to detail.work
- */
- if (rename(data->detail, buffer) < 0) {
- close(this->fd);
- this->fd = -1;
- return 0;
- }
- } /* else detail.work existed, and we opened it */
-
- rad_assert(data->vps == NULL);
-
- rad_assert(data->fp == NULL);
- data->fp = fdopen(this->fd, "r");
- if (!data->fp) {
- radlog(L_ERR, "Failed to re-open %s: %s",
- data->detail, strerror(errno));
- return 0;
- }
-
- data->state = STATE_UNLOCKED;
-
- data->client_ip.af = AF_UNSPEC;
- data->timestamp = 0;
-
- return 1;
-}
+ ADDSTRING(", ");
+ snprintf(buffer, bufsize, "%d", sock->my_port);
+ FORWARD;
-/*
- * This is a bad hack, just so complaints have meaningful text.
- */
-static const RADCLIENT detail_client = {
- { /* ipaddr */
- AF_INET,
- {{ INADDR_NONE }}
- },
- 32,
- "<detail-file>",
- "secret",
- "UNKNOWN-CLIENT",
- "other",
- "",
- "",
- -1
-};
+ if (this->server) {
+ ADDSTRING(", virtual-server=");
+ ADDSTRING(this->server);
+ }
-static int detail_recv(rad_listen_t *listener,
- RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
-{
- int free_slot = -1;
- char key[256], value[1024];
- VALUE_PAIR *vp, **tail;
- RADIUS_PACKET *packet;
- char buffer[2048];
- listen_detail_t *data = listener->data;
+ ADDSTRING(")");
- if (data->state == STATE_UNOPENED) {
- rad_assert(listener->fd < 0);
- if (!detail_open(listener)) return 0;
+ return 1;
}
- rad_assert(listener->fd >= 0);
+#ifdef WITH_PROXY
/*
- * Try to lock fd. If we can't, return. If we can,
- * continue. This means that the server doesn't block
- * while waiting for the lock to open...
+ * Maybe it's a socket that we opened to a home server.
*/
- if (data->state == STATE_UNLOCKED) {
- /*
- * Note that we do NOT block waiting for the
- * lock. We've re-named the file above, so we've
- * already guaranteed that any *new* detail
- * writer will not be opening this file. The
- * only purpose of the lock is to catch a race
- * condition where the execution "ping-pongs"
- * between radiusd & radrelay.
- */
- if (rad_lockfd_nonblock(listener->fd, 0) < 0) {
- return 0;
+ if ((sock->proto == IPPROTO_TCP) &&
+ (this->type == RAD_LISTEN_PROXY)) {
+ ADDSTRING(" (");
+ ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
+ FORWARD;
+
+ ADDSTRING(", ");
+ snprintf(buffer, bufsize, "%d", sock->my_port);
+ FORWARD;
+ ADDSTRING(") -> home_server (");
+
+ if ((sock->other_ipaddr.af == AF_INET) &&
+ (sock->other_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+ strlcpy(buffer, "*", bufsize);
+ } else {
+ ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
+ }
+ FORWARD;
+
+ ADDSTRING(", ");
+ snprintf(buffer, bufsize, "%d", sock->other_port);
+ FORWARD;
+
+ ADDSTRING(")");
+
+ return 1;
+ }
+#endif /* WITH_PROXY */
+#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);
+ } else {
+ ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
+ }
+ FORWARD;
+
+ ADDSTRING(" port ");
+ snprintf(buffer, bufsize, "%d", sock->my_port);
+ FORWARD;
+
+ if (this->server) {
+ ADDSTRING(" as server ");
+ ADDSTRING(this->server);
+ }
+
+#undef ADDSTRING
+#undef FORWARD
+
+ return 1;
+}
+
+extern int check_config; /* radiusd.c */
+
+
+/*
+ * Parse an authentication or accounting socket.
+ */
+static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
+{
+ int rcode;
+ int listen_port;
+ fr_ipaddr_t ipaddr;
+ listen_socket_t *sock = this->data;
+ char *section_name = NULL;
+ CONF_SECTION *client_cs, *parentcs;
+
+ /*
+ * Try IPv4 first
+ */
+ 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(cf_sectiontoitem(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");
+ if (rcode < 0) return -1;
+
+ if ((listen_port < 0) || (listen_port > 65535)) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "Invalid value for \"port\"");
+ return -1;
+ }
+
+ sock->proto = IPPROTO_UDP;
+
+ if (cf_pair_find(cs, "proto")) {
+#ifndef WITH_TCP
+ cf_log_err(cf_sectiontoitem(cs),
+ "System does not support the TCP protocol. Delete this line from the configuration file.");
+ return -1;
+#else
+ char *proto = NULL;
+
+
+ rcode = cf_item_parse(cs, "proto", PW_TYPE_STRING_PTR,
+ &proto, "udp");
+ if (rcode < 0) return -1;
+
+ if (strcmp(proto, "udp") == 0) {
+ sock->proto = IPPROTO_UDP;
+
+ } else if (strcmp(proto, "tcp") == 0) {
+ sock->proto = IPPROTO_TCP;
+
+ rcode = cf_item_parse(cs, "max_connections", PW_TYPE_INTEGER,
+ &sock->max_connections, "64");
+ if (rcode < 0) return -1;
+
+ } else {
+ cf_log_err(cf_sectiontoitem(cs),
+ "Unknown proto name \"%s\"", proto);
+ free(proto);
+ return -1;
+ }
+ free(proto);
+
+ /*
+ * TCP requires a destination IP for sockets.
+ * UDP doesn't, so it's allowed.
+ */
+#ifdef WITH_PROXY
+ if ((this->type == RAD_LISTEN_PROXY) &&
+ (sock->proto != IPPROTO_UDP)) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "Proxy listeners can only listen on proto = udp");
+ return -1;
+ }
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
+ }
+
+ sock->my_ipaddr = ipaddr;
+ sock->my_port = listen_port;
+
+ if (check_config) {
+ if (home_server_find(&sock->my_ipaddr, sock->my_port, sock->proto)) {
+ char buffer[128];
+
+ DEBUG("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;
+ }
+
+ return 0; /* don't do anything */
+ }
+
+ /*
+ * If we can bind to interfaces, do so,
+ * else don't.
+ */
+ if (cf_pair_find(cs, "interface")) {
+ const char *value;
+ CONF_PAIR *cp = cf_pair_find(cs, "interface");
+
+ rad_assert(cp != NULL);
+ value = cf_pair_value(cp);
+ if (!value) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "No interface name given");
+ return -1;
+ }
+ sock->interface = value;
+ }
+
+#ifdef WITH_DHCP
+ /*
+ * If we can do broadcasts..
+ */
+ if (cf_pair_find(cs, "broadcast")) {
+#ifndef SO_BROADCAST
+ cf_log_err(cf_sectiontoitem(cs),
+ "System does not support broadcast sockets. Delete this line from the configuration file.");
+ return -1;
+#else
+ const char *value;
+ CONF_PAIR *cp = cf_pair_find(cs, "broadcast");
+
+ if (this->type != RAD_LISTEN_DHCP) {
+ cf_log_err(cf_pairtoitem(cp),
+ "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) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "No broadcast value given");
+ return -1;
+ }
+
+ /*
+ * Hack... whatever happened to cf_section_parse?
+ */
+ sock->broadcast = (strcmp(value, "yes") == 0);
+#endif
+ }
+#endif
+
+ /*
+ * And bind it to the port.
+ */
+ if (listen_bind(this) < 0) {
+ char buffer[128];
+ cf_log_err(cf_sectiontoitem(cs),
+ "Error binding to port for %s port %d",
+ ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)),
+ sock->my_port);
+ return -1;
+ }
+
+#ifdef WITH_PROXY
+ /*
+ * Proxy sockets don't have clients.
+ */
+ 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);
+ 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);
+ if (!client_cs) {
+ client_cs = cf_section_find(section_name);
+ }
+ if (!client_cs) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "Failed to find clients %s {...}",
+ section_name);
+ free(section_name);
+ return -1;
+ }
+ free(section_name);
+ } /* else there was no "clients = " entry. */
+
+ if (!client_cs) {
+ CONF_SECTION *server_cs;
+
+ server_cs = cf_section_sub_find_name2(parentcs,
+ "server",
+ this->server);
+ /*
+ * Found a "server foo" section. If there are clients
+ * in it, use them.
+ */
+ if (server_cs &&
+ (cf_section_sub_find(server_cs, "client") != NULL)) {
+ client_cs = server_cs;
}
+ }
+
+ /*
+ * Still nothing. Look for global clients.
+ */
+ if (!client_cs) client_cs = parentcs;
+
+ sock->clients = clients_parse_section(client_cs);
+ if (!sock->clients) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "Failed to load clients for this listen section");
+ return -1;
+ }
+
+#ifdef WITH_TCP
+ if (sock->proto == IPPROTO_TCP) {
/*
- * Look for the header
+ * Re-write the listener receive function to
+ * allow us to accept the socket.
*/
- data->state = STATE_HEADER;
+ this->recv = auth_tcp_accept;
}
+#endif
+
+ return 0;
+}
+
+/*
+ * Send an authentication response packet
+ */
+static int auth_socket_send(rad_listen_t *listener, REQUEST *request)
+{
+ rad_assert(request->listener == listener);
+ rad_assert(listener->send == auth_socket_send);
+
+ return rad_send(request->reply, request->packet,
+ request->client->secret);
+}
+
+
+#ifdef WITH_ACCOUNTING
+/*
+ * Send an accounting response packet (or not)
+ */
+static int acct_socket_send(rad_listen_t *listener, REQUEST *request)
+{
+ rad_assert(request->listener == listener);
+ rad_assert(listener->send == acct_socket_send);
+
+ /*
+ * Accounting reject's are silently dropped.
+ *
+ * We do it here to avoid polluting the rest of the
+ * code with this knowledge
+ */
+ if (request->reply->code == 0) return 0;
+
+ return rad_send(request->reply, request->packet,
+ request->client->secret);
+}
+#endif
+
+#ifdef WITH_PROXY
+/*
+ * Send a packet to a home server.
+ *
+ * FIXME: have different code for proxy auth & acct!
+ */
+static int proxy_socket_send(rad_listen_t *listener, REQUEST *request)
+{
+ rad_assert(request->proxy_listener == listener);
+ rad_assert(listener->send == proxy_socket_send);
+
+ return rad_send(request->proxy, request->packet,
+ request->home_server->secret);
+}
+#endif
+
+#ifdef WITH_STATS
+/*
+ * Check if an incoming request is "ok"
+ *
+ * It takes packets, not requests. It sees if the packet looks
+ * OK. If so, it does a number of sanity checks on it.
+ */
+static int stats_socket_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ ssize_t rcode;
+ int code, src_port;
+ RADIUS_PACKET *packet;
+ RADCLIENT *client;
+ fr_ipaddr_t src_ipaddr;
+
+ rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
+ if (rcode < 0) return 0;
+
+ RAD_STATS_TYPE_INC(listener, total_requests);
+
+ if (rcode < 20) { /* AUTH_HDR_LEN */
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ return 0;
+ }
+
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+ return 0;
+ }
+
+ /*
+ * We only understand Status-Server on this socket.
+ */
+ if (code != PW_STATUS_SERVER) {
+ DEBUG("Ignoring packet code %d sent to Status-Server port",
+ code);
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_unknown_types);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+ return 0;
+ }
+
+ /*
+ * Now that we've sanity checked everything, receive the
+ * packet.
+ */
+ packet = rad_recv(listener->fd, 1); /* require message authenticator */
+ if (!packet) {
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ DEBUG("%s", fr_strerror());
+ return 0;
+ }
+
+ if (!received_request(listener, packet, prequest, client)) {
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+ rad_free(&packet);
+ return 0;
+ }
+
+ *pfun = rad_status_server;
+ return 1;
+}
+#endif
+
+
+/*
+ * Check if an incoming request is "ok"
+ *
+ * It takes packets, not requests. It sees if the packet looks
+ * OK. If so, it does a number of sanity checks on it.
+ */
+static int auth_socket_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ ssize_t rcode;
+ int code, src_port;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ RADCLIENT *client;
+ fr_ipaddr_t src_ipaddr;
+
+ rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
+ if (rcode < 0) return 0;
+
+ RAD_STATS_TYPE_INC(listener, total_requests);
+
+ if (rcode < 20) { /* AUTH_HDR_LEN */
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ return 0;
+ }
+
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+ return 0;
+ }
+
+ /*
+ * Some sanity checks, based on the packet code.
+ */
+ switch(code) {
+ case PW_AUTHENTICATION_REQUEST:
+ RAD_STATS_CLIENT_INC(listener, client, total_requests);
+ fun = rad_authenticate;
+ break;
+
+ case PW_STATUS_SERVER:
+ if (!mainconfig.status_server) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+ DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
+ return 0;
+ }
+ fun = rad_status_server;
+ break;
+
+ default:
+ rad_recv_discard(listener->fd);
+ RAD_STATS_INC(radius_auth_stats.total_unknown_types);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+
+ DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED",
+ code, client->shortname, src_port);
+ return 0;
+ break;
+ } /* switch over packet types */
/*
- * If we keep track of the outstanding requests, do so
- * here. Note that to minimize potential work, we do
- * so only once the file is opened & locked.
+ * Now that we've sanity checked everything, receive the
+ * packet.
*/
- if (data->max_outstanding) {
- int i;
+ packet = rad_recv(listener->fd, client->message_authenticator);
+ if (!packet) {
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ DEBUG("%s", fr_strerror());
+ return 0;
+ }
- for (i = 0; i < data->max_outstanding; i++) {
- if (!data->outstanding[i]) {
- free_slot = i;
- break;
- }
- }
+ if (!received_request(listener, packet, prequest, client)) {
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+ rad_free(&packet);
+ return 0;
+ }
- /*
- * All of the slots are full, don't read data.
- */
- if (free_slot < 0) return 0;
+ *pfun = fun;
+ return 1;
+}
+
+
+#ifdef WITH_ACCOUNTING
+/*
+ * Receive packets from an accounting socket
+ */
+static int acct_socket_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ ssize_t rcode;
+ int code, src_port;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ RADCLIENT *client;
+ fr_ipaddr_t src_ipaddr;
+
+ rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
+ if (rcode < 0) return 0;
+
+ RAD_STATS_TYPE_INC(listener, total_requests);
+
+ if (rcode < 20) { /* AUTH_HDR_LEN */
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ return 0;
+ }
+
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+ return 0;
}
/*
- * Catch an out of memory condition which will most likely
- * never be met.
+ * Some sanity checks, based on the packet code.
*/
- if (data->state == STATE_DONE) goto alloc_packet;
+ switch(code) {
+ case PW_ACCOUNTING_REQUEST:
+ RAD_STATS_CLIENT_INC(listener, client, total_requests);
+ fun = rad_accounting;
+ break;
+
+ case PW_STATUS_SERVER:
+ if (!mainconfig.status_server) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+
+ DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
+ return 0;
+ }
+ fun = rad_status_server;
+ break;
+
+ default:
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_unknown_types);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+
+ DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED",
+ code, client->shortname, src_port);
+ return 0;
+ } /* switch over packet types */
/*
- * If we're in another state, then it means that we read
- * a partial packet, which is bad.
+ * Now that we've sanity checked everything, receive the
+ * packet.
*/
- rad_assert(data->state == STATE_HEADER);
- rad_assert(data->vps == NULL);
+ packet = rad_recv(listener->fd, 0);
+ if (!packet) {
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ radlog(L_ERR, "%s", fr_strerror());
+ return 0;
+ }
/*
- * We read the last packet, and returned it for
- * processing. We later come back here to shut
- * everything down, and unlink the file.
+ * There can be no duplicate accounting packets.
*/
- if (feof(data->fp)) {
- rad_assert(data->state == STATE_HEADER);
+ if (!received_request(listener, packet, prequest, client)) {
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+ rad_free(&packet);
+ return 0;
+ }
- /*
- * Don't unlink the file until we've received
- * all of the responses.
- */
- if (data->max_outstanding > 0) {
- int i;
+ *pfun = fun;
+ return 1;
+}
+#endif
- for (i = 0; i < data->max_outstanding; i++) {
- /*
- * FIXME: close the file?
- */
- if (data->outstanding[i]) {
- data->state = STATE_WAITING;
- return 0;
- }
- }
- }
- cleanup:
- rad_assert(data->vps == NULL);
+#ifdef WITH_COA
+/*
+ * For now, all CoA requests are *only* originated, and not
+ * proxied. So all of the necessary work is done in the
+ * post-proxy section, which is automatically handled by event.c.
+ * As a result, we don't have to do anything here.
+ */
+static int rad_coa_reply(REQUEST *request)
+{
+ VALUE_PAIR *s1, *s2;
- snprintf(buffer, sizeof(buffer), "%s.work", data->detail);
- unlink(buffer);
- fclose(data->fp); /* closes listener->fd */
- data->fp = NULL;
- listener->fd = -1;
- data->state = STATE_UNOPENED;
+ /*
+ * Inform the user about RFC requirements.
+ */
+ s1 = pairfind(request->proxy->vps, PW_STATE, 0);
+ if (s1) {
+ s2 = pairfind(request->proxy_reply->vps, PW_STATE, 0);
- /*
- * Try to open "detail" again. If we're on a
- * busy RADIUS server, odds are that it will
- * now exist.
- */
- detail_open(listener);
- return 0;
+ if (!s2) {
+ DEBUG("WARNING: Client was sent State in CoA, and did not respond with State.");
+
+ } else if ((s1->length != s2->length) ||
+ (memcmp(s1->vp_octets, s2->vp_octets,
+ s1->length) != 0)) {
+ DEBUG("WARNING: Client was sent State in CoA, and did not respond with the same State.");
+ }
}
- tail = &data->vps;
+ return RLM_MODULE_OK;
+}
+
+/*
+ * Receive a CoA packet.
+ */
+static int rad_coa_recv(REQUEST *request)
+{
+ int rcode = RLM_MODULE_OK;
+ int ack, nak;
+ VALUE_PAIR *vp;
/*
- * Fill the buffer...
+ * Get the correct response
*/
- while (fgets(buffer, sizeof(buffer), data->fp)) {
+ switch (request->packet->code) {
+ case PW_COA_REQUEST:
+ ack = PW_COA_ACK;
+ nak = PW_COA_NAK;
+ break;
+
+ case PW_DISCONNECT_REQUEST:
+ ack = PW_DISCONNECT_ACK;
+ nak = PW_DISCONNECT_NAK;
+ break;
+
+ default: /* shouldn't happen */
+ return RLM_MODULE_FAIL;
+ }
+
+#ifdef WITH_PROXY
+#define WAS_PROXIED (request->proxy)
+#else
+#define WAS_PROXIED (0)
+#endif
+
+ if (!WAS_PROXIED) {
/*
- * No CR, die.
+ * RFC 5176 Section 3.3. If we have a CoA-Request
+ * with Service-Type = Authorize-Only, it MUST
+ * have a State attribute in it.
*/
- if (!strchr(buffer, '\n')) {
- pairfree(&data->vps);
- goto cleanup;
+ vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0);
+ if (request->packet->code == PW_COA_REQUEST) {
+ if (vp && (vp->vp_integer == 17)) {
+ vp = pairfind(request->packet->vps, PW_STATE, 0);
+ if (!vp || (vp->length == 0)) {
+ RDEBUG("ERROR: CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
+ request->reply->code = PW_COA_NAK;
+ return RLM_MODULE_FAIL;
+ }
+ }
+ } else if (vp) {
+ /*
+ * RFC 5176, Section 3.2.
+ */
+ RDEBUG("ERROR: Disconnect-Request MUST NOT contain a Service-Type attribute");
+ request->reply->code = PW_DISCONNECT_NAK;
+ return RLM_MODULE_FAIL;
}
- /*
- * We've read a header, possibly packet contents,
- * and are now at the end of the packet.
- */
- if ((data->state == STATE_READING) &&
- (buffer[0] == '\n')) {
- data->state = STATE_DONE;
+ rcode = module_recv_coa(0, request);
+ switch (rcode) {
+ case RLM_MODULE_FAIL:
+ case RLM_MODULE_INVALID:
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ default:
+ request->reply->code = nak;
+ break;
+
+ case RLM_MODULE_HANDLED:
+ return rcode;
+
+ case RLM_MODULE_NOOP:
+ case RLM_MODULE_NOTFOUND:
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ request->reply->code = ack;
break;
}
+ } else {
+ /*
+ * Start the reply code with the proxy reply
+ * code.
+ */
+ request->reply->code = request->proxy_reply->code;
+ }
+ /*
+ * Copy State from the request to the reply.
+ * See RFC 5176 Section 3.3.
+ */
+ vp = paircopy2(request->packet->vps, PW_STATE, 0);
+ if (vp) pairadd(&request->reply->vps, vp);
+
+ /*
+ * We may want to over-ride the reply.
+ */
+ rcode = module_send_coa(0, request);
+ switch (rcode) {
/*
- * Look for date/time header, and read VP's if
- * found. If not, keep reading lines until we
- * find one.
+ * We need to send CoA-NAK back if Service-Type
+ * is Authorize-Only. Rely on the user's policy
+ * to do that. We're not a real NAS, so this
+ * restriction doesn't (ahem) apply to us.
*/
- if (data->state == STATE_HEADER) {
- int y;
+ case RLM_MODULE_FAIL:
+ case RLM_MODULE_INVALID:
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ default:
+ /*
+ * Over-ride an ACK with a NAK
+ */
+ request->reply->code = nak;
+ break;
+
+ case RLM_MODULE_HANDLED:
+ return rcode;
- if (sscanf(buffer, "%*s %*s %*d %*d:%*d:%*d %d", &y)) {
- data->state = STATE_READING;
+ case RLM_MODULE_NOOP:
+ case RLM_MODULE_NOTFOUND:
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ /*
+ * Do NOT over-ride a previously set value.
+ * Otherwise an "ok" here will re-write a
+ * NAK to an ACK.
+ */
+ if (request->reply->code == 0) {
+ request->reply->code = ack;
}
- continue;
- }
+ break;
- /*
- * We have a full "attribute = value" line.
- * If it doesn't look reasonable, skip it.
- */
- if (sscanf(buffer, "%255s = %1023s", key, value) != 2) {
- continue;
- }
+ }
- /*
- * Skip non-protocol attributes.
- */
- if (!strcasecmp(key, "Request-Authenticator")) continue;
+ return RLM_MODULE_OK;
+}
- /*
- * Set the original client IP address, based on
- * what's in the detail file.
- *
- * Hmm... we don't set the server IP address.
- * or port. Oh well.
- */
- if (!strcasecmp(key, "Client-IP-Address")) {
- data->client_ip.af = AF_INET;
- ip_hton(value, AF_INET, &data->client_ip);
- continue;
- }
- /*
- * The original time at which we received the
- * packet. We need this to properly calculate
- * Acct-Delay-Time.
- */
- if (!strcasecmp(key, "Timestamp")) {
- data->timestamp = atoi(value);
- continue;
- }
+/*
+ * Check if an incoming request is "ok"
+ *
+ * It takes packets, not requests. It sees if the packet looks
+ * OK. If so, it does a number of sanity checks on it.
+ */
+static int coa_socket_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ ssize_t rcode;
+ int code, src_port;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ RADCLIENT *client;
+ fr_ipaddr_t src_ipaddr;
- /*
- * Read one VP.
- *
- * FIXME: do we want to check for non-protocol
- * attributes like radsqlrelay does?
- */
- vp = NULL;
- if ((userparse(buffer, &vp) > 0) &&
- (vp != NULL)) {
- *tail = vp;
- tail = &(vp->next);
- }
+ rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
+ if (rcode < 0) return 0;
+
+ RAD_STATS_TYPE_INC(listener, total_requests);
+
+ if (rcode < 20) { /* AUTH_HDR_LEN */
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ return 0;
}
- /*
- * We got to EOF, If we're in STATE_HEADER, it's OK.
- * Otherwise it's a problem. In any case, nuke the file
- * and start over from scratch,
- */
- if (feof(data->fp)) {
- goto cleanup;
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+ return 0;
}
/*
- * FIXME: Do load management.
+ * Some sanity checks, based on the packet code.
*/
+ switch(code) {
+ case PW_COA_REQUEST:
+ case PW_DISCONNECT_REQUEST:
+ fun = rad_coa_recv;
+ break;
- /*
- * If we're not done, then there's a problem. The checks
- * above for EOF
- */
- rad_assert(data->state == STATE_DONE);
+ default:
+ rad_recv_discard(listener->fd);
+ DEBUG("Invalid packet code %d sent to coa port from client %s port %d : IGNORED",
+ code, client->shortname, src_port);
+ return 0;
+ break;
+ } /* switch over packet types */
/*
- * The packet we read was empty, re-set the state to look
- * for a header, and don't return anything.
+ * Now that we've sanity checked everything, receive the
+ * packet.
*/
- if (!data->vps) {
- data->state = STATE_HEADER;
+ packet = rad_recv(listener->fd, client->message_authenticator);
+ if (!packet) {
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ DEBUG("%s", fr_strerror());
return 0;
}
- /*
- * Allocate the packet. If we fail, it's a serious
- * problem.
- */
- alloc_packet:
- packet = rad_alloc(1);
- if (!packet) {
- return 0; /* maybe memory will magically free up... */
+ if (!received_request(listener, packet, prequest, client)) {
+ rad_free(&packet);
+ return 0;
}
- memset(packet, 0, sizeof(*packet));
- packet->sockfd = -1;
- packet->src_ipaddr.af = AF_INET;
- packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- packet->code = PW_ACCOUNTING_REQUEST;
- packet->timestamp = time(NULL);
+ *pfun = fun;
+ return 1;
+}
+#endif
- /*
- * Look for Acct-Delay-Time, and update
- * based on Acct-Delay-Time += (time(NULL) - timestamp)
- */
- vp = pairfind(packet->vps, PW_ACCT_DELAY_TIME);
- if (!vp) {
- vp = paircreate(PW_ACCT_DELAY_TIME, PW_TYPE_INTEGER);
- rad_assert(vp != NULL);
- }
- if (data->timestamp != 0) {
- vp->lvalue += time(NULL) - data->timestamp;
+#ifdef WITH_PROXY
+/*
+ * Recieve packets from a proxy socket.
+ */
+static int proxy_socket_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ REQUEST *request;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ char buffer[128];
+
+ packet = rad_recv(listener->fd, 0);
+ if (!packet) {
+ radlog(L_ERR, "%s", fr_strerror());
+ return 0;
}
/*
- * Remember where it came from, so that we don't
- * proxy it to the place it came from...
+ * FIXME: Client MIB updates?
*/
- if (data->client_ip.af != AF_UNSPEC) {
- packet->src_ipaddr = data->client_ip;
- }
+ switch(packet->code) {
+ case PW_AUTHENTICATION_ACK:
+ case PW_ACCESS_CHALLENGE:
+ case PW_AUTHENTICATION_REJECT:
+ fun = rad_authenticate;
+ break;
- vp = pairfind(packet->vps, PW_PACKET_SRC_IP_ADDRESS);
- if (vp) {
- packet->src_ipaddr.af = AF_INET;
- packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->lvalue;
- } else {
- vp = pairfind(packet->vps, PW_PACKET_SRC_IPV6_ADDRESS);
- if (vp) {
- packet->src_ipaddr.af = AF_INET6;
- memcpy(&packet->src_ipaddr.ipaddr.ip6addr,
- &vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
- }
+#ifdef WITH_ACCOUNTING
+ case PW_ACCOUNTING_RESPONSE:
+ fun = rad_accounting;
+ break;
+#endif
+
+#ifdef WITH_COA
+ case PW_DISCONNECT_ACK:
+ case PW_DISCONNECT_NAK:
+ case PW_COA_ACK:
+ case PW_COA_NAK:
+ fun = rad_coa_reply;
+ break;
+#endif
+
+ default:
+ /*
+ * FIXME: Update MIB for packet types?
+ */
+ radlog(L_ERR, "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)),
+ packet->src_port, packet->id);
+ rad_free(&packet);
+ return 0;
}
- vp = pairfind(packet->vps, PW_PACKET_DST_IP_ADDRESS);
- if (vp) {
- packet->dst_ipaddr.af = AF_INET;
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->lvalue;
- } else {
- vp = pairfind(packet->vps, PW_PACKET_DST_IPV6_ADDRESS);
- if (vp) {
- packet->dst_ipaddr.af = AF_INET6;
- memcpy(&packet->dst_ipaddr.ipaddr.ip6addr,
- &vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
- }
+ request = received_proxy_response(packet);
+ if (!request) {
+ rad_free(&packet);
+ return 0;
}
+#ifdef WITH_COA
/*
- * We've got to give SOME value for Id & ports, so that
- * the packets can be added to the request queue.
- * However, we don't want to keep track of used/unused
- * id's and ports, as that's a lot of work. This hack
- * ensures that (if we have real random numbers), that
- * there will be a collision on every (2^(16+16+2+24))/2
- * packets, on average. That means we can read 2^32 (4G)
- * packets before having a collision, which means it's
- * effectively impossible. Having 4G packets currently
- * being process is ridiculous.
+ * Distinguish proxied CoA requests from ones we
+ * originate.
*/
- packet->id = lrad_rand() & 0xff;
- packet->src_port = lrad_rand() & 0xffff;
- packet->dst_port = lrad_rand() & 0xffff;
+ if ((fun == rad_coa_reply) &&
+ (request->packet->code == request->proxy->code)) {
+ fun = rad_coa_recv;
+ }
+#endif
+
+ rad_assert(fun != NULL);
+ *pfun = fun;
+ *prequest = request;
+
+ return 1;
+}
- packet->dst_ipaddr.af = AF_INET;
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl((INADDR_LOOPBACK & ~0xffffff) | (lrad_rand() & 0xffffff));
+#ifdef WITH_TCP
+/*
+ * Recieve packets from a proxy socket.
+ */
+static int proxy_socket_tcp_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ REQUEST *request;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ listen_socket_t *sock = listener->data;
+ char buffer[128];
- packet->vps = data->vps;
+ packet = fr_tcp_recv(listener->fd, 0);
+ if (!packet) {
+ listener->status = RAD_LISTEN_STATUS_REMOVE_FD;
+ event_new_fd(listener);
+ return 0;
+ }
/*
- * Re-set the state.
+ * FIXME: Client MIB updates?
*/
- data->vps = NULL;
- data->state = STATE_HEADER;
+ switch(packet->code) {
+ case PW_AUTHENTICATION_ACK:
+ case PW_ACCESS_CHALLENGE:
+ case PW_AUTHENTICATION_REJECT:
+ fun = rad_authenticate;
+ break;
+
+#ifdef WITH_ACCOUNTING
+ case PW_ACCOUNTING_RESPONSE:
+ fun = rad_accounting;
+ break;
+#endif
- /*
- * FIXME: many of these checks may not be necessary...
- */
- if (!common_checks(listener, packet, prequest, &detail_client)) {
+ default:
+ /*
+ * FIXME: Update MIB for packet types?
+ */
+ radlog(L_ERR, "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)),
+ packet->src_port, packet->id);
rad_free(&packet);
return 0;
}
+ packet->src_ipaddr = sock->other_ipaddr;
+ packet->src_port = sock->other_port;
+ packet->dst_ipaddr = sock->my_ipaddr;
+ packet->dst_port = sock->my_port;
+
/*
- * Keep track of free slots, as a hack, in an otherwise
- * unused 'int'
+ * FIXME: Have it return an indication of packets that
+ * are OK to ignore (dups, too late), versus ones that
+ * aren't OK to ignore (unknown response, spoofed, etc.)
+ *
+ * Close the socket on bad packets...
*/
- (*prequest)->simul_max = free_slot;
- if (free_slot) data->outstanding[free_slot] = 1;
+ request = received_proxy_response(packet);
+ if (!request) {
+ return 0;
+ }
- *pfun = rad_accounting;
+ rad_assert(fun != NULL);
+ sock->opened = sock->last_packet = request->timestamp;
- if (debug_flag) {
- printf("detail_recv: Read packet from %s\n", data->detail);
- for (vp = packet->vps; vp; vp = vp->next) {
- putchar('\t');
- vp_print(stdout, vp);
- putchar('\n');
- }
- }
+ *pfun = fun;
+ *prequest = request;
return 1;
}
+#endif
+#endif
-/*
- * Free detail-specific stuff.
- */
-static void detail_free(rad_listen_t *this)
+static int client_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
{
- listen_detail_t *data = this->data;
-
- free(data->detail);
- pairfree(&data->vps);
- free(data->outstanding);
-
- if (data->fp != NULL) fclose(data->fp);
-}
+ if (!request->reply->code) return 0;
+ rad_encode(request->reply, request->packet,
+ request->client->secret);
+ rad_sign(request->reply, request->packet,
+ request->client->secret);
-static int detail_print(rad_listen_t *this, char *buffer, size_t bufsize)
-{
- return snprintf(buffer, bufsize, "%s",
- ((listen_detail_t *)(this->data))->detail);
+ return 0;
}
-static const CONF_PARSER detail_config[] = {
- { "detail", PW_TYPE_STRING_PTR,
- offsetof(listen_detail_t, detail), NULL, NULL },
- { "max_outstanding", PW_TYPE_INTEGER,
- offsetof(listen_detail_t, max_outstanding), NULL, "100" },
-
- { NULL, -1, 0, NULL, NULL } /* end the list */
-};
-
-
-/*
- * Parse a detail section.
- */
-static int detail_parse(const char *filename, int lineno,
- const CONF_SECTION *cs, rad_listen_t *this)
+static int client_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
{
- int rcode;
- listen_detail_t *data;
-
- data = this->data;
-
- rcode = cf_section_parse(cs, data, detail_config);
- if (rcode < 0) {
- radlog(L_ERR, "%s[%d]: Failed parsing listen section",
- filename, lineno);
- return -1;
- }
-
- if (!data->detail) {
- radlog(L_ERR, "%s[%d]: No detail file specified in listen section",
- filename, lineno);
+ if (rad_verify(request->packet, NULL,
+ request->client->secret) < 0) {
return -1;
}
-
- data->vps = NULL;
- data->fp = NULL;
- data->state = STATE_UNOPENED;
- if (data->max_outstanding > 32768) data->max_outstanding = 32768;
+ return rad_decode(request->packet, NULL,
+ request->client->secret);
+}
- if (data->max_outstanding > 0) {
- data->outstanding = rad_malloc(sizeof(int) * data->max_outstanding);
- }
-
- detail_open(this);
+#ifdef WITH_PROXY
+static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
+{
+ rad_encode(request->proxy, NULL, request->home_server->secret);
+ rad_sign(request->proxy, NULL, request->home_server->secret);
return 0;
}
-/*
- * See radiusd.c & request_list.c
- */
-#define SLEEP_FOREVER (65536)
-/*
- * A generic "update the request list once a second" function.
- */
-static int generic_update(rad_listen_t *this, time_t now)
+static int proxy_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
{
- if (!this->rl) return SLEEP_FOREVER;
+ /*
+ * rad_verify is run in event.c, received_proxy_response()
+ */
- return rl_clean_list(this->rl, now);
+ return rad_decode(request->proxy_reply, request->proxy,
+ request->home_server->secret);
}
+#endif
+#include "dhcpd.c"
+#include "command.c"
static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = {
- { NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */
+#ifdef WITH_STATS
+ { common_socket_parse, NULL,
+ stats_socket_recv, auth_socket_send,
+ socket_print, client_socket_encode, client_socket_decode },
+#else
+ /*
+ * This always gets defined.
+ */
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */
+#endif
+
+#ifdef WITH_PROXY
+ /* proxying */
+ { common_socket_parse, NULL,
+ proxy_socket_recv, proxy_socket_send,
+ socket_print, proxy_socket_encode, proxy_socket_decode },
+#endif
/* authentication */
{ common_socket_parse, NULL,
auth_socket_recv, auth_socket_send,
- generic_update, socket_print },
+ socket_print, client_socket_encode, client_socket_decode },
+#ifdef WITH_ACCOUNTING
/* accounting */
{ common_socket_parse, NULL,
acct_socket_recv, acct_socket_send,
- generic_update, socket_print},
-
- /* proxying */
- { NULL, NULL,
- proxy_socket_recv, proxy_socket_send,
- generic_update, socket_print }, /* FIXME: update func is wrong! */
+ socket_print, client_socket_encode, client_socket_decode},
+#endif
+#ifdef WITH_DETAIL
/* detail */
{ detail_parse, detail_free,
detail_recv, detail_send,
- generic_update, detail_print }
+ detail_print, detail_encode, detail_decode },
+#endif
+
+#ifdef WITH_VMPS
+ /* vlan query protocol */
+ { common_socket_parse, NULL,
+ vqp_socket_recv, vqp_socket_send,
+ socket_print, vqp_socket_encode, vqp_socket_decode },
+#endif
+
+#ifdef WITH_DHCP
+ /* dhcp query protocol */
+ { dhcp_socket_parse, NULL,
+ dhcp_socket_recv, dhcp_socket_send,
+ socket_print, dhcp_socket_encode, dhcp_socket_decode },
+#endif
+
+#ifdef WITH_COMMAND_SOCKET
+ /* TCP command socket */
+ { command_socket_parse, command_socket_free,
+ command_domain_accept, command_domain_send,
+ command_socket_print, command_socket_encode, command_socket_decode },
+#endif
+
+#ifdef WITH_COA
+ /* Change of Authorization */
+ { common_socket_parse, NULL,
+ coa_socket_recv, auth_socket_send, /* CoA packets are same as auth */
+ socket_print, client_socket_encode, client_socket_decode },
+#endif
+
};
+
/*
* Binds a listener to a socket.
*/
static int listen_bind(rad_listen_t *this)
{
- rad_listen_t **last;
+ int rcode;
+ struct sockaddr_storage salocal;
+ socklen_t salen;
listen_socket_t *sock = this->data;
+#ifndef WITH_TCP
+#define proto_for_port "udp"
+#define sock_type SOCK_DGRAM
+#else
+ const char *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");
+ return -1;
+ }
+#endif
+
+ proto_for_port = "tcp";
+ sock_type = SOCK_STREAM;
+ }
+#endif
/*
* If the port is zero, then it means the appropriate
* thing from /etc/services.
*/
- if (sock->port == 0) {
+ if (sock->my_port == 0) {
struct servent *svp;
switch (this->type) {
case RAD_LISTEN_AUTH:
- svp = getservbyname ("radius", "udp");
+ svp = getservbyname ("radius", proto_for_port);
if (svp != NULL) {
- sock->port = ntohs(svp->s_port);
+ sock->my_port = ntohs(svp->s_port);
} else {
- sock->port = PW_AUTH_UDP_PORT;
+ sock->my_port = PW_AUTH_UDP_PORT;
}
break;
+#ifdef WITH_ACCOUNTING
case RAD_LISTEN_ACCT:
- svp = getservbyname ("radacct", "udp");
+ svp = getservbyname ("radacct", proto_for_port);
+ if (svp != NULL) {
+ sock->my_port = ntohs(svp->s_port);
+ } else {
+ sock->my_port = PW_ACCT_UDP_PORT;
+ }
+ break;
+#endif
+
+#ifdef WITH_PROXY
+ case RAD_LISTEN_PROXY:
+ /* leave it at zero */
+ break;
+#endif
+
+#ifdef WITH_VMPS
+ case RAD_LISTEN_VQP:
+ sock->my_port = 1589;
+ break;
+#endif
+
+#ifdef WITH_COA
+ case RAD_LISTEN_COA:
+ svp = getservbyname ("radius-dynauth", "udp");
if (svp != NULL) {
- sock->port = ntohs(svp->s_port);
+ sock->my_port = ntohs(svp->s_port);
} else {
- sock->port = PW_ACCT_UDP_PORT;
+ sock->my_port = PW_COA_UDP_PORT;
}
break;
+#endif
default:
- radlog(L_ERR|L_CONS, "ERROR: Non-fatal internal sanity check failed in bind.");
+ DEBUG("WARNING: Internal sanity check failed in binding to socket. Ignoring problem.");
return -1;
}
}
/*
- * Find it in the old list, AFTER updating the port. If
- * it's there, use that, rather than creating a new
- * socket. This allows HUP's to re-use the old sockets,
- * which means that packets waiting in the socket queue
- * don't get lost.
+ * Copy fr_socket() here, as we may need to bind to a device.
+ */
+ this->fd = socket(sock->my_ipaddr.af, sock_type, 0);
+ if (this->fd < 0) {
+ radlog(L_ERR, "Failed opening socket: %s", strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Bind to a device BEFORE touching IP addresses.
*/
- for (last = &mainconfig.listen;
- *last != NULL;
- last = &((*last)->next)) {
- if ((this->type == (*last)->type) &&
- (sock->port == ((listen_socket_t *)((*last)->data))->port) &&
- (sock->ipaddr.af == ((listen_socket_t *)((*last)->data))->ipaddr.af)) {
- int equal;
+ if (sock->interface) {
+#ifdef SO_BINDTODEVICE
+ struct ifreq ifreq;
+ strcpy(ifreq.ifr_name, sock->interface);
- if (sock->ipaddr.af == AF_INET) {
- equal = (sock->ipaddr.ipaddr.ip4addr.s_addr == ((listen_socket_t *)((*last)->data))->ipaddr.ipaddr.ip4addr.s_addr);
- } else if (sock->ipaddr.af == AF_INET6) {
- equal = IN6_ARE_ADDR_EQUAL(&(sock->ipaddr.ipaddr.ip6addr), &(((listen_socket_t *)((*last)->data))->ipaddr.ipaddr.ip6addr));
- } else {
- equal = 0;
+ fr_suid_up();
+ rcode = setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *)&ifreq, sizeof(ifreq));
+ fr_suid_down();
+ if (rcode < 0) {
+ close(this->fd);
+ radlog(L_ERR, "Failed binding to interface %s: %s",
+ sock->interface, strerror(errno));
+ return -1;
+ } /* else it worked. */
+#else
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+#ifdef HAVE_NET_IF_H
+ /*
+ * Odds are that any system supporting "bind to
+ * device" also supports IPv6, so this next bit
+ * isn't necessary. But it's here for
+ * completeness.
+ *
+ * If we're doing IPv6, and the scope hasn't yet
+ * been defined, set the scope to the scope of
+ * the interface.
+ */
+ if (sock->my_ipaddr.af == AF_INET6) {
+ if (sock->my_ipaddr.scope == 0) {
+ 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));
+ return -1;
+ }
+ } /* else scope was defined: we're OK. */
+ } else
+#endif
+#endif
+ /*
+ * IPv4: no link local addresses,
+ * and no bind to device.
+ */
+ {
+ close(this->fd);
+ radlog(L_ERR, "Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface);
+ return -1;
+ }
+#endif
+ }
+
+#ifdef WITH_TCP
+ if (sock->proto == IPPROTO_TCP) {
+ int on = 1;
+
+ 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));
+ return -1;
+ }
+ }
+#endif
+
+#if defined(WITH_TCP) && defined(WITH_UDPFROMTO)
+ else /* UDP sockets get UDPfromto */
+#endif
+
+#ifdef WITH_UDPFROMTO
+ /*
+ * Initialize udpfromto for all sockets.
+ */
+ if (udpfromto_init(this->fd) != 0) {
+ close(this->fd);
+ return -1;
+ }
+#endif
+
+ /*
+ * Set up sockaddr stuff.
+ */
+ if (!fr_ipaddr2sockaddr(&sock->my_ipaddr, sock->my_port, &salocal, &salen)) {
+ close(this->fd);
+ return -1;
+ }
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ if (sock->my_ipaddr.af == AF_INET6) {
+ /*
+ * Listening on '::' does NOT get you IPv4 to
+ * IPv6 mapping. You've got to listen on an IPv4
+ * address, too. This makes the rest of the server
+ * design a little simpler.
+ */
+#ifdef IPV6_V6ONLY
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr)) {
+ int on = 1;
+
+ setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&on, sizeof(on));
+ }
+#endif /* IPV6_V6ONLY */
+ }
+#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
+
+ 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
+ * also makes sure that the "don't fragment"
+ * flag is zero.
+ */
+ flag = IP_PMTUDISC_DONT;
+ setsockopt(this->fd, IPPROTO_IP, IP_MTU_DISCOVER,
+ &flag, sizeof(flag));
+#endif
+
+#if defined(IP_DONTFRAG)
+ /*
+ * Ensure that the "don't fragment" flag is zero.
+ */
+ flag = 0;
+ setsockopt(this->fd, IPPROTO_IP, IP_DONTFRAG,
+ &flag, sizeof(flag));
+#endif
+ }
+
+#ifdef WITH_DHCP
+#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\n",
+ strerror(errno));
+ return -1;
+ }
+ }
+#endif
+#endif
+
+ /*
+ * 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\n",
+ 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));
+ 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
+ * happens, notice, and remember our real IP.
+ */
+ {
+ 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));
+ return -1;
}
- if (equal) {
- this->rl = (*last)->rl;
- this->fd = (*last)->fd;
- (*last)->fd = -1;
- (*last)->rl = NULL;
- return 0;
+ if (!fr_sockaddr2ipaddr(&src, sizeof_src,
+ &sock->my_ipaddr, &sock->my_port)) {
+ radlog(L_ERR, "Socket has unsupported address family");
+ return -1;
}
}
}
- this->fd = lrad_socket(&sock->ipaddr, sock->port);
- if (this->fd < 0) {
- radlog(L_ERR|L_CONS, "ERROR: Failed to open socket: %s",
- librad_errstr);
- return -1;
- }
+#ifdef WITH_TCP
+ if (sock->proto == IPPROTO_TCP) {
+ if (listen(this->fd, 8) < 0) {
+ close(this->fd);
+ radlog(L_ERR, "Failed in listen(): %s", strerror(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.
+ */
+ sock->other_ipaddr.af = sock->my_ipaddr.af;
+
+/*
+ * Don't screw up other people.
+ */
+#undef proto_for_port
+#undef sock_type
return 0;
}
this->type = type;
this->recv = master_listen[this->type].recv;
this->send = master_listen[this->type].send;
- this->update = master_listen[this->type].update;
this->print = master_listen[this->type].print;
+ this->encode = master_listen[this->type].encode;
+ this->decode = master_listen[this->type].decode;
switch (type) {
+#ifdef WITH_STATS
+ case RAD_LISTEN_NONE:
+#endif
case RAD_LISTEN_AUTH:
+#ifdef WITH_ACCOUNTING
case RAD_LISTEN_ACCT:
+#endif
+#ifdef WITH_PROXY
case RAD_LISTEN_PROXY:
+#endif
+#ifdef WITH_VMPS
+ case RAD_LISTEN_VQP:
+#endif
+#ifdef WITH_COA
+ case RAD_LISTEN_COA:
+#endif
this->data = rad_malloc(sizeof(listen_socket_t));
memset(this->data, 0, sizeof(listen_socket_t));
break;
+#ifdef WITH_DHCP
+ case RAD_LISTEN_DHCP:
+ this->data = rad_malloc(sizeof(dhcp_socket_t));
+ memset(this->data, 0, sizeof(dhcp_socket_t));
+ break;
+#endif
+
+#ifdef WITH_DETAIL
case RAD_LISTEN_DETAIL:
- this->data = rad_malloc(sizeof(listen_detail_t));
- memset(this->data, 0, sizeof(listen_detail_t));
+ this->data = NULL;
+ break;
+#endif
+
+#ifdef WITH_COMMAND_SOCKET
+ case RAD_LISTEN_COMMAND:
+ this->data = rad_malloc(sizeof(fr_command_socket_t));
+ memset(this->data, 0, sizeof(fr_command_socket_t));
+ break;
+#endif
+
+ default:
+ rad_assert("Unsupported option!" == NULL);
+ break;
+ }
+
+ return this;
+}
+
+#ifdef WITH_PROXY
+/*
+ * Externally visible function for creating a new proxy LISTENER.
+ *
+ * Not thread-safe, but all calls to it are protected by the
+ * proxy mutex in event.c
+ */
+int proxy_new_listener(home_server *home, int src_port)
+{
+ rad_listen_t *this;
+ listen_socket_t *sock;
+
+ if (!home) return 0;
+
+ if ((home->max_connections > 0) &&
+ (home->num_connections >= home->max_connections)) {
+ DEBUG("WARNING: Home server has too many open connections (%d)",
+ home->max_connections);
+ return 0;
+ }
+
+ this = listen_alloc(RAD_LISTEN_PROXY);
+
+ sock = this->data;
+ sock->other_ipaddr = home->ipaddr;
+ sock->other_port = home->port;
+ sock->home = home;
+
+ sock->my_ipaddr = home->src_ipaddr;
+ sock->my_port = src_port;
+ sock->proto = home->proto;
+
+#ifdef WITH_TCP
+ sock->last_packet = time(NULL);
+
+ if (home->proto == IPPROTO_TCP) {
+ this->recv = proxy_socket_tcp_recv;
+
+ /*
+ * FIXME: connect() is blocking!
+ * We do this with the proxy mutex locked, which may
+ * cause large delays!
+ *
+ * http://www.developerweb.net/forum/showthread.php?p=13486
+ */
+ this->fd = fr_tcp_client_socket(&home->src_ipaddr,
+ &home->ipaddr, home->port);
+ } else
+#endif
+ this->fd = fr_socket(&home->src_ipaddr, src_port);
+
+ if (this->fd < 0) {
+ DEBUG("Failed opening client socket: %s", fr_strerror());
+ listen_free(&this);
+ return 0;
+ }
+
+ /*
+ * Figure out which port we were bound to.
+ */
+ if (sock->my_port == 0) {
+ struct sockaddr_storage src;
+ socklen_t sizeof_src = sizeof(src);
- default:
- break;
+ 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));
+ listen_free(&this);
+ return 0;
+ }
+
+ if (!fr_sockaddr2ipaddr(&src, sizeof_src,
+ &sock->my_ipaddr, &sock->my_port)) {
+ radlog(L_ERR, "Socket has unsupported address family");
+ listen_free(&this);
+ return 0;
+ }
}
- return this;
+ /*
+ * Tell the event loop that we have a new FD
+ */
+ if (!event_new_fd(this)) {
+ listen_free(&this);
+ return 0;
+ }
+
+ return 1;
}
+#endif
-/*
- * Externally visible function for creating a new proxy LISTENER.
- *
- * For now, don't take ipaddr or port.
- *
- * Not thread-safe, but all calls to it are protected by the
- * proxy mutex in request_list.c
- */
-rad_listen_t *proxy_new_listener()
+static const FR_NAME_NUMBER listen_compare[] = {
+#ifdef WITH_STATS
+ { "status", RAD_LISTEN_NONE },
+#endif
+ { "auth", RAD_LISTEN_AUTH },
+#ifdef WITH_ACCOUNTING
+ { "acct", RAD_LISTEN_ACCT },
+#endif
+#ifdef WITH_DETAIL
+ { "detail", RAD_LISTEN_DETAIL },
+#endif
+#ifdef WITH_PROXY
+ { "proxy", RAD_LISTEN_PROXY },
+#endif
+#ifdef WITH_VMPS
+ { "vmps", RAD_LISTEN_VQP },
+#endif
+#ifdef WITH_DHCP
+ { "dhcp", RAD_LISTEN_DHCP },
+#endif
+#ifdef WITH_COMMAND_SOCKET
+ { "control", RAD_LISTEN_COMMAND },
+#endif
+#ifdef WITH_COA
+ { "coa", RAD_LISTEN_COA },
+#endif
+ { NULL, 0 },
+};
+
+
+static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server)
{
- int last_proxy_port, port;
- rad_listen_t *this, *tmp, **last;
- listen_socket_t *sock, *old;
+ int type, rcode;
+ char *listen_type;
+ rad_listen_t *this;
- this = listen_alloc(RAD_LISTEN_PROXY);
+ listen_type = NULL;
+
+ cf_log_info(cs, "listen {");
+
+ rcode = cf_item_parse(cs, "type", PW_TYPE_STRING_PTR,
+ &listen_type, "");
+ if (rcode < 0) return NULL;
+ if (rcode == 1) {
+ free(listen_type);
+ cf_log_err(cf_sectiontoitem(cs),
+ "No type specified in listen section");
+ return NULL;
+ }
+
+ type = fr_str2int(listen_compare, listen_type, -1);
+ if (type < 0) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "Invalid type \"%s\" in listen section.",
+ listen_type);
+ free(listen_type);
+ return NULL;
+ }
+ free(listen_type);
/*
- * Find an existing proxy socket to copy.
- *
- * FIXME: Make it per-realm, or per-home server!
- */
- last_proxy_port = 0;
- old = NULL;
- last = &mainconfig.listen;
- for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) {
- if (tmp->type == RAD_LISTEN_PROXY) {
- sock = tmp->data;
- if (sock->port > last_proxy_port) {
- last_proxy_port = sock->port + 1;
- }
- if (!old) old = sock;
+ * Allow listen sections in the default config to
+ * 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);
}
-
- last = &(tmp->next);
+ if (rcode < 0) return NULL;
}
- if (!old) return NULL; /* This is a serious error. */
+#ifdef WITH_PROXY
+ /*
+ * We were passed a virtual server, so the caller is
+ * defining a proxy listener inside of a virtual server.
+ * 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");
+ return NULL;
+ }
+#endif
/*
- * FIXME: find a new IP address to listen on?
+ * Set up cross-type data.
*/
- sock = this->data;
- memcpy(&sock->ipaddr, &old->ipaddr, sizeof(sock->ipaddr));
+ this = listen_alloc(type);
+ this->server = server;
+ this->fd = -1;
/*
- * Keep going until we find an unused port.
+ * Call per-type parser.
*/
- for (port = last_proxy_port; port < 64000; port++) {
- sock->port = port;
- if (listen_bind(this) == 0) {
- /*
- * Add the new listener to the list of
- * listeners.
- */
- *last = this;
- return this;
- }
+ if (master_listen[type].parse(cs, this) < 0) {
+ listen_free(&this);
+ return NULL;
}
- return NULL;
-}
+ cf_log_info(cs, "}");
+ return this;
+}
-static const LRAD_NAME_NUMBER listen_compare[] = {
- { "auth", RAD_LISTEN_AUTH },
- { "acct", RAD_LISTEN_ACCT },
- { "detail", RAD_LISTEN_DETAIL },
- { NULL, 0 },
-};
+#ifdef WITH_PROXY
+static int is_loopback(const fr_ipaddr_t *ipaddr)
+{
+ /*
+ * 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;
+ }
+#endif
+ return 0;
+}
+#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(const char *filename, rad_listen_t **head)
+int listen_init(CONF_SECTION *config, rad_listen_t **head)
{
+ int override = FALSE;
int rcode;
- CONF_SECTION *cs;
+ CONF_SECTION *cs = NULL;
rad_listen_t **last;
rad_listen_t *this;
- lrad_ipaddr_t server_ipaddr;
+ fr_ipaddr_t server_ipaddr;
int auth_port = 0;
+#ifdef WITH_PROXY
+ int defined_proxy = 0;
+#endif
/*
* We shouldn't be called with a pre-existing list.
*/
rad_assert(head && (*head == NULL));
-
- if (start_time != 0) start_time = time(NULL);
last = head;
server_ipaddr.af = AF_UNSPEC;
/*
* If the port is specified on the command-line,
* it over-rides the configuration file.
+ *
+ * FIXME: If argv[0] == "vmpsd", then don't listen on auth/acct!
*/
- if (mainconfig.port >= 0) {
- auth_port = mainconfig.port;
- } else {
- rcode = cf_item_parse(mainconfig.config, "port",
- PW_TYPE_INTEGER, &auth_port,
- Stringify(PW_AUTH_UDP_PORT));
- if (rcode < 0) return -1; /* error parsing it */
-
- if (rcode == 0)
- radlog(L_INFO, "WARNING: The directive 'port' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
- }
+ if (mainconfig.port >= 0) auth_port = mainconfig.port;
/*
* If the IP address was configured on the command-line,
if (mainconfig.myip.af != AF_UNSPEC) {
memcpy(&server_ipaddr, &mainconfig.myip,
sizeof(server_ipaddr));
+ override = TRUE;
goto bind_it;
}
* Else look for bind_address and/or listen sections.
*/
server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- rcode = cf_item_parse(mainconfig.config, "bind_address",
+ rcode = cf_item_parse(config, "bind_address",
PW_TYPE_IPADDR,
&server_ipaddr.ipaddr.ip4addr, NULL);
if (rcode < 0) return -1; /* error parsing it */
-
+
if (rcode == 0) { /* successfully parsed IPv4 */
listen_socket_t *sock;
server_ipaddr.af = AF_INET;
- radlog(L_INFO, "WARNING: The directive 'bind_adress' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
+ radlog(L_INFO, "WARNING: The directive 'bind_address' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
bind_it:
- this = listen_alloc(RAD_LISTEN_AUTH);
+#ifdef WITH_VMPS
+ if (strcmp(progname, "vmpsd") == 0) {
+ this = listen_alloc(RAD_LISTEN_VQP);
+ if (!auth_port) auth_port = 1589;
+ } else
+#endif
+ this = listen_alloc(RAD_LISTEN_AUTH);
+
sock = this->data;
- sock->ipaddr = server_ipaddr;
- sock->port = auth_port;
-
- if (listen_bind(this) < 0) {
+ sock->my_ipaddr = server_ipaddr;
+ sock->my_port = auth_port;
+
+ sock->clients = clients_parse_section(config);
+ if (!sock->clients) {
+ cf_log_err(cf_sectiontoitem(config),
+ "Failed to find any clients for this listen section");
listen_free(&this);
+ return -1;
+ }
+
+ if (listen_bind(this) < 0) {
listen_free(head);
- radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->port);
+ radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->my_port);
+ listen_free(&this);
return -1;
}
- auth_port = sock->port; /* may have been updated in listen_bind */
+ 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;
+ }
+
*last = this;
last = &(this->next);
-
+
+#ifdef WITH_VMPS
+ /*
+ * No acct for vmpsd
+ */
+ if (strcmp(progname, "vmpsd") == 0) goto add_sockets;
+#endif
+
+#ifdef WITH_ACCOUNTING
/*
* Open Accounting Socket.
*
*/
this = listen_alloc(RAD_LISTEN_ACCT);
sock = this->data;
-
+
/*
* Create the accounting socket.
*
* The accounting port is always the
* authentication port + 1
*/
- sock->ipaddr = server_ipaddr;
- sock->port = auth_port + 1;
-
+ sock->my_ipaddr = server_ipaddr;
+ sock->my_port = auth_port + 1;
+
+ sock->clients = clients_parse_section(config);
+ if (!sock->clients) {
+ cf_log_err(cf_sectiontoitem(config),
+ "Failed to find any clients for this listen section");
+ return -1;
+ }
+
if (listen_bind(this) < 0) {
listen_free(&this);
listen_free(head);
- radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->port);
+ radlog(L_ERR, "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;
+ }
+
*last = this;
last = &(this->next);
-
+#endif
} else if (mainconfig.port > 0) { /* no bind address, but a port */
- radlog(L_CONS|L_ERR, "The command-line says \"-p %d\", but there is no associated IP address to use",
+ radlog(L_ERR, "The command-line says \"-p %d\", but there is no associated IP address to use",
mainconfig.port);
return -1;
}
/*
* They specified an IP on the command-line, ignore
- * all listen sections.
+ * all listen sections except the one in '-n'.
*/
- if (mainconfig.myip.af != AF_UNSPEC) goto do_proxy;
+ if (mainconfig.myip.af != AF_UNSPEC) {
+ CONF_SECTION *subcs;
+ const char *name2 = cf_section_name2(cs);
+
+ cs = cf_section_sub_find_name2(config, "server",
+ mainconfig.name);
+ if (!cs) goto add_sockets;
+
+ /*
+ * Should really abstract this code...
+ */
+ for (subcs = cf_subsection_find_next(cs, NULL, "listen");
+ subcs != NULL;
+ subcs = cf_subsection_find_next(cs, subcs, "listen")) {
+ this = listen_parse(subcs, name2);
+ if (!this) {
+ listen_free(head);
+ return -1;
+ }
+
+ *last = this;
+ last = &(this->next);
+ } /* loop over "listen" directives in server <foo> */
+
+ goto add_sockets;
+ }
/*
* Walk through the "listen" sections, if they exist.
*/
- for (cs = cf_subsection_find_next(mainconfig.config, NULL, "listen");
+ for (cs = cf_subsection_find_next(config, NULL, "listen");
cs != NULL;
- cs = cf_subsection_find_next(mainconfig.config, cs, "listen")) {
- int type;
- char *listen_type, *identity;
- int lineno = cf_section_lineno(cs);
-
- listen_type = identity = NULL;
-
- rcode = cf_item_parse(cs, "type", PW_TYPE_STRING_PTR,
- &listen_type, "");
- if (rcode < 0) return -1;
- if (rcode == 1) {
- listen_free(head);
- free(listen_type);
- radlog(L_ERR, "%s[%d]: No type specified in listen section",
- filename, lineno);
- return -1;
- }
-
- /*
- * See if there's an identity.
- */
- rcode = cf_item_parse(cs, "identity", PW_TYPE_STRING_PTR,
- &identity, NULL);
- if (rcode < 0) {
+ cs = cf_subsection_find_next(config, cs, "listen")) {
+ this = listen_parse(cs, NULL);
+ if (!this) {
listen_free(head);
- free(identity);
return -1;
}
- type = lrad_str2int(listen_compare, listen_type,
- RAD_LISTEN_NONE);
- free(listen_type);
- if (type == RAD_LISTEN_NONE) {
- listen_free(head);
- radlog(L_CONS|L_ERR, "%s[%d]: Invalid type in listen section.",
- filename, lineno);
- return -1;
- }
+ *last = this;
+ last = &(this->next);
+ }
- /*
- * Set up cross-type data.
- */
- this = listen_alloc(type);
- this->identity = identity;
- this->fd = -1;
+ /*
+ * Check virtual servers for "listen" sections, too.
+ *
+ * FIXME: Move to virtual server init?
+ */
+ for (cs = cf_subsection_find_next(config, NULL, "server");
+ cs != NULL;
+ cs = cf_subsection_find_next(config, cs, "server")) {
+ CONF_SECTION *subcs;
+ const char *name2 = cf_section_name2(cs);
- /*
- * Call per-type parser.
- */
- if (master_listen[type].parse(filename, lineno,
- cs, this) < 0) {
- listen_free(&this);
- listen_free(head);
- return -1;
+ for (subcs = cf_subsection_find_next(cs, NULL, "listen");
+ subcs != NULL;
+ subcs = cf_subsection_find_next(cs, subcs, "listen")) {
+ this = listen_parse(subcs, name2);
+ if (!this) {
+ listen_free(head);
+ return -1;
+ }
+
+ *last = this;
+ last = &(this->next);
+ } /* loop over "listen" directives in virtual servers */
+ } /* loop over virtual servers */
+
+add_sockets:
+ /*
+ * No sockets to receive packets, this is an error.
+ * proxying is pointless.
+ */
+ if (!*head) {
+ radlog(L_ERR, "The server is not configured to listen on any ports. Cannot start.");
+ return -1;
+ }
+
+ /*
+ * Print out which sockets we're listening on, and
+ * add them to the event list.
+ */
+ for (this = *head; this != NULL; this = this->next) {
+#ifdef WITH_PROXY
+ if (this->type == RAD_LISTEN_PROXY) {
+ defined_proxy = 1;
}
- *last = this;
- last = &(this->next);
+#endif
+ event_new_fd(this);
}
/*
* If we're proxying requests, open the proxy FD.
* Otherwise, don't do anything.
*/
- do_proxy:
- if (mainconfig.proxy_requests == TRUE) {
- int port = -1;
+#ifdef WITH_PROXY
+ if ((mainconfig.proxy_requests == TRUE) &&
+ (*head != NULL) && !defined_proxy) {
listen_socket_t *sock = NULL;
+ int port = 0;
+ home_server home;
- /*
- * No sockets to receive packets, therefore
- * proxying is pointless.
- */
- if (!*head) return -1;
+ memset(&home, 0, sizeof(home));
/*
- * If we previously had proxy sockets, copy them
- * to the new config.
+ *
*/
- if (mainconfig.listen != NULL) {
- rad_listen_t *old, *next, **tail;
-
- tail = &mainconfig.listen;
- for (old = mainconfig.listen;
- old != NULL;
- old = next) {
- next = old->next;
-
- if (old->type != RAD_LISTEN_PROXY) {
- tail = &((*tail)->next);
- continue;
- }
-
- *last = old;
- *tail = next;
- old->next = NULL;
- last = &(old->next);
- }
-
- goto done;
- }
+ home.proto = IPPROTO_UDP;
+ home.src_ipaddr = server_ipaddr;
/*
* Find the first authentication port,
for (this = *head; this != NULL; this = this->next) {
if (this->type == RAD_LISTEN_AUTH) {
sock = this->data;
- if (server_ipaddr.af == AF_UNSPEC) {
- server_ipaddr = sock->ipaddr;
+
+ 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->port + 2; /* skip acct port */
+ port = sock->my_port + 1;
break;
}
+#endif
}
- rad_assert(port > 0); /* must have found at least one entry! */
/*
* Address is still unspecified, use IPv4.
*/
- if (server_ipaddr.af == AF_UNSPEC) {
- server_ipaddr.af = AF_INET;
- server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
+ if (home.src_ipaddr.af == AF_UNSPEC) {
+ home.src_ipaddr.af = AF_INET;
+ /* everything else is already set to zero */
}
- this = listen_alloc(RAD_LISTEN_PROXY);
- sock = this->data;
-
- /*
- * Create the first proxy socket.
- */
- sock->ipaddr = server_ipaddr;
-
- /*
- * Try to find a proxy port (value doesn't matter)
- */
- for (sock->port = port;
- sock->port < 64000;
- sock->port++) {
- if (listen_bind(this) == 0) {
- *last = this;
- last = &(this->next); /* just in case */
- break;
- }
- }
+ home.ipaddr.af = home.src_ipaddr.af;
+ /* everything else is already set to zero */
- if (sock->port >= 64000) {
+ if (!proxy_new_listener(&home, port)) {
listen_free(head);
- listen_free(&this);
- radlog(L_ERR|L_CONS, "Failed to open socket for proxying");
return -1;
}
}
+#endif
/*
- * Sanity check the configuration.
+ * Haven't defined any sockets. Die.
*/
- rcode = 0;
- for (this = *head; this != NULL; this = this->next) {
- if ((this->type != RAD_LISTEN_PROXY) &&
- !this->rl) {
- /*
- * FIXME: Pass type to rl_init, so that
- * it knows how to deal with accounting
- * packets. i.e. it caches them, but
- * doesn't bother trying to re-transmit.
- */
- this->rl = rl_init();
- if (!this->rl) {
- rad_assert(0 == 1); /* FIXME: */
- }
- }
+ if (!*head) return -1;
- if (((this->type == RAD_LISTEN_ACCT) &&
- (rcode == RAD_LISTEN_DETAIL)) ||
- ((this->type == RAD_LISTEN_DETAIL) &&
- (rcode == RAD_LISTEN_ACCT))) {
- rad_assert(0 == 1); /* FIXME: configuration error */
- }
-
- if (rcode != 0) continue;
-
- if ((this->type == RAD_LISTEN_ACCT) ||
- (this->type == RAD_LISTEN_DETAIL)) {
- rcode = this->type;
- }
- }
- done:
return 0;
}
this = *head;
while (this) {
rad_listen_t *next = this->next;
-
- free(this->identity);
-
- rl_deinit(this->rl);
/*
* Other code may have eaten the FD.
if (master_listen[this->type].free) {
master_listen[this->type].free(this);
}
+
+#ifdef WITH_TCP
+ if ((this->type == RAD_LISTEN_AUTH)
+#ifdef WITH_ACCT
+ || (this->type == RAD_LISTEN_ACCT)
+#endif
+#ifdef WITH_PROXY
+ || (this->type == RAD_LISTEN_PROXY)
+#endif
+ ) {
+ listen_socket_t *sock = this->data;
+ rad_free(&sock->packet);
+ }
+#endif
+
free(this->data);
free(this);
-
+
this = next;
}
*head = NULL;
}
+
+#ifdef WITH_STATS
+RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
+ int port)
+{
+ rad_listen_t *this;
+
+ for (this = mainconfig.listen; this != NULL; this = this->next) {
+ listen_socket_t *sock;
+
+ if ((this->type != RAD_LISTEN_AUTH)
+#ifdef WITH_ACCOUNTING
+ && (this->type != RAD_LISTEN_ACCT)
+#endif
+ ) continue;
+
+ sock = this->data;
+
+ if ((sock->my_port == port) &&
+ (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
+ return sock->clients;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port)
+{
+ rad_listen_t *this;
+
+ for (this = mainconfig.listen; this != NULL; this = this->next) {
+ listen_socket_t *sock;
+
+ /*
+ * FIXME: For TCP, ignore the *secondary*
+ * listeners associated with the main socket.
+ */
+ if ((this->type != RAD_LISTEN_AUTH)
+#ifdef WITH_ACCOUNTING
+ && (this->type != RAD_LISTEN_ACCT)
+#endif
+ ) continue;
+
+ sock = this->data;
+
+ if ((sock->my_port == port) &&
+ (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
+ return this;
+ }
+
+ if ((sock->my_port == port) &&
+ fr_inaddr_any(&sock->my_ipaddr)) {
+ return this;
+ }
+ }
+
+ return NULL;
+}