#include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/vqp.h>
#include <freeradius-devel/dhcp.h>
+#include <freeradius-devel/process.h>
#include <freeradius-devel/vmps.h>
#include <freeradius-devel/detail.h>
#endif
+static void print_packet(RADIUS_PACKET *packet)
+{
+ char src[256], dst[256];
+
+ ip_ntoh(&packet->src_ipaddr, src, sizeof(src));
+ ip_ntoh(&packet->dst_ipaddr, dst, sizeof(dst));
+
+ fprintf(stderr, "ID %d: %s %d -> %s %d\n", packet->id,
+ src, packet->src_port, dst, packet->dst_port);
+
+}
+
+
/*
* We'll use this below.
*/
rad_listen_decode_t decode;
} rad_listen_master_t;
-typedef struct listen_socket_t {
- /*
- * For normal sockets.
- */
- fr_ipaddr_t ipaddr;
- int port;
+static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
-#ifdef SO_BINDTODEVICE
- const char *interface;
-#endif
-
-#ifdef WITH_TCP
- int proto;
+/*
+ * Xlat for %{listen:foo}
+ */
+static size_t xlat_listen(UNUSED void *instance, REQUEST *request,
+ char *fmt, char *out,
+ size_t outlen,
+ UNUSED RADIUS_ESCAPE_STRING func)
+{
+ const char *value = NULL;
+ CONF_PAIR *cp;
- int max_connections;
- int num_connections;
- struct listen_socket_t *parent;
+ if (!fmt || !out || (outlen < 1)) return 0;
- fr_ipaddr_t src_ipaddr;
- int src_port;
- RADCLIENT *client; /* for server sockets */
+ if (!request || !request->listener) {
+ *out = '\0';
+ return 0;
+ }
- fr_tcp_radius_t *tcp; /* for RAD_LISTEN_PROXY */
- home_server *home;
- RADIUS_PACKET *packet; /* for reading partial packets */
-#endif
- RADCLIENT_LIST *clients;
-} listen_socket_t;
+ cp = cf_pair_find(request->listener->cs, fmt);
+ if (!cp || !(value = cf_pair_value(cp))) {
+ *out = '\0';
+ return 0;
+ }
+
+ strlcpy(out, value, outlen);
-static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
+ return strlen(out);
+}
/*
* Find a per-socket client.
*/
-RADCLIENT *client_listener_find(const rad_listen_t *listener,
+RADCLIENT *client_listener_find(rad_listen_t *listener,
const fr_ipaddr_t *ipaddr, int src_port)
{
#ifdef WITH_DYNAMIC_CLIENTS
time_t now;
RADCLIENT *client;
RADCLIENT_LIST *clients;
+ listen_socket_t *sock;
rad_assert(listener != NULL);
rad_assert(ipaddr != NULL);
- clients = ((listen_socket_t *)listener->data)->clients;
+ sock = listener->data;
+ clients = sock->clients;
/*
* This HAS to have been initialized previously.
*/
rad_assert(clients != NULL);
- client = client_find(clients, ipaddr
-#ifdef WITH_TCP
- , IPPROTO_UDP
-#endif
- );
+ client = client_find(clients, ipaddr,sock->proto);
if (!client) {
- static time_t last_printed = 0;
char name[256], buffer[128];
#ifdef WITH_DYNAMIC_CLIENTS
#endif
/*
- * DoS attack quenching, but only in debug mode.
+ * 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;
+
now = time(NULL);
if (last_printed == now) return NULL;
listener->print(listener, name, sizeof(name));
- radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d",
- name, inet_ntop(ipaddr->af, &ipaddr->ipaddr,
- buffer, sizeof(buffer)),
- src_port);
+ 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;
}
/*
* Go find the enclosing network again.
*/
- client = client_find(clients, ipaddr
-#ifdef WITH_TCP
- , IPPROTO_UDP
-#endif
- );
+ client = client_find(clients, ipaddr, sock->proto);
/*
* WTF?
* can be defined.
*/
rad_assert(client->dynamic == 0);
- } else {
+
+ } else if (!client->dynamic && client->rate_limit) {
/*
* The IP is unknown, so we've found an enclosing
* network. Enable DoS protection. We only
request_free(&request);
goto unknown;
}
- request->packet->timestamp = request->timestamp;
+ gettimeofday(&request->packet->timestamp, NULL);
request->number = 0;
request->priority = listener->type;
request->server = client->client_server;
*/
if (!client_validate(clients, client, created)) goto unknown;
}
+
+ request->server = client->server;
+ exec_trigger(request, NULL, "server.client.add");
+
request_free(&request);
if (!created) goto unknown;
* 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)
+int rad_status_server(REQUEST *request)
{
int rcode = RLM_MODULE_OK;
DICT_VALUE *dval;
case RAD_LISTEN_NONE:
#endif
case RAD_LISTEN_AUTH:
- dval = dict_valbyname(PW_AUTZ_TYPE, "Status-Server");
+ dval = dict_valbyname(PW_AUTZ_TYPE, 0, "Status-Server");
if (dval) {
rcode = module_authorize(dval->value, request);
} else {
#ifdef WITH_ACCOUNTING
case RAD_LISTEN_ACCT:
- dval = dict_valbyname(PW_ACCT_TYPE, "Status-Server");
+ dval = dict_valbyname(PW_ACCT_TYPE, 0, "Status-Server");
if (dval) {
rcode = module_accounting(dval->value, request);
} else {
* the WG. We like it, so it goes in here.
*/
case RAD_LISTEN_COA:
- dval = dict_valbyname(PW_RECV_COA_TYPE, "Status-Server");
+ dval = dict_valbyname(PW_RECV_COA_TYPE, 0, "Status-Server");
if (dval) {
rcode = module_recv_coa(dval->value, request);
} else {
return 0;
}
+#ifdef WITH_TCP
+static int dual_tcp_recv(rad_listen_t *listener)
+{
+ int rcode;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ listen_socket_t *sock = listener->data;
+ RADCLIENT *client = sock->client;
+
+ /*
+ * Allocate a packet for partial reads.
+ */
+ 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);
+
+ /*
+ * 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;
+ }
+
+ 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);
+ }
+
+ 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--;
+ }
+
+ /*
+ * Tell the event handler that an FD has disappeared.
+ */
+ DEBUG("Client has closed connection");
+ event_new_fd(listener);
+
+ /*
+ * 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;
+ }
+
+ /*
+ * Some sanity checks, based on the packet code.
+ */
+ switch(packet->code) {
+ case PW_AUTHENTICATION_REQUEST:
+ if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
+ FR_STATS_INC(auth, total_requests);
+ fun = rad_authenticate;
+ break;
+
+#ifdef WITH_ACCOUNTING
+ case PW_ACCOUNTING_REQUEST:
+ if (listener->type != RAD_LISTEN_ACCT) goto bad_packet;
+ FR_STATS_INC(acct, total_requests);
+ fun = rad_accounting;
+ break;
+#endif
+
+ case PW_STATUS_SERVER:
+ if (!mainconfig.status_server) {
+ FR_STATS_INC(auth, total_unknown_types);
+ DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
+ rad_free(&sock->packet);
+ return 0;
+ }
+ fun = rad_status_server;
+ break;
+
+ default:
+ bad_packet:
+ FR_STATS_INC(auth, total_unknown_types);
+
+ DEBUG("Invalid packet code %d sent from client %s port %d : IGNORED",
+ packet->code, client->shortname, packet->src_port);
+ rad_free(&sock->packet);
+ return 0;
+ } /* switch over packet types */
+
+ if (!request_receive(listener, packet, client, fun)) {
+ FR_STATS_INC(auth, total_packets_dropped);
+ rad_free(&sock->packet);
+ return 0;
+ }
+
+ sock->packet = NULL; /* we have no need for more partial reads */
+ return 1;
+}
+
+static int dual_tcp_accept(rad_listen_t *listener)
+{
+ 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 = NULL;
+
+ salen = sizeof(src);
+
+ DEBUG2(" ... new connection request on TCP socket.");
+
+ newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
+ if (newfd < 0) {
+ /*
+ * Non-blocking sockets must handle this.
+ */
+#ifdef EWOULDBLOCK
+ if (errno == EWOULDBLOCK) {
+ return 0;
+ }
+#endif
+
+ DEBUG2(" ... failed to accept connection.");
+ return -1;
+ }
+
+ if (!fr_sockaddr2ipaddr(&src, salen, &src_ipaddr, &src_port)) {
+ DEBUG2(" ... unknown address family.");
+ return 0;
+ }
+
+ /*
+ * Enforce client IP address checks on accept, not on
+ * every packet.
+ */
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ close(newfd);
+ FR_STATS_INC(auth, total_invalid_requests);
+ return 0;
+ }
+
+ /*
+ * Enforce max_connectionsx on client && listen section.
+ */
+ if ((client->max_connections != 0) &&
+ (client->max_connections == client->num_connections)) {
+ /*
+ * FIXME: Print client IP/port, and server IP/port.
+ */
+ radlog(L_INFO, "Ignoring new connection due to client max_connections (%d)", client->max_connections);
+ close(newfd);
+ return 0;
+ }
+
+ 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++;
+
+ /*
+ * Add the new listener.
+ */
+ this = listen_alloc(listener->type);
+ if (!this) return -1;
+
+ /*
+ * Copy everything, including the pointer to the socket
+ * information.
+ */
+ sock = this->data;
+ memcpy(this->data, listener->data, sizeof(*sock));
+ memcpy(this, listener, sizeof(*this));
+ this->next = NULL;
+ this->data = sock; /* fix it back */
+
+ sock->parent = listener->data;
+ sock->other_ipaddr = src_ipaddr;
+ sock->other_port = src_port;
+ sock->client = client;
+ sock->opened = sock->last_packet = time(NULL);
+
+ this->fd = newfd;
+ this->status = RAD_LISTEN_STATUS_INIT;
+ this->recv = dual_tcp_recv;
+
+#ifdef WITH_TLS
+ if (this->tls) {
+ this->recv = dual_tls_recv;
+ this->send = dual_tls_send;
+ }
+#endif
+
+ /*
+ * FIXME: set O_NONBLOCK on the accept'd fd.
+ * See djb's portability rants for details.
+ */
+
+ /*
+ * Tell the event loop that we have a new FD.
+ * This can be called from a child thread...
+ */
+ event_new_fd(this);
+
+ return 0;
+}
+#endif
+
-static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
+/*
+ * This function is stupid and complicated.
+ */
+static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
{
size_t len;
listen_socket_t *sock = this->data;
ADDSTRING(name);
-#ifdef SO_BINDTODEVICE
if (sock->interface) {
ADDSTRING(" interface ");
ADDSTRING(sock->interface);
}
+
+#ifdef WITH_TCP
+ if (this->recv == dual_tcp_accept) {
+ ADDSTRING(" proto tcp");
+ }
#endif
+#ifdef WITH_TCP
+ /*
+ * TCP sockets get printed a little differently, to make
+ * it clear what's going on.
+ */
+ 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;
+
+ ADDSTRING(", ");
+ snprintf(buffer, bufsize, "%d", sock->my_port);
+ FORWARD;
+
+ if (this->server) {
+ ADDSTRING(", virtual-server=");
+ ADDSTRING(this->server);
+ }
+
+ ADDSTRING(")");
+
+ return 1;
+ }
+
+#ifdef WITH_PROXY
+ /*
+ * Maybe it's a socket that we opened to a home server.
+ */
+ 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->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+ if ((sock->my_ipaddr.af == AF_INET) &&
+ (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
strlcpy(buffer, "*", bufsize);
} else {
- ip_ntoh(&sock->ipaddr, buffer, bufsize);
+ ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
}
FORWARD;
ADDSTRING(" port ");
- snprintf(buffer, bufsize, "%d", sock->port);
+ snprintf(buffer, bufsize, "%d", sock->my_port);
FORWARD;
+#ifdef WITH_TLS
+ if (this->tls) {
+ ADDSTRING(" (TLS)");
+ FORWARD;
+ }
+#endif
+
if (this->server) {
ADDSTRING(" as server ");
ADDSTRING(this->server);
return 1;
}
+extern int check_config; /* radiusd.c */
+
/*
* Parse an authentication or accounting socket.
char *section_name = NULL;
CONF_SECTION *client_cs, *parentcs;
+ this->cs = cs;
+
/*
* Try IPv4 first
*/
+ memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR,
&ipaddr.ipaddr.ip4addr, NULL);
return -1;
}
- sock->ipaddr = ipaddr;
- sock->port = listen_port;
+ sock->proto = IPPROTO_UDP;
- /*
- * If we can bind to interfaces, do so,
- * else don't.
- */
- if (cf_pair_find(cs, "interface")) {
-#ifndef SO_BINDTODEVICE
+ if (cf_pair_find(cs, "proto")) {
+#ifndef WITH_TCP
cf_log_err(cf_sectiontoitem(cs),
- "System does not support binding to interfaces. Delete this line from the configuration file.");
+ "System does not support the TCP protocol. Delete this line from the configuration file.");
return -1;
#else
- const char *value;
- CONF_PAIR *cp = cf_pair_find(cs, "interface");
+ char *proto = NULL;
+#ifdef WITH_TLS
+ CONF_SECTION *tls;
+#endif
- rad_assert(cp != NULL);
- value = cf_pair_value(cp);
- if (!value) {
+ 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),
- "No interface name given");
+ "Unknown proto name \"%s\"", proto);
+ free(proto);
return -1;
}
- sock->interface = value;
-#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->ipaddr, buffer, sizeof(buffer)),
- sock->port);
- return -1;
- }
+ free(proto);
+ /*
+ * TCP requires a destination IP for sockets.
+ * UDP doesn't, so it's allowed.
+ */
#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 ((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 */
+
+#ifdef WITH_TLS
+ tls = cf_section_sub_find(cs, "tls");
+
+ /*
+ * Don't allow TLS configurations for UDP sockets.
+ */
+ if (sock->proto != IPPROTO_TCP) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "TLS transport is not available for UDP sockets.");
+ return -1;
+ }
+
+ if (tls) {
+ /*
+ * FIXME: Make this better.
+ */
+ if (listen_port == 0) listen_port = 2083;
+
+ this->tls = tls_server_conf_parse(tls);
+ if (!this->tls) {
+ return -1;
+ }
+
+#ifdef HAVE_PTRHEAD_H
+ if (pthread_mutex_init(&sock->mutex, NULL) < 0) {
+ rad_assert(0 == 1);
+ listen_free(&this);
+ return 0;
+ }
+#endif
+
+ }
+#else /* WITH_TLS */
+ /*
+ * Built without TLS. Disallow it.
+ */
+ if (cf_section_sub_find(cs, "tls")) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "TLS transport is not available in this executable.");
+ return -1;
+ }
+#endif /* WITH_TLS */
+
+#endif /* WITH_TCP */
+
+ /*
+ * No "proto" field. Disallow TLS.
+ */
+ } else if (cf_section_sub_find(cs, "tls")) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "TLS transport is not available in this \"listen\" section.");
+ return -1;
+ }
+
+ sock->my_ipaddr = ipaddr;
+ sock->my_port = listen_port;
+
+#ifdef WITH_PROXY
+ 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 */
+ }
+#endif
+
+ /*
+ * 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) {
/*
return -1;
}
+#ifdef WITH_TCP
+ if (sock->proto == IPPROTO_TCP) {
+ /*
+ * Re-write the listener receive function to
+ * allow us to accept the socket.
+ */
+ this->recv = dual_tcp_accept;
+ }
+#endif
+
return 0;
}
rad_assert(request->listener == listener);
rad_assert(listener->send == auth_socket_send);
- return rad_send(request->reply, request->packet,
- request->client->secret);
+ if (rad_send(request->reply, request->packet,
+ request->client->secret) < 0) {
+ radlog_request(L_ERR, 0, request, "Failed sending reply: %s",
+ fr_strerror());
+ return -1;
+ }
+
+ return 0;
}
*/
if (request->reply->code == 0) return 0;
- return rad_send(request->reply, request->packet,
- request->client->secret);
+ if (rad_send(request->reply, request->packet,
+ request->client->secret) < 0) {
+ radlog_request(L_ERR, 0, request, "Failed sending reply: %s",
+ fr_strerror());
+ return -1;
+ }
+
+ return 0;
}
#endif
*/
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;
+ if (rad_send(request->proxy, NULL,
+ request->home_server->secret) < 0) {
+ radlog_request(L_ERR, 0, request, "Failed sending proxied request: %s",
+ fr_strerror());
+ return -1;
+ }
- return rad_send(request->proxy, request->packet,
- request->home_server->secret);
+ return 0;
}
#endif
* 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)
+static int stats_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
int code, src_port;
RADIUS_PACKET *packet;
- RADCLIENT *client;
+ RADCLIENT *client = NULL;
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);
+ FR_STATS_INC(auth, total_requests);
if (rcode < 20) { /* AUTH_HDR_LEN */
- RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ FR_STATS_INC(auth, 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);
+ FR_STATS_INC(auth, total_invalid_requests);
return 0;
}
+ FR_STATS_TYPE_INC(client->auth.total_requests);
+
/*
* We only understand Status-Server on this socket.
*/
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);
+ FR_STATS_INC(auth, total_unknown_types);
return 0;
}
*/
packet = rad_recv(listener->fd, 1); /* require message authenticator */
if (!packet) {
- RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ FR_STATS_INC(auth, 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);
+ if (!request_receive(listener, packet, client, rad_status_server)) {
+ FR_STATS_INC(auth, total_packets_dropped);
rad_free(&packet);
return 0;
}
- *pfun = rad_status_server;
return 1;
}
#endif
* 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)
+static int auth_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
int code, src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
- RADCLIENT *client;
+ RADCLIENT *client = NULL;
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);
+ FR_STATS_INC(auth, total_requests);
if (rcode < 20) { /* AUTH_HDR_LEN */
- RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ FR_STATS_INC(auth, 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);
+ FR_STATS_INC(auth, total_invalid_requests);
return 0;
}
+ FR_STATS_TYPE_INC(client->auth.total_requests);
+
/*
* 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);
+ FR_STATS_INC(auth, total_unknown_types);
DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
return 0;
}
default:
rad_recv_discard(listener->fd);
- RAD_STATS_INC(radius_auth_stats.total_unknown_types);
- RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+ FR_STATS_INC(auth,total_unknown_types);
DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED",
code, client->shortname, src_port);
*/
packet = rad_recv(listener->fd, client->message_authenticator);
if (!packet) {
- RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ FR_STATS_INC(auth, 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);
+ if (!request_receive(listener, packet, client, fun)) {
+ FR_STATS_INC(auth, total_packets_dropped);
rad_free(&packet);
return 0;
}
- *pfun = fun;
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 acct_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
int code, src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
- RADCLIENT *client;
+ RADCLIENT *client = NULL;
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);
+ FR_STATS_INC(acct, total_requests);
if (rcode < 20) { /* AUTH_HDR_LEN */
- RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ FR_STATS_INC(acct, 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);
+ FR_STATS_INC(acct, total_invalid_requests);
return 0;
}
+ FR_STATS_TYPE_INC(client->acct.total_requests);
+
/*
* Some sanity checks, based on the packet code.
*/
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);
+ FR_STATS_INC(acct, total_unknown_types);
DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
return 0;
default:
rad_recv_discard(listener->fd);
- RAD_STATS_TYPE_INC(listener, total_unknown_types);
- RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+ FR_STATS_INC(acct, total_unknown_types);
DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED",
code, client->shortname, src_port);
*/
packet = rad_recv(listener->fd, 0);
if (!packet) {
- RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ FR_STATS_INC(acct, total_malformed_requests);
radlog(L_ERR, "%s", fr_strerror());
return 0;
}
/*
* There can be no duplicate accounting packets.
*/
- if (!received_request(listener, packet, prequest, client)) {
- RAD_STATS_TYPE_INC(listener, total_packets_dropped);
- RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+ if (!request_receive(listener, packet, client, fun)) {
+ FR_STATS_INC(acct, total_packets_dropped);
rad_free(&packet);
return 0;
}
- *pfun = fun;
return 1;
}
#endif
#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)
+static int do_proxy(REQUEST *request)
{
- VALUE_PAIR *s1, *s2;
-
- /*
- * Inform the user about RFC requirements.
- */
- s1 = pairfind(request->proxy->vps, PW_STATE);
- if (s1) {
- s2 = pairfind(request->proxy_reply->vps, PW_STATE);
+ VALUE_PAIR *vp;
- if (!s2) {
- DEBUG("WARNING: Client was sent State in CoA, and did not respond with State.");
+ if (request->in_proxy_hash ||
+ (request->proxy_reply && (request->proxy_reply->code != 0))) {
+ return 0;
+ }
- } 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.");
- }
+ vp = pairfind(request->config_items, PW_HOME_SERVER_POOL, 0);
+ if (!vp) return 0;
+
+ if (!home_pool_byname(vp->vp_strvalue, HOME_TYPE_COA)) {
+ RDEBUG2("ERROR: Cannot proxy to unknown pool %s",
+ vp->vp_strvalue);
+ return 0;
}
- return RLM_MODULE_OK;
+ return 1;
}
/*
* with Service-Type = Authorize-Only, it MUST
* have a State attribute in it.
*/
- vp = pairfind(request->packet->vps, PW_SERVICE_TYPE);
+ 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);
+ 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;
case RLM_MODULE_NOTFOUND:
case RLM_MODULE_OK:
case RLM_MODULE_UPDATED:
+ if (do_proxy(request)) return RLM_MODULE_OK;
request->reply->code = ack;
break;
}
- } else {
+ } else if (request->proxy_reply) {
/*
* Start the reply code with the proxy reply
* code.
* Copy State from the request to the reply.
* See RFC 5176 Section 3.3.
*/
- vp = paircopy2(request->packet->vps, PW_STATE);
+ 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) {
- /*
- * 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 (request->reply->code) {
+ rcode = module_send_coa(0, request);
+ switch (rcode) {
+ /*
+ * 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.
+ */
case RLM_MODULE_FAIL:
case RLM_MODULE_INVALID:
case RLM_MODULE_REJECT:
request->reply->code = ack;
}
break;
-
+ }
}
return RLM_MODULE_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)
+static int coa_socket_recv(rad_listen_t *listener)
{
ssize_t rcode;
int code, src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
- RADCLIENT *client;
+ RADCLIENT *client = NULL;
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);
+ FR_STATS_INC(coa, total_requests);
+ FR_STATS_INC(coa, 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);
-
- if (debug_flag > 0) {
- char name[1024];
-
- listener->print(listener, name, sizeof(name));
-
- /*
- * This is debugging rather than logging, so that
- * DoS attacks don't affect us.
- */
- DEBUG("Ignoring request to %s from unknown client %s port %d",
- name,
- inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr,
- buffer, sizeof(buffer)), src_port);
- }
-
+ FR_STATS_INC(coa, total_requests);
+ FR_STATS_INC(coa, total_invalid_requests);
return 0;
}
*/
switch(code) {
case PW_COA_REQUEST:
+ FR_STATS_INC(coa, total_requests);
+ fun = rad_coa_recv;
+ break;
+
case PW_DISCONNECT_REQUEST:
+ FR_STATS_INC(dsc, total_requests);
fun = rad_coa_recv;
break;
default:
rad_recv_discard(listener->fd);
+ FR_STATS_INC(coa, total_unknown_types);
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 */
/*
*/
packet = rad_recv(listener->fd, client->message_authenticator);
if (!packet) {
- RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ FR_STATS_INC(coa, total_malformed_requests);
DEBUG("%s", fr_strerror());
return 0;
}
- if (!received_request(listener, packet, prequest, client)) {
+ if (!request_receive(listener, packet, client, fun)) {
+ FR_STATS_INC(coa, total_packets_dropped);
rad_free(&packet);
return 0;
}
- *pfun = fun;
return 1;
}
#endif
/*
* Recieve packets from a proxy socket.
*/
-static int proxy_socket_recv(rad_listen_t *listener,
- RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int proxy_socket_recv(rad_listen_t *listener)
{
- REQUEST *request;
RADIUS_PACKET *packet;
- RAD_REQUEST_FUNP fun = NULL;
char buffer[128];
packet = rad_recv(listener->fd, 0);
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
case PW_DISCONNECT_NAK:
case PW_COA_ACK:
case PW_COA_NAK:
- fun = rad_coa_reply;
break;
#endif
return 0;
}
- request = received_proxy_response(packet);
- if (!request) {
+ if (!request_proxy_reply(packet)) {
rad_free(&packet);
return 0;
}
-#ifdef WITH_COA
- /*
- * Distinguish proxied CoA requests from ones we
- * originate.
- */
- 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;
}
/*
* Recieve packets from a proxy socket.
*/
-static int proxy_socket_tcp_recv(rad_listen_t *listener,
- RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int proxy_socket_tcp_recv(rad_listen_t *listener)
{
- REQUEST *request;
RADIUS_PACKET *packet;
- RAD_REQUEST_FUNP fun = NULL;
+ listen_socket_t *sock = listener->data;
char buffer[128];
packet = fr_tcp_recv(listener->fd, 0);
if (!packet) {
- proxy_close_tcp_listener(listener);
+ listener->status = RAD_LISTEN_STATUS_REMOVE_FD;
+ event_new_fd(listener);
return 0;
}
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
return 0;
}
-#if 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;
+
/*
- * Commented out until the rest of the code is added.
+ * 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...
*/
- request = received_proxy_tcp_response(packet,
- fr_listen2tcp(listener));
-#endif
- if (!request) {
+ if (!request_proxy_reply(packet)) {
+ rad_free(&packet);
return 0;
}
- rad_assert(fun != NULL);
- *pfun = fun;
- *prequest = request;
+ sock->opened = sock->last_packet = time(NULL);
return 1;
}
{
if (!request->reply->code) return 0;
- rad_encode(request->reply, request->packet,
- request->client->secret);
- rad_sign(request->reply, request->packet,
- request->client->secret);
+ if (rad_encode(request->reply, request->packet,
+ request->client->secret) < 0) {
+ radlog_request(L_ERR, 0, request, "Failed encoding packet: %s",
+ fr_strerror());
+ return -1;
+ }
+
+ if (rad_sign(request->reply, request->packet,
+ request->client->secret) < 0) {
+ radlog_request(L_ERR, 0, request, "Failed signing packet: %s",
+ fr_strerror());
+ return -1;
+ }
return 0;
}
#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);
+ if (rad_encode(request->proxy, NULL, request->home_server->secret) < 0) {
+ radlog_request(L_ERR, 0, request, "Failed encoding proxied packet: %s",
+ fr_strerror());
+ return -1;
+ }
+
+ if (rad_sign(request->proxy, NULL, request->home_server->secret) < 0) {
+ radlog_request(L_ERR, 0, request, "Failed signing proxied packet: %s",
+ fr_strerror());
+ return -1;
+ }
return 0;
}
#ifdef WITH_COMMAND_SOCKET
/* TCP command socket */
- { command_socket_parse, NULL,
+ { command_socket_parse, command_socket_free,
command_domain_accept, command_domain_send,
command_socket_print, command_socket_encode, command_socket_decode },
#endif
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->port = ntohs(svp->s_port);
+ sock->my_port = ntohs(svp->s_port);
} else {
- sock->port = PW_ACCT_UDP_PORT;
+ sock->my_port = PW_ACCT_UDP_PORT;
}
break;
#endif
#ifdef WITH_PROXY
case RAD_LISTEN_PROXY:
- sock->port = 0;
+ /* leave it at zero */
break;
#endif
#ifdef WITH_VMPS
case RAD_LISTEN_VQP:
- sock->port = 1589;
+ sock->my_port = 1589;
break;
#endif
#ifdef WITH_COA
case RAD_LISTEN_COA:
- sock->port = PW_COA_UDP_PORT;
+ svp = getservbyname ("radius-dynauth", "udp");
+ if (svp != NULL) {
+ sock->my_port = ntohs(svp->s_port);
+ } else {
+ sock->my_port = PW_COA_UDP_PORT;
+ }
break;
#endif
default:
- radlog(L_ERR, "ERROR: Non-fatal internal sanity check failed in bind.");
+ DEBUG("WARNING: Internal sanity check failed in binding to socket. Ignoring problem.");
return -1;
}
}
/*
+ * Don't open sockets if we're checking the config.
+ */
+ if (check_config) {
+ this->fd = -1;
+ return 0;
+ }
+
+ /*
* Copy fr_socket() here, as we may need to bind to a device.
*/
- this->fd = socket(sock->ipaddr.af, SOCK_DGRAM, 0);
+ this->fd = socket(sock->my_ipaddr.af, sock_type, 0);
if (this->fd < 0) {
- radlog(L_ERR, "Failed opening socket: %s", strerror(errno));
+ char buffer[256];
+
+ this->print(this, buffer, sizeof(buffer));
+
+ radlog(L_ERR, "Failed opening %s: %s", buffer, strerror(errno));
return -1;
}
-#ifdef SO_BINDTODEVICE
/*
* Bind to a device BEFORE touching IP addresses.
*/
if (sock->interface) {
+#ifdef SO_BINDTODEVICE
struct ifreq ifreq;
- strcpy(ifreq.ifr_name, sock->interface);
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ strlcpy(ifreq.ifr_name, sock->interface, sizeof(ifreq.ifr_name));
fr_suid_up();
rcode = setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE,
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) {
+ radlog(L_ERR, "Failed initializing udpfromto: %s",
+ strerror(errno));
close(this->fd);
return -1;
}
#endif
-
+
/*
* Set up sockaddr stuff.
*/
- if (!fr_ipaddr2sockaddr(&sock->ipaddr, sock->port, &salocal, &salen)) {
+ if (!fr_ipaddr2sockaddr(&sock->my_ipaddr, sock->my_port, &salocal, &salen)) {
close(this->fd);
return -1;
}
#ifdef HAVE_STRUCT_SOCKADDR_IN6
- if (sock->ipaddr.af == AF_INET6) {
+ 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
*/
#ifdef IPV6_V6ONLY
- if (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr)) {
int on = 1;
setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY,
}
#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
-
- if (sock->ipaddr.af == AF_INET) {
+ if (sock->my_ipaddr.af == AF_INET) {
UNUSED int flag;
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
#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.
*/
- 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);
+ if (sock->my_port != 0) {
+#ifdef SO_REUSEADDR
+ int on = 1;
- memset(&src, 0, sizeof_src);
- if (getsockname(this->fd, (struct sockaddr *) &src,
- &sizeof_src) < 0) {
- radlog(L_ERR, "Failed getting socket name: %s",
+ 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
- if (!fr_sockaddr2ipaddr(&src, sizeof_src,
- &sock->ipaddr, &sock->port)) {
- radlog(L_ERR, "Socket has unsupported address family");
+ 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 (!fr_sockaddr2ipaddr(&src, sizeof_src,
+ &sock->my_ipaddr, &sock->my_port)) {
+ radlog(L_ERR, "Socket has unsupported address family");
+ return -1;
+ }
+ }
}
-#ifdef O_NONBLOCK
- {
- int flags;
-
- if ((flags = fcntl(this->fd, F_GETFL, NULL)) < 0) {
- radlog(L_ERR, "Failure getting socket flags: %s)\n",
- strerror(errno));
- return -1;
- }
-
- flags |= O_NONBLOCK;
- if( fcntl(this->fd, F_SETFL, flags) < 0) {
- radlog(L_ERR, "Failure setting socket flags: %s)\n",
- strerror(errno));
+#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;
}
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
*/
-rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
+int proxy_new_listener(home_server *home, int src_port)
{
- int last_proxy_port, port;
- rad_listen_t *this, *tmp, **last;
- listen_socket_t *sock, *old;
+ rad_listen_t *this;
+ listen_socket_t *sock;
+#ifndef NDEBUG
+ char buffer[256];
+#endif
- /*
- * Find an existing proxy socket to copy.
- */
- last_proxy_port = 0;
- old = NULL;
- last = &mainconfig.listen;
- for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) {
- /*
- * Not proxy, ignore it.
- */
- if (tmp->type != RAD_LISTEN_PROXY) continue;
+ if (!home) return 0;
- sock = tmp->data;
+ 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;
+ }
- /*
- * If we were asked to copy a specific one, do
- * so. If we're just finding one that already
- * exists, return a pointer to it. Otherwise,
- * create ANOTHER one with the same IP address.
- */
- if ((ipaddr->af != AF_UNSPEC) &&
- (fr_ipaddr_cmp(&sock->ipaddr, ipaddr) != 0)) {
- if (exists) return tmp;
- continue;
- }
-
- if (sock->port > last_proxy_port) {
- last_proxy_port = sock->port + 1;
- }
- if (!old) old = sock;
+ 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;
- last = &(tmp->next);
+ if (debug_flag >= 2) {
+ this->print(this, buffer, sizeof(buffer));
+ DEBUG("Opening new %s", buffer);
}
- if (!old) {
+#ifdef WITH_TCP
+ sock->opened = sock->last_packet = time(NULL);
+
+ if (home->proto == IPPROTO_TCP) {
+ this->recv = proxy_socket_tcp_recv;
+
/*
- * The socket MUST already exist if we're binding
- * to an address while proxying.
+ * FIXME: connect() is blocking!
+ * We do this with the proxy mutex locked, which may
+ * cause large delays!
*
- * If we're initializing the server, it's OK for the
- * socket to NOT exist.
+ * http://www.developerweb.net/forum/showthread.php?p=13486
*/
- if (!exists) return NULL;
-
- this = listen_alloc(RAD_LISTEN_PROXY);
+ this->fd = fr_tcp_client_socket(&home->src_ipaddr,
+ &home->ipaddr, home->port);
+#ifdef WITH_TLS
+ if (home->tls) {
+ DEBUG("Trying SSL to port %d\n", home->port);
+ sock->ssn = tls_new_client_session(home->tls, this->fd);
+ if (!sock->ssn) {
+ listen_free(&this);
+ return 0;
+ }
- sock = this->data;
- sock->ipaddr = *ipaddr;
+ this->recv = proxy_tls_recv;
+ this->send = proxy_tls_send;
+ }
+#endif
+ } else
+#endif
+ this->fd = fr_socket(&home->src_ipaddr, src_port);
- } else {
- this = listen_alloc(RAD_LISTEN_PROXY);
-
- sock = this->data;
- sock->ipaddr = old->ipaddr;
+ if (this->fd < 0) {
+ this->print(this, buffer,sizeof(buffer));
+ DEBUG("Failed opening client socket ::%s:: : %s",
+ buffer, fr_strerror());
+ listen_free(&this);
+ return 0;
}
/*
- * Keep going until we find an unused port.
+ * Figure out which port we were bound to.
*/
- for (port = last_proxy_port; port < 64000; port++) {
- int rcode;
-
- sock->port = port;
-
- rcode = listen_bind(this);
- if (rcode < 0) {
+ if (sock->my_port == 0) {
+ struct sockaddr_storage src;
+ socklen_t sizeof_src = sizeof(src);
+
+ memset(&src, 0, sizeof_src);
+ if (getsockname(this->fd, (struct sockaddr *) &src,
+ &sizeof_src) < 0) {
+ radlog(L_ERR, "Failed getting socket name: %s",
+ strerror(errno));
listen_free(&this);
- return NULL;
+ return 0;
}
- /*
- * Add the new listener to the list of
- * listeners.
- */
- *last = this;
- return this;
+ 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;
+ }
}
- listen_free(&this);
- return NULL;
+ /*
+ * Tell the event loop that we have a new FD
+ */
+ if (!event_new_fd(this)) {
+ listen_free(&this);
+ return 0;
+ }
+
+ return 1;
}
#endif
+
static const FR_NAME_NUMBER listen_compare[] = {
#ifdef WITH_STATS
{ "status", RAD_LISTEN_NONE },
return NULL;
}
free(listen_type);
-
+
/*
* Allow listen sections in the default config to
* refer to a server.
if (rcode < 0) return NULL;
}
+#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
+
/*
* Set up cross-type data.
*/
return this;
}
+#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(CONF_SECTION *config, rad_listen_t **head)
+int listen_init(CONF_SECTION *config, rad_listen_t **head, int spawn_flag)
{
int override = FALSE;
int rcode;
#ifdef WITH_PROXY
int defined_proxy = 0;
#endif
+#ifndef WITH_TLS
+ spawn_flag = spawn_flag; /* -Wunused */
+#endif
/*
* We shouldn't be called with a pre-existing list.
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:
#ifdef WITH_VMPS
sock = this->data;
- sock->ipaddr = server_ipaddr;
- sock->port = auth_port;
+ sock->my_ipaddr = server_ipaddr;
+ sock->my_port = auth_port;
sock->clients = clients_parse_section(config);
if (!sock->clients) {
if (listen_bind(this) < 0) {
listen_free(head);
- radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->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);
/*
* No acct for vmpsd
*/
- if (strcmp(progname, "vmpsd") == 0) goto do_proxy;
+ if (strcmp(progname, "vmpsd") == 0) goto add_sockets;
#endif
#ifdef WITH_ACCOUNTING
* 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) {
if (listen_bind(this) < 0) {
listen_free(&this);
listen_free(head);
- radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->port);
+ radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->my_port);
return -1;
}
cs = cf_section_sub_find_name2(config, "server",
mainconfig.name);
- if (!cs) goto do_proxy;
+ if (!cs) goto add_sockets;
/*
* Should really abstract this code...
return -1;
}
-#ifdef WITH_PROXY
- if (this->type == RAD_LISTEN_PROXY) defined_proxy = 1;
-#endif
-
*last = this;
last = &(this->next);
} /* loop over "listen" directives in server <foo> */
- goto do_proxy;
+ goto add_sockets;
}
/*
return -1;
}
-#ifdef WITH_PROXY
- if (this->type == RAD_LISTEN_PROXY) defined_proxy = 1;
-#endif
-
*last = this;
last = &(this->next);
}
return -1;
}
-#ifdef WITH_PROXY
- if (this->type == RAD_LISTEN_PROXY) {
- radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section");
- listen_free(head);
- return -1;
- }
-#endif
-
*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;
+ }
+
+#endif
+
+#ifdef WITH_TLS
+ if (!spawn_flag && this->tls) {
+ cf_log_err(cf_sectiontoitem(this->cs), "Threading must be enabled for TLS sockets to function properly.");
+ cf_log_err(cf_sectiontoitem(this->cs), "You probably need to do 'radiusd -fxx -l stdout' for debugging");
+ return -1;
+ }
+#endif
+ if (!check_config) event_new_fd(this);
+ }
+
/*
* If we're proxying requests, open the proxy FD.
* Otherwise, don't do anything.
*/
- do_proxy:
#ifdef WITH_PROXY
- if (mainconfig.proxy_requests == TRUE) {
- int port = -1;
+ if ((mainconfig.proxy_requests == TRUE) &&
+ !check_config &&
+ (*head != NULL) && !defined_proxy) {
listen_socket_t *sock = NULL;
+ int port = 0;
+ home_server home;
+
+ memset(&home, 0, sizeof(home));
/*
- * No sockets to receive packets, therefore
- * proxying is pointless.
+ *
*/
- if (!*head) return -1;
-
- if (defined_proxy) goto check_home_servers;
+ 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->port + 2; /* skip acct port */
+ port = sock->my_port + 2;
break;
}
-#ifdef WITH_VMPS
- if (this->type == RAD_LISTEN_VQP) {
+#ifdef WITH_ACCT
+ if (this->type == RAD_LISTEN_ACCT) {
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->port + 1;
+ port = sock->my_port + 1;
break;
}
#endif
}
- if (port < 0) port = 1024 + (fr_rand() & 0x1ff);
-
/*
* 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, "Failed to open socket for proxying");
return -1;
}
-
- /*
- * Create *additional* proxy listeners, based
- * on their src_ipaddr.
- */
- check_home_servers:
- if (home_server_create_listeners(*head) != 0) return -1;
}
#endif
+ /*
+ * Haven't defined any sockets. Die.
+ */
+ if (!*head) return -1;
+
+ xlat_register("listen", xlat_listen, NULL);
+
return 0;
}
if (master_listen[this->type].free) {
master_listen[this->type].free(this);
}
- free(this->data);
- free(this);
- this = next;
- }
-
- *head = NULL;
-}
+#ifdef WITH_TLS
+ if (this->tls) tls_server_conf_free(this->tls);
+#endif
#ifdef WITH_TCP
-fr_tcp_radius_t *fr_listen2tcp(rad_listen_t *this)
-{
- listen_socket_t *sock;
-
- if (!this || (this->type != RAD_LISTEN_PROXY) || !this->data) {
- return NULL;
- }
-
- sock = this->data;
- return sock->tcp;
-}
-
-rad_listen_t *proxy_new_tcp_listener(home_server *home)
-{
- int i;
- fr_tcp_radius_t *tcp;
- struct sockaddr_storage src;
- socklen_t sizeof_src = sizeof(src);
- rad_listen_t *this;
- listen_socket_t *sock;
-
- if (!home ||
- ((home->max_connections > 0) &&
- (home->num_connections >= home->max_connections))) {
- DEBUG("WARNING: Home server has too many open connections (%d)",
- home->max_connections);
- return NULL;
- }
+ 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;
- this = NULL;
+#ifdef WITH_TLS
+ if (sock->request) {
+ pthread_mutex_destroy(&(sock->mutex));
+ request_free(&sock->request);
+ sock->packet = NULL;
- /*
- * FIXME: Move to RBTrees.
- */
- for (i = 0; i < home->max_connections; i++) {
- if (home->listeners[i]) continue;
+ if (sock->ssn) session_free(sock->ssn);
+ request_free(&sock->request);
+ } else
+#endif
+ rad_free(&sock->packet);
- this = home->listeners[i] = listen_alloc(RAD_LISTEN_PROXY);
- if (!this) {
- DEBUG("WARNING: Failed allocating memory");
- return NULL;
}
- break;
- }
-
- if (!this) {
- DEBUG("WARNING: Failed to find a free connection slot");
- return NULL;
- }
- sock = this->data;
-
- tcp = sock->tcp = rad_malloc(sizeof(*tcp));
- memset(tcp, 0, sizeof(*tcp));
+#endif /* WITH_TCP */
- /*
- * Initialize th
- *
- * Open a new socket...
- *
- * Do stuff...
- */
- tcp->dst_ipaddr = home->ipaddr;
- tcp->dst_port = home->port;
- tcp->lifetime = home->lifetime;
- tcp->opened = time(NULL);
-
- /*
- * 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 = tcp->fd = fr_tcp_client_socket(&tcp->dst_ipaddr, tcp->dst_port);
- if (tcp->fd < 0) {
- listen_free(&this);
- DEBUG("WARNING: Failed opening socket to home server");
- return NULL;
- }
- memset(&src, 0, sizeof_src);
- if (getsockname(tcp->fd, (struct sockaddr *) &src, &sizeof_src) < 0) {
- close(tcp->fd);
- listen_free(&this);
- return NULL;
- }
-
- if (!fr_sockaddr2ipaddr(&src, sizeof_src,
- &tcp->src_ipaddr, &tcp->src_port)) {
- close(tcp->fd);
- listen_free(&this);
- return NULL;
- }
-
- /*
- * Fill in socket information.
- */
- sock->proto = IPPROTO_TCP;
- sock->tcp = tcp;
-
- sock->ipaddr = tcp->src_ipaddr;
- sock->port = tcp->src_port;
-
- /*
- * Don't ask. Just don't ask.
- */
- sock->src_ipaddr = tcp->dst_ipaddr;
- sock->src_port = tcp->dst_port;
- sock->home = home;
- sock->home->num_connections++;
-
- this->recv = proxy_socket_tcp_recv;
-
- /*
- * Tell the event handler about the new socket.
- *
- * It keeps track of "this", so we don't have to insert
- * it into the main list of listeners.
- */
- event_new_fd(this);
-
- return this;
-}
-
-void proxy_close_tcp_listener(rad_listen_t *listener)
-{
- int i;
- listen_socket_t *sock = listener->data;
-
- /*
- * This is the second time around for the socket. Free
- * the memory now.
- */
- if (listener->status != RAD_LISTEN_STATUS_KNOWN) {
- listen_free(&listener);
- return;
- }
+ free(this->data);
+ free(this);
- listener->status = RAD_LISTEN_STATUS_CLOSED;
- event_new_fd(listener);
-
- /*
- * Find the home server, and mark this listener as
- * no longer being active.
- */
- for (i = 0; i < sock->home->max_connections; i++) {
- if (sock->home->listeners[i] == listener) {
- sock->home->listeners[i] = NULL;
- sock->home->num_connections--;
- break;
- }
+ this = next;
}
- /*
- * There are still one or more requests using this socket.
- * leave it marked as "closed", but don't free it. When the
- * last requeast using it is cleaned up, it will be deleted.
- */
- if (sock->tcp->used > 0) return;
-
- listen_free(&listener);
+ *head = NULL;
}
-#endif
#ifdef WITH_STATS
RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
for (this = mainconfig.listen; this != NULL; this = this->next) {
listen_socket_t *sock;
- if ((this->type != RAD_LISTEN_AUTH) &&
- (this->type != RAD_LISTEN_ACCT)) continue;
+ if ((this->type != RAD_LISTEN_AUTH)
+#ifdef WITH_ACCOUNTING
+ && (this->type != RAD_LISTEN_ACCT)
+#endif
+ ) continue;
sock = this->data;
- if ((sock->port == port) &&
- (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
+ if ((sock->my_port == port) &&
+ (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
return sock->clients;
}
}
}
#endif
-rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port)
+rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port, int proto)
{
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) &&
- (this->type != RAD_LISTEN_ACCT)) continue;
-
sock = this->data;
- if ((sock->port == port) &&
- (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
- return this;
- }
+ if (sock->my_port != port) continue;
+ if (sock->proto != proto) continue;
+ if (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) != 0) continue;
- if ((sock->port == port) &&
- ((sock->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) {
- return this;
- }
+ return this;
+ }
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- if ((sock->port == port) &&
- (sock->ipaddr.af == AF_INET6) &&
- (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr))) {
- return this;
- }
-#endif
+ /*
+ * Failed to find a specific one. Find INADDR_ANY
+ */
+ for (this = mainconfig.listen; this != NULL; this = this->next) {
+ listen_socket_t *sock;
+
+ sock = this->data;
+
+ if (sock->my_port != port) continue;
+ if (sock->proto != proto) continue;
+ if (!fr_inaddr_any(&sock->my_ipaddr)) continue;
+
+ return this;
}
return NULL;