#include <freeradius-devel/sha1.h>
#include <freeradius-devel/md4.h>
+#ifndef WITHOUT_TCP
+#define WITH_TCP (1)
+#endif
+
#define EAP_START 2
#define AUTH_VECTOR_LEN 16
uint32_t fr_request_packet_hash(const RADIUS_PACKET *packet);
uint32_t fr_reply_packet_hash(const RADIUS_PACKET *packet);
int fr_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b);
+int fr_inaddr_any(fr_ipaddr_t *ipaddr);
void fr_request_from_reply(RADIUS_PACKET *request,
const RADIUS_PACKET *reply);
int fr_socket(fr_ipaddr_t *ipaddr, int port);
+int fr_nonblock(int fd);
typedef struct fr_packet_list_t fr_packet_list_t;
RADIUS_PACKET *request);
int fr_packet_list_num_elements(fr_packet_list_t *pl);
int fr_packet_list_id_alloc(fr_packet_list_t *pl,
- RADIUS_PACKET *request);
+ RADIUS_PACKET *request, void **pctx);
int fr_packet_list_id_free(fr_packet_list_t *pl,
RADIUS_PACKET *request);
-int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd);
-int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd);
+int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd,
+ fr_ipaddr_t *dst_ipaddr, int dst_port,
+ void *ctx);
+int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd,
+ void **pctx);
+int fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd);
int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx,
fr_hash_table_walk_t callback);
int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set);
#endif
};
+/*
+ * This shouldn't really be exposed...
+ */
+typedef struct listen_socket_t {
+ /*
+ * For normal sockets.
+ */
+ fr_ipaddr_t my_ipaddr;
+ int my_port;
+
+#ifdef SO_BINDTODEVICE
+ const char *interface;
+#endif
+
+ /* for outgoing sockets */
+ home_server *home;
+ fr_ipaddr_t other_ipaddr;
+ int other_port;
+
+#ifdef WITH_TCP
+ int proto;
+
+ /* for a proxy connecting to home servers */
+ time_t last_packet;
+ time_t opened;
+ fr_event_t *ev;
+
+ /* for clients connecting to the server */
+ int max_connections;
+ int num_connections;
+ struct listen_socket_t *parent;
+ RADCLIENT *client;
+
+ RADIUS_PACKET *packet; /* for reading partial packets */
+#endif
+ RADCLIENT_LIST *clients;
+} listen_socket_t;
+
#define RAD_LISTEN_STATUS_INIT (0)
#define RAD_LISTEN_STATUS_KNOWN (1)
-#define RAD_LISTEN_STATUS_CLOSED (2)
-#define RAD_LISTEN_STATUS_FINISH (3)
+#define RAD_LISTEN_STATUS_REMOVE_FD (2)
+#define RAD_LISTEN_STATUS_CLOSED (3)
+#define RAD_LISTEN_STATUS_FINISH (4)
typedef enum radlog_dest_t {
RADLOG_STDOUT = 0,
/* listen.c */
void listen_free(rad_listen_t **head);
int listen_init(CONF_SECTION *cs, rad_listen_t **head);
-rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists);
+int proxy_new_listener(home_server *home, int src_port);
RADCLIENT *client_listener_find(const rad_listen_t *listener,
const fr_ipaddr_t *ipaddr, int src_port);
-#ifdef WITH_TCP
-fr_tcp_radius_t *fr_listen2tcp(rad_listen_t *this);
-rad_listen_t *proxy_new_tcp_listener(home_server *home);
-void proxy_close_tcp_listener(rad_listen_t *listener);
-#endif
#ifdef WITH_STATS
RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
RADCLIENT *client);
REQUEST *received_proxy_response(RADIUS_PACKET *packet);
void event_new_fd(rad_listen_t *listener);
-#ifdef WITH_TCP
-REQUEST *received_proxy_tcp_response(RADIUS_PACKET *packet,
- fr_tcp_radius_t *tcp);
-#endif
/* evaluate.c */
int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
#ifdef WITH_TCP
int proto;
+#endif
int max_connections;
- int num_connections;
- int max_requests;
+ int num_connections; /* protected by proxy mutex */
+ int max_requests; /* for one connection */
int lifetime;
int idle_timeout;
-#endif
/*
* Maybe also have list of source IP/ports, && socket?
fr_stats_ema_t ema;
#endif
-
-#ifdef WITH_TCP
- struct rad_listen_t *listeners[1];
-#endif
} home_server;
home_server *home_server_ldb(const char *realmname, home_pool_t *pool, REQUEST *request);
home_server *home_server_find(fr_ipaddr_t *ipaddr, int port);
-int home_server_create_listeners(void *head);
#ifdef WITH_COA
home_server *home_server_byname(const char *name, int type);
#endif
#include <freeradius-devel/ident.h>
RCSIDH(tcp_h, "$Id$")
-/*
- * Application-layer watchdog from RFC 3539, Appendix A.
- */
-typedef enum fr_watchdog_t {
- ALW_INITIAL = 0,
- ALW_OK,
- ALW_SUSPECT,
- ALW_DOWN,
- ALW_REOPEN
-} fr_watchdog_t;
-
-#define ALW_TWINIT (6)
-
-typedef struct fr_tcp_radius_t {
- int fd;
- fr_ipaddr_t src_ipaddr;
- fr_ipaddr_t dst_ipaddr;
- int src_port;
- int dst_port;
-
- int num_packets;
- int lifetime;
-
- time_t opened;
- time_t last_packet;
-#ifdef WITH_TCP_PING
- struct timeval when;
- void *ev;
-#endif
-
- int state;
- int ping_interval;
- int num_pings_to_alive;
- int num_received_pings;
- int num_pings_sent;
- int ping_timeout;
-
- int used;
- void *ev;
- RADIUS_PACKET **ids[256];
-} fr_tcp_radius_t;
-
int fr_tcp_socket(fr_ipaddr_t *ipaddr, int port);
-int fr_tcp_client_socket(fr_ipaddr_t *ipaddr, int port);
+int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, int dst_port);
int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags);
RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags);
RADIUS_PACKET *fr_tcp_accept(int sockfd);
ssize_t fr_tcp_write_packet(RADIUS_PACKET *packet);
-
-int fr_tcp_list_init(fr_tcp_radius_t *list);
-int fr_tcp_list_insert(RADIUS_PACKET **packet, int num,
- fr_tcp_radius_t *array[]);
-
-int fr_tcp_id_free(int ids[256], int id);
-
#endif /* FR_TCP_H */
#include <freeradius-devel/udpfromto.h>
#endif
+#include <fcntl.h>
+
/*
* Take the key fields of a request packet, and convert it to a
* hash.
return fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
}
+int fr_inaddr_any(fr_ipaddr_t *ipaddr)
+{
+
+ if (ipaddr->af == AF_INET) {
+ if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY) {
+ return 1;
+ }
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ } else if (ipaddr->af == AF_INET6) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr))) {
+ return 1;
+ }
+#endif
+
+ } else {
+ fr_strerror_printf("Unknown address family");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Create a fake "request" from a reply, for later lookup.
}
+int fr_nonblock(UNUSED int fd)
+{
+ int flags = 0;
+
+#ifdef O_NONBLOCK
+
+ flags = fcntl(fd, F_GETFL, NULL);
+ if (flags >= 0) {
+ flags |= O_NONBLOCK;
+ return fcntl(fd, F_SETFL, flags);
+ }
+#endif
+ return flags;
+}
+
/*
* Open a socket on the given IP and port.
*/
}
#endif
+ if (fr_nonblock(sockfd) < 0) {
+ close(sockfd);
+ return -1;
+ }
if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
return sockfd;
*/
typedef struct fr_packet_socket_t {
int sockfd;
+ void *ctx;
int num_outgoing;
- int offset; /* 0..31 */
- int inaddr_any;
- fr_ipaddr_t ipaddr;
- int port;
+ int src_any;
+ fr_ipaddr_t src_ipaddr;
+ int src_port;
+
+ int dst_any;
+ fr_ipaddr_t dst_ipaddr;
+ int dst_port;
+
+ int dont_use;
+
#ifdef WITH_TCP
int type;
#endif
+
+ uint8_t id[32];
} fr_packet_socket_t;
#define FNV_MAGIC_PRIME (0x01000193)
-#define MAX_SOCKETS (32)
+#define MAX_SOCKETS (256)
#define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
#define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
struct fr_packet_list_t {
fr_hash_table_t *ht;
- fr_hash_table_t *dst2id_ht;
-
int alloc_id;
int num_outgoing;
+ int last_recv;
- uint32_t mask;
- int last_recv;
fr_packet_socket_t sockets[MAX_SOCKETS];
};
* Ugh. Doing this on every sent/received packet is not nice.
*/
static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl,
- int sockfd)
+ int sockfd)
{
int i, start;
return NULL;
}
-int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd)
+int fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
+{
+ fr_packet_socket_t *ps;
+
+ if (!pl) return 0;
+
+ ps = fr_socket_find(pl, sockfd);
+ if (!ps) return 0;
+
+ ps->dont_use = 1;
+ return 1;
+}
+
+int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd,
+ void **pctx)
{
fr_packet_socket_t *ps;
if (ps->num_outgoing != 0) return 0;
ps->sockfd = -1;
- pl->mask &= ~(1 << ps->offset);
-
+ if (pctx) *pctx = ps->ctx;
return 1;
}
-int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd)
+int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd,
+ fr_ipaddr_t *dst_ipaddr, int dst_port,
+ void *ctx)
{
int i, start;
struct sockaddr_storage src;
socklen_t sizeof_src;
fr_packet_socket_t *ps;
- if (!pl) return 0;
+ if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
+ fr_strerror_printf("Invalid argument");
+ return 0;
+ }
ps = NULL;
i = start = SOCK2OFFSET(sockfd);
} while (i != start);
if (!ps) {
+ fr_strerror_printf("All socket entries are full");
return 0;
}
memset(ps, 0, sizeof(*ps));
- ps->sockfd = sockfd;
- ps->offset = start;
+ ps->ctx = ctx;
+
#ifdef WITH_TCP
sizeof_src = sizeof(ps->type);
if (getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &ps->type, &sizeof_src) < 0) {
+ fr_strerror_printf("%s", strerror(errno));
return 0;
}
+
#endif
/*
memset(&src, 0, sizeof_src);
if (getsockname(sockfd, (struct sockaddr *) &src,
&sizeof_src) < 0) {
+ fr_strerror_printf("%s", strerror(errno));
return 0;
}
- if (!fr_sockaddr2ipaddr(&src, sizeof_src, &ps->ipaddr, &ps->port)) {
+ if (!fr_sockaddr2ipaddr(&src, sizeof_src, &ps->src_ipaddr,
+ &ps->src_port)) {
+ fr_strerror_printf("Failed to get IP");
return 0;
}
+ ps->dst_ipaddr = *dst_ipaddr;
+ ps->dst_port = dst_port;
+
+ ps->src_any = fr_inaddr_any(&ps->src_ipaddr);
+ if (ps->src_any < 0) return 0;
+
+ ps->dst_any = fr_inaddr_any(&ps->dst_ipaddr);
+ if (ps->dst_any < 0) return 0;
+
/*
- * Grab IP addresses & ports from the sockaddr.
+ * As the last step before returning.
*/
- if (src.ss_family == AF_INET) {
- if (ps->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) {
- ps->inaddr_any = 1;
- }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- } else if (src.ss_family == AF_INET6) {
- if (IN6_IS_ADDR_UNSPECIFIED(&ps->ipaddr.ipaddr.ip6addr)) {
- ps->inaddr_any = 1;
- }
-#endif
- } else {
- return 0;
- }
+ ps->sockfd = sockfd;
- pl->mask |= (1 << ps->offset);
return 1;
}
return fr_packet_cmp(*a, *b);
}
-/*
- * A particular socket can have 256 RADIUS ID's outstanding to
- * any one destination IP/port. So we have a structure that
- * manages destination IP & port, and has an array of 256 ID's.
- *
- * The only magic here is that we map the socket number (0..256)
- * into an "internal" socket number 0..31, that we use to set
- * bits in the ID array. If a bit is 1, then that ID is in use
- * for that socket, and the request MUST be in the packet hash!
- *
- * Note that as a minor memory leak, we don't have an API to free
- * this structure, except when we discard the whole packet list.
- * This means that if destinations are added and removed, they
- * won't be removed from this tree.
- */
-typedef struct fr_packet_dst2id_t {
- fr_ipaddr_t dst_ipaddr;
- int dst_port;
- uint32_t id[1]; /* really id[256] */
-} fr_packet_dst2id_t;
-
-
-static uint32_t packet_dst2id_hash(const void *data)
-{
- uint32_t hash;
- const fr_packet_dst2id_t *pd = data;
-
- hash = fr_hash(&pd->dst_port, sizeof(pd->dst_port));
-
- switch (pd->dst_ipaddr.af) {
- case AF_INET:
- hash = fr_hash_update(&pd->dst_ipaddr.ipaddr.ip4addr,
- sizeof(pd->dst_ipaddr.ipaddr.ip4addr),
- hash);
- break;
- case AF_INET6:
- hash = fr_hash_update(&pd->dst_ipaddr.ipaddr.ip6addr,
- sizeof(pd->dst_ipaddr.ipaddr.ip6addr),
- hash);
- break;
- default:
- break;
- }
-
- return hash;
-}
-
-static int packet_dst2id_cmp(const void *one, const void *two)
-{
- const fr_packet_dst2id_t *a = one;
- const fr_packet_dst2id_t *b = two;
-
- if (a->dst_port < b->dst_port) return -1;
- if (a->dst_port > b->dst_port) return +1;
-
- return fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
-}
-
-static void packet_dst2id_free(void *data)
-{
- free(data);
-}
-
-
void fr_packet_list_free(fr_packet_list_t *pl)
{
if (!pl) return;
fr_hash_table_free(pl->ht);
- fr_hash_table_free(pl->dst2id_ht);
free(pl);
}
pl->sockets[i].sockfd = -1;
}
- if (alloc_id) {
- pl->alloc_id = 1;
-
- pl->dst2id_ht = fr_hash_table_create(packet_dst2id_hash,
- packet_dst2id_cmp,
- packet_dst2id_free);
- if (!pl->dst2id_ht) {
- fr_packet_list_free(pl);
- return NULL;
- }
- }
+ pl->alloc_id = alloc_id;
return pl;
}
my_request.sockfd = reply->sockfd;
my_request.id = reply->id;
- if (ps->inaddr_any) {
- my_request.src_ipaddr = ps->ipaddr;
+ if (ps->src_any) {
+ my_request.src_ipaddr = ps->src_ipaddr;
} else {
my_request.src_ipaddr = reply->dst_ipaddr;
}
- my_request.src_port = ps->port;;
+ my_request.src_port = ps->src_port;
my_request.dst_ipaddr = reply->src_ipaddr;
my_request.dst_port = reply->src_port;
* should be protected by a mutex. This does NOT have to be
* the same mutex as the one protecting the insert/find/yank
* calls!
+ *
+ * We assume that the packet has dst_ipaddr && dst_port
+ * already initialized. We will use those to find an
+ * outgoing socket. The request MAY also have src_ipaddr set.
+ *
+ * We also assume that the sender doesn't care which protocol
+ * should be used.
*/
int fr_packet_list_id_alloc(fr_packet_list_t *pl,
- RADIUS_PACKET *request)
+ RADIUS_PACKET *request, void **pctx)
{
- int i, id, start, fd;
- uint32_t free_mask;
- fr_packet_dst2id_t my_pd, *pd;
+ int i, j, k, fd, id, start_i, start_j, start_k;
+ int src_any = 0;
fr_packet_socket_t *ps;
- if (!pl || !pl->alloc_id || !request) return 0;
-
- my_pd.dst_ipaddr = request->dst_ipaddr;
- my_pd.dst_port = request->dst_port;
-
- pd = fr_hash_table_finddata(pl->dst2id_ht, &my_pd);
- if (!pd) {
- pd = malloc(sizeof(*pd) + 255 * sizeof(pd->id[0]));
- if (!pd) return 0;
+ if ((request->dst_ipaddr.af == AF_UNSPEC) ||
+ (request->dst_port == 0)) {
+ fr_strerror_printf("No destination address/port specified");
+ return 0;
+ }
- memset(pd, 0, sizeof(*pd) + 255 * sizeof(pd->id[0]));
+ /*
+ * Special case: unspec == "don't care"
+ */
+ if (request->src_ipaddr.af == AF_UNSPEC) {
+ memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
+ request->src_ipaddr.af = request->dst_ipaddr.af;
+ }
- pd->dst_ipaddr = request->dst_ipaddr;
- pd->dst_port = request->dst_port;
+ src_any = fr_inaddr_any(&request->src_ipaddr);
+ if (src_any < 0) return 0;
- if (!fr_hash_table_insert(pl->dst2id_ht, pd)) {
- free(pd);
- return 0;
- }
- }
+ /*
+ * MUST specify a destination address.
+ */
+ if (fr_inaddr_any(&request->dst_ipaddr) != 0) return 0;
/*
* FIXME: Go to an LRU system. This prevents ID re-use
* The LRU can be avoided if the caller takes care to free
* Id's only when all responses have been received, OR after
* a timeout.
+ *
+ * Right now, the random approach is almost OK... it's
+ * brute-force over all of the available ID's, BUT using
+ * random numbers for everything spreads the load a bit.
+ *
+ * The old method had a hash lookup on allocation AND
+ * on free. The new method has brute-force on allocation,
+ * and near-zero cost on free.
*/
- id = start = (int) fr_rand() & 0xff;
-
- while (pd->id[id] == pl->mask) { /* all sockets are using this ID */
- redo:
- id++;
- id &= 0xff;
- if (id == start) return 0;
- }
- free_mask = ~((~pd->id[id]) & pl->mask);
+ id = fd = -1;
+ start_i = fr_rand() & SOCKOFFSET_MASK;
- /*
- * This ID has at least one socket free. Check the sockets
- * to see if they are satisfactory for the caller.
- */
- fd = -1;
+#define ID_i ((i + start_i) & SOCKOFFSET_MASK)
for (i = 0; i < MAX_SOCKETS; i++) {
- if (pl->sockets[i].sockfd == -1) continue; /* paranoia */
+ if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */
+
+ ps = &(pl->sockets[ID_i]);
/*
- * This ID is allocated.
+ * This socket is marked as "don't use for new
+ * packets". But we can still receive packets
+ * that are outstanding.
*/
- if ((free_mask & (1 << i)) != 0) continue;
-
+ if (ps->dont_use) continue;
+
+ /*
+ * All IDs are allocated: ignore it.
+ */
+ if (ps->num_outgoing == 256) continue;
+
+ /*
+ * MUST match dst port, if one has been given.
+ */
+ if ((ps->dst_port != 0) &&
+ (ps->dst_port != request->dst_port)) continue;
+
/*
- * If the caller cares about the source address,
- * try to re-use that. This means that the
- * requested source address is set, AND this
- * socket wasn't bound to "*", AND the requested
- * source address is the same as this socket
- * address.
+ * We're sourcing from *, and they asked for a
+ * specific source address: ignore it.
*/
- if ((request->src_ipaddr.af != AF_UNSPEC) &&
- !pl->sockets[i].inaddr_any &&
- (fr_ipaddr_cmp(&request->src_ipaddr, &pl->sockets[i].ipaddr) != 0)) continue;
+ if (ps->src_any && !src_any) continue;
/*
- * They asked for a specific address, and this socket
- * is bound to a wildcard address. Ignore this one, too.
+ * We're sourcing from a specific IP, and they
+ * asked for a source IP that isn't us: ignore
+ * it.
*/
- if ((request->src_ipaddr.af != AF_UNSPEC) &&
- pl->sockets[i].inaddr_any) continue;
+ if (!ps->src_any && !src_any &&
+ (fr_ipaddr_cmp(&request->src_ipaddr,
+ &ps->src_ipaddr) != 0)) continue;
+
+ /*
+ * UDP sockets are allowed to match
+ * destination IPs exactly, OR a socket
+ * with destination * is allowed to match
+ * any requested destination.
+ *
+ * TCP sockets must match the destination
+ * exactly. They *always* have dst_any=0,
+ * so the first check always matches.
+ */
+ if (!ps->dst_any &&
+ (fr_ipaddr_cmp(&request->dst_ipaddr,
+ &ps->dst_ipaddr) != 0)) continue;
- fd = i;
- break;
- }
+ /*
+ * Otherwise, this socket is OK to use.
+ */
- if (fd < 0) {
- goto redo; /* keep searching IDs */
+ /*
+ * Look for a free Id, starting from a random number.
+ */
+ start_j = fr_rand() & 0x1f;
+#define ID_j ((j + start_j) & 0x1f)
+ for (j = 0; j < 32; j++) {
+ if (ps->id[ID_j] == 0xff) continue;
+
+
+ start_k = fr_rand() & 0x07;
+#define ID_k ((k + start_k) & 0x07)
+ for (k = 0; k < 8; k++) {
+ if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
+
+ ps->id[ID_j] |= (1 << ID_k);
+ id = (ID_j * 8) + ID_k;
+ fd = i;
+ break;
+ }
+ if (fd >= 0) break;
+ }
+#undef ID_i
+#undef ID_j
+#undef ID_k
+ if (fd >= 0) break;
+ break;
}
- pd->id[id] |= (1 << fd);
- ps = &pl->sockets[fd];
+ /*
+ * Ask the caller to allocate a new ID.
+ */
+ if (fd < 0) return 0;
ps->num_outgoing++;
pl->num_outgoing++;
request->id = id;
request->sockfd = ps->sockfd;
- request->src_ipaddr = ps->ipaddr;
- request->src_port = ps->port;
+ request->src_ipaddr = ps->src_ipaddr;
+ request->src_port = ps->src_port;
+
+ if (pctx) *pctx = ps->ctx;
return 1;
}
RADIUS_PACKET *request)
{
fr_packet_socket_t *ps;
- fr_packet_dst2id_t my_pd, *pd;
if (!pl || !request) return 0;
ps = fr_socket_find(pl, request->sockfd);
if (!ps) return 0;
- my_pd.dst_ipaddr = request->dst_ipaddr;
- my_pd.dst_port = request->dst_port;
-
- pd = fr_hash_table_finddata(pl->dst2id_ht, &my_pd);
- if (!pd) return 0;
+#if 0
+ if (!ps->id[(request->id >> 3) & 0x1f] & (1 << (request->id & 0x07))) {
+ exit(1);
+ }
+#endif
- pd->id[request->id] &= ~(1 << ps->offset);
+ ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
request->hash = 0; /* invalidate the cached hash */
ps->num_outgoing--;
return sockfd;
}
+ if (fr_nonblock(sockfd) < 0) {
+ close(sockfd);
+ return -1;
+ }
+
if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
- return sockfd; /* don't bind it */
+ close(sockfd);
+ return -1;
}
#ifdef HAVE_STRUCT_SOCKADDR_IN6
return -1;
}
-#if 0
-#ifdef O_NONBLOCK
- {
- int flags;
-
- if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) {
- fr_strerror_printf("Failure getting socket flags: %s",
- strerror(errno));
- close(sockfd);
- return -1;
- }
-
- flags |= O_NONBLOCK;
- if( fcntl(sockfd, F_SETFL, flags) < 0) {
- fr_strerror_printf("Failure setting socket flags: %s",
- strerror(errno));
- close(sockfd);
- return -1;
- }
- }
-#endif
-#endif
return sockfd;
}
/*
* Open a socket TO the given IP and port.
*/
-int fr_tcp_client_socket(fr_ipaddr_t *ipaddr, int port)
+int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
+ fr_ipaddr_t *dst_ipaddr, int dst_port)
{
int sockfd;
struct sockaddr_storage salocal;
socklen_t salen;
- if ((port < 0) || (port > 65535)) {
- fr_strerror_printf("Port %d is out of allowed bounds", port);
+ if ((dst_port < 0) || (dst_port > 65535)) {
+ fr_strerror_printf("Port %d is out of allowed bounds",
+ dst_port);
return -1;
}
- sockfd = socket(ipaddr->af, SOCK_STREAM, 0);
+ if (!dst_ipaddr) return -1;
+
+ sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
if (sockfd < 0) {
return sockfd;
}
}
#endif
#endif
+ /*
+ * Allow the caller to bind us to a specific source IP.
+ */
+ if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
+ if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
+ close(sockfd);
+ return -1;
+ }
+
+ if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
+ fr_strerror_printf("Failure binding to IP: %s",
+ strerror(errno));
+ close(sockfd);
+ return -1;
+ }
+ }
- if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
+ if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
+ close(sockfd);
return -1;
}
#ifdef HAVE_PTHREAD_H
#ifdef WITH_PROXY
static pthread_mutex_t proxy_mutex;
+static rad_listen_t *proxy_listener_list = NULL;
#endif
#define PTHREAD_MUTEX_LOCK if (have_children) pthread_mutex_lock
}
#endif
+/*
+ * We need mutexes around the event FD list *only* in certain
+ * cases.
+ */
+#if defined (HAVE_PTHREAD_H) && (defined(WITH_PROXY) || defined(WITH_TCP))
+static pthread_mutex_t fd_mutex;
+#define FD_MUTEX_LOCK if (have_children) pthread_mutex_lock
+#define FD_MUTEX_UNLOCK if (have_children) pthread_mutex_unlock
+#else
+/*
+ * This is easier than ifdef's throughout the code.
+ */
+#define FD_MUTEX_LOCK(_x)
+#define FD_MUTEX_UNLOCK(_x)
+#endif
+
+
#define INSERT_EVENT(_function, _ctx) if (!fr_event_insert(el, _function, _ctx, &((_ctx)->when), &((_ctx)->ev))) { _rad_panic(__FILE__, __LINE__, "Failed to insert event"); }
#ifdef WITH_PROXY
static fr_packet_list_t *proxy_list = NULL;
+static void remove_from_proxy_hash(REQUEST *request);
-/*
- * We keep the proxy FD's here. The RADIUS Id's are marked
- * "allocated" per Id, via a bit per proxy FD.
- */
-static int proxy_fds[32];
-static rad_listen_t *proxy_listeners[32];
#else
#define remove_from_proxy_hash(foo)
#endif
#ifdef WITH_TCP
request->listener->count--;
-
- /*
- * The TCP socket was closed, but we've got to
- * hang around until done.
- */
- if ((request->listener->status == RAD_LISTEN_STATUS_FINISH) &&
- (request->listener->count == 0)) {
- listen_free(&request->listener);
- }
#endif
}
-
#ifdef WITH_PROXY
static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply)
{
* correctly.
*/
if (request->num_proxied_requests == request->num_proxied_responses) {
- fr_packet_list_yank(proxy_list, request->proxy);
- fr_packet_list_id_free(proxy_list, request->proxy);
- request->in_proxy_hash = FALSE;
- }
-
- /*
- * On the FIRST reply, decrement the count of outstanding
- * requests. Note that this is NOT the count of sent
- * packets, but whether or not the home server has
- * responded at all.
- */
- if (!request->proxy_reply &&
- request->home_server &&
- request->home_server->currently_outstanding) {
- request->home_server->currently_outstanding--;
+ remove_from_proxy_hash(request);
}
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
}
fr_packet_list_yank(proxy_list, request->proxy);
-
-#ifdef WITH_TCP
- /*
- * This should be hit when accounting packets don't have
- * responses, or when the home server is down.
- */
- if (request->home_server->proto == IPPROTO_TCP) {
- fr_tcp_radius_t *tcp;
-
- tcp = fr_listen2tcp(request->proxy_listener);
- if (tcp && tcp->ids[request->proxy->id]) {
- tcp->ids[request->proxy->id] = NULL;
- tcp->used--;
- }
- } else
-#endif
fr_packet_list_id_free(proxy_list, request->proxy);
/*
- * The home server hasn't replied, but we've given up on
- * this request. Don't count this request against the
- * home server.
+ * On the FIRST reply, decrement the count of outstanding
+ * requests. Note that this is NOT the count of sent
+ * packets, but whether or not the home server has
+ * responded at all.
*/
if (!request->proxy_reply &&
request->home_server &&
request->home_server->currently_outstanding--;
}
+#ifdef WITH_TCP
+ request->proxy_listener->count--;
+ request->proxy_listener = NULL;
+#endif
+
/*
* Got from YES in hash, to NO, not in hash while we hold
* the mutex. This guarantees that when another thread
- * grans the mutex, the "not in hash" flag is correct.
+ * grabs the mutex, the "not in hash" flag is correct.
*/
request->in_proxy_hash = FALSE;
request_free(prequest);
}
-#ifdef WITH_PROXY
-static int proxy_id_alloc(REQUEST *request, RADIUS_PACKET *packet)
+#ifdef WITH_TCP
+static int remove_all_requests(void *ctx, void *data)
{
- int i, proxy, found;
- rad_listen_t *proxy_listener;
+ rad_listen_t *this = ctx;
+ RADIUS_PACKET **packet_p = data;
+ REQUEST *request;
+
+ request = fr_packet2myptr(REQUEST, packet, packet_p);
+ if (request->packet->sockfd != this->fd) return 0;
- if (fr_packet_list_id_alloc(proxy_list, packet)) return 1;
+ switch (request->child_state) {
+ case REQUEST_RUNNING:
+ rad_assert(request->ev != NULL); /* or it's lost forever */
+ case REQUEST_QUEUED:
+ request->master_state = REQUEST_STOP_PROCESSING;
+ return 0;
- /*
- * Allocate a new proxy fd. This function adds
- * it to the tail of the list of listeners. With
- * some care, this can be thread-safe.
- */
- proxy_listener = proxy_new_listener(&packet->src_ipaddr, FALSE);
- if (!proxy_listener) {
- RDEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+ /*
+ * Waiting for a reply. There's no point in
+ * doing anything else. We remove it from the
+ * request hash so that we can close the upstream
+ * socket.
+ */
+ case REQUEST_PROXIED:
+ remove_from_request_hash(request);
+ request->child_state = REQUEST_DONE;
return 0;
+
+ case REQUEST_REJECT_DELAY:
+ case REQUEST_CLEANUP_DELAY:
+ case REQUEST_DONE:
+ ev_request_free(&request);
+ break;
}
+
+ return 0;
+}
+
+#ifdef WITH_PROXY
+static int remove_all_proxied_requests(void *ctx, void *data)
+{
+ rad_listen_t *this = ctx;
+ RADIUS_PACKET **proxy_p = data;
+ REQUEST *request;
- /*
- * Cache it locally.
- */
- found = -1;
- proxy = proxy_listener->fd;
- for (i = 0; i < 32; i++) {
+ request = fr_packet2myptr(REQUEST, proxy, proxy_p);
+ if (request->proxy->sockfd != this->fd) return 0;
+
+ switch (request->child_state) {
+ case REQUEST_RUNNING:
+ rad_assert(request->ev != NULL); /* or it's lost forever */
+ case REQUEST_QUEUED:
+ request->master_state = REQUEST_STOP_PROCESSING;
+ return 0;
+
/*
- * Found a free entry. Save the socket,
- * and remember where we saved it.
+ * Eventually we will discover that there is no
+ * response to the proxied request.
*/
- if (proxy_fds[(proxy + i) & 0x1f] == -1) {
- found = (proxy + i) & 0x1f;
- proxy_fds[found] = proxy;
- proxy_listeners[found] = proxy_listener;
- break;
- }
+ case REQUEST_PROXIED:
+ break;
+
+ /*
+ * Keep it in the cache for duplicate detection.
+ */
+ case REQUEST_REJECT_DELAY:
+ case REQUEST_CLEANUP_DELAY:
+ case REQUEST_DONE:
+ break;
}
- rad_assert(found >= 0);
-
- if (!fr_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
- RDEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+
+ remove_from_proxy_hash(request);
+ return 0;
+}
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
+
+
+#ifdef WITH_PROXY
+static int proxy_id_alloc(REQUEST *request, RADIUS_PACKET *packet)
+{
+ void *proxy_listener;
+
+ if (fr_packet_list_id_alloc(proxy_list, packet,
+ &proxy_listener)) {
+ request->proxy_listener = proxy_listener;
+ return 1;
+ }
+
+ if (!proxy_new_listener(request->home_server, 0)) {
+ RDEBUG2("ERROR: Failed to create a new socket for proxying requests.");
return 0;
-
}
-
- if (!fr_packet_list_id_alloc(proxy_list, packet)) {
- RDEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+
+ if (!fr_packet_list_id_alloc(proxy_list, packet,
+ &proxy_listener)) {
+ RDEBUG2("ERROR: Failed allocating Id for new socket when proxying requests.");
return 0;
}
- /*
- * Signal the main thread to add the new FD to the list
- * of listening FD's.
- */
- radius_signal_self(RADIUS_SIGNAL_SELF_NEW_FD);
+ request->proxy_listener = proxy_listener;
return 1;
}
static int insert_into_proxy_hash(REQUEST *request, int retransmit)
{
- int i, proxy;
char buf[128];
rad_assert(request->proxy != NULL);
PTHREAD_MUTEX_LOCK(&proxy_mutex);
- /*
- * Keep track of maximum outstanding requests to a
- * particular home server. 'max_outstanding' is
- * enforced in home_server_ldb(), in realms.c.
- */
- if (request->home_server) {
- request->home_server->currently_outstanding++;
- }
-
-#ifdef WITH_TCP
- if (request->home_server->proto == IPPROTO_TCP) {
- rad_listen_t *this = NULL;
- fr_tcp_radius_t *tcp = NULL;
-
- /*
- * FIXME: Order the TCP sockets by most recently
- * used, so that we keep the TCP pipe full.
- *
- * FIXME: Try to use the same TCP connection for
- * all packets of an EAP conversation. This will
- * be hard...
- */
- request->proxy->id = -1;
- for (i = 0; i < request->home_server->max_connections; i++) {
- this = request->home_server->listeners[i];
-
- if (!this) continue;
- tcp = fr_listen2tcp(this);
- if (!tcp) continue;
-
- /*
- * If max requests per connection are
- * set, and we've reached that limit,
- * then skip the connection. It will be
- * closed later, when all of the
- * responses come back.
- */
- if (request->home_server->max_requests &&
- (tcp->num_packets >= request->home_server->max_requests)) {
- continue;
- }
-
- /*
- * FIXME: This is inefficient. Use
- * something else, like MRU.
- */
- for (i = 0; i < 256; i++) {
- if (tcp->ids[i]) continue;
-
- request->proxy->id = i;
- break;
- }
-
- if (request->proxy->id >= 0) break;
- }
-
- if (request->proxy->id < 0) {
- /*
- * Allocate new listener for this home
- * server.
- *
- * FIXME: split the "connect" portion
- * into a separate call, so it can happen
- * when the mutex is NOT held!
- */
- this = proxy_new_tcp_listener(request->home_server);
- if (!this) {
- PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- DEBUG2("ERROR: Failed to allocate id");
- return 0;
- }
-
- tcp = fr_listen2tcp(this);
- if (!tcp) return 0;
-
- rad_assert(tcp->used == 0);
- rad_assert(tcp->ids[0] == NULL);
- request->proxy->id = 0;
-
- /*
- * FIXME: Start timers as per RFC 3539
- */
-
- }
-
- rad_assert(tcp != NULL);
- rad_assert(tcp->ids[request->proxy->id] == NULL);
-
- tcp->ids[request->proxy->id] = &request->proxy;
- request->proxy_listener = this;
- request->proxy->sockfd = tcp->fd;
- request->proxy->src_ipaddr = tcp->src_ipaddr;
- request->proxy->dst_ipaddr = tcp->dst_ipaddr;
- request->proxy->src_port = tcp->src_port;
- request->proxy->dst_port = tcp->dst_port;
-
- tcp->num_packets++;
-
- if (!fr_packet_list_insert(proxy_list, &request->proxy)) {
+ if (!retransmit) {
+ if (!proxy_id_alloc(request, request->proxy)) {
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- DEBUG2("ERROR: Failed to insert entry into proxy list");
return 0;
}
+ } else {
+ RADIUS_PACKET packet;
- tcp->used++;
- tcp->ids[request->proxy->id] = &request->proxy;
-
- goto done;
- }
+#ifdef WITH_TCP
+ rad_assert(request->home_server->proto != IPPROTO_TCP);
#endif
- if (retransmit) {
- RADIUS_PACKET packet;
-
packet = *request->proxy;
if (!proxy_id_alloc(request, &packet)) {
fr_packet_list_yank(proxy_list, request->proxy);
fr_packet_list_id_free(proxy_list, request->proxy);
*request->proxy = packet;
+ }
- } else if (!proxy_id_alloc(request, request->proxy)) {
+ if (!fr_packet_list_insert(proxy_list, &request->proxy)) {
+ fr_packet_list_id_free(proxy_list, request->proxy);
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ RDEBUG2("ERROR: Failed to insert entry into proxy list");
return 0;
}
- rad_assert(request->proxy->sockfd >= 0);
+ request->in_proxy_hash = TRUE;
/*
- * FIXME: Hack until we get rid of rad_listen_t, and put
- * the information into the packet_list.
+ * Keep track of maximum outstanding requests to a
+ * particular home server. 'max_outstanding' is
+ * enforced in home_server_ldb(), in realms.c.
*/
- proxy = -1;
- for (i = 0; i < 32; i++) {
- if (proxy_fds[i] == request->proxy->sockfd) {
- proxy = i;
- break;
- }
- }
-
- if (proxy < 0) {
- PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- RDEBUG2("ERROR: All sockets are full.");
- return 0;
- }
-
- rad_assert(proxy_fds[proxy] != -1);
- rad_assert(proxy_listeners[proxy] != NULL);
- request->proxy_listener = proxy_listeners[proxy];
-
- if (!fr_packet_list_insert(proxy_list, &request->proxy)) {
- fr_packet_list_id_free(proxy_list, request->proxy);
- PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- RDEBUG2("ERROR: Failed to insert entry into proxy list");
- return 0;
+ if (request->home_server) {
+ request->home_server->currently_outstanding++;
}
#ifdef WITH_TCP
- done:
+ request->proxy_listener->count++;
#endif
- request->in_proxy_hash = TRUE;
-
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
RDEBUG3(" proxy: allocating destination %s port %d - Id %d",
if (have_children) wait_a_bit(request);
}
-#ifdef WITH_TCP
-static void no_response_to_tcp_request(REQUEST *request)
-{
- fr_tcp_radius_t *tcp;
- rad_listen_t *proxy_listener;
-
- proxy_listener = request->proxy_listener;
- tcp = fr_listen2tcp(proxy_listener);
- /* MAY be NULL if proxy_listener is NULL */
-
- /*
- * May have been closed by someone else.
- */
- if (!request->proxy_listener) return;
-
- rad_assert(tcp != NULL);
- rad_assert(tcp->state != HOME_STATE_IS_DEAD);
-
- /*
- * There's already an event set up to mark it dead.
- */
- if (tcp->state == HOME_STATE_ZOMBIE) {
- rad_assert(tcp->ev != NULL);
- return;
- }
-
- /*
- * Enable the zombie period when we notice that the home
- * server hasn't responded. We do NOT back-date the start
- * of the zombie period.
- */
- if (tcp->state == HOME_STATE_ALIVE) {
- char buffer[128];
- radlog(L_ERR, "PROXY: Marking TCP connection to %s port %d as zombie (it looks like it is dead).",
- inet_ntop(tcp->dst_ipaddr.af, &tcp->dst_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- tcp->dst_port);
- tcp->state = HOME_STATE_ZOMBIE;
- }
-}
-#endif /* WITH_TCP */
-
/* maybe check this against wait_for_proxy_id_to_expire? */
static void no_response_to_proxied_request(void *ctx)
{
return;
}
- home = request->home_server;
-
#ifdef WITH_TCP
- /*
- * If there's no response, then the connection is likely
- * dead.
- */
- if (home->proto == IPPROTO_TCP) {
- no_response_to_tcp_request(request);
- } else
+ if (request->home_server->proto != IPPROTO_TCP)
#endif
- check_for_zombie_home_server(request);
+ check_for_zombie_home_server(request);
/*
* The default as of 2.1.7 is to allow requests to
post_proxy_fail_handler(request);
}
+ home = request->home_server;
+
/*
* Don't touch request due to race conditions
*/
+
+#ifdef WITH_TCP
+ /*
+ * Do nothing more. The home server didn't respond,
+ * but that isn't a catastrophic failure. Some home
+ * servers don't respond to packets...
+ */
+ if (home->proto == IPPROTO_TCP) {
+ /*
+ * FIXME: Set up TCP pinging on this connection.
+ *
+ * Maybe the CONNECTION is dead, but the home
+ * server is alive. In that case, we need to start
+ * pinging on the connection.
+ *
+ * This means doing the pinging BEFORE the
+ * post_proxy_fail_handler above, as it may do
+ * something with the request, and cause the
+ * proxy listener to go away!
+ */
+ return;
+ }
+#endif
+
if (home->state == HOME_STATE_IS_DEAD) {
rad_assert(home->ev != NULL); /* or it will never wake up */
return;
}
#endif
-#ifdef WITH_TCP
-static void tcp_socket_lifetime(void *ctx)
-{
- rad_listen_t *listener = ctx;
-
- DEBUG("Reached lifetime on outgoing connection to home server");
- proxy_close_tcp_listener(listener);
-}
-
-static void tcp_idle_timeout(void *ctx)
-{
- rad_listen_t *listener = ctx;
- fr_tcp_radius_t *tcp = fr_listen2tcp(listener);
-
- /*
- * Rather than adding && deleting the timer when "used"
- * changes from 1->0, or from 0->1, we just add it when
- * "used" reaches zero, and don't touch it after that.
- * However, in order to avoid deleting used sockets, we
- * double-check things here.
- */
- if (tcp->used != 0) {
- /*
- * If it's NOT idle, we still need to enforce
- * maximum lifetime.
- */
- if (tcp->lifetime > 0) {
- struct timeval when;
-
- when.tv_sec = tcp->opened;
- when.tv_sec += tcp->lifetime;
- when.tv_usec = fr_rand() & 0xffff;
-
- fr_event_insert(el, tcp_socket_lifetime,
- listener,
- &when, (fr_event_t **) &tcp->ev);
- }
- return;
- }
-
-
- DEBUG("Reasched idle timeout on outgoing connection to home server");
- proxy_close_tcp_listener(listener);
-}
-#endif
-
-
static int request_pre_handler(REQUEST *request)
{
int rcode;
* Put the decoded packet into it's proper place.
*/
if (request->proxy_reply != NULL) {
- rcode = request->proxy_listener->decode(request->proxy_listener,
- request);
+ /*
+ * FIXME: For now, we can only proxy RADIUS packets.
+ *
+ * In order to proxy other packets, we need to
+ * somehow cache the "decode" function.
+ */
+ rcode = rad_decode(request->proxy_reply, request->proxy,
+ request->home_server->secret);
DEBUG_PACKET(request, request->proxy_reply, 0);
} else
#endif
*/
case REQUEST_PROXIED:
default:
- rad_assert(request->ev != NULL);
break;
}
}
#ifdef WITH_PROXY
-#ifdef WITH_TCP
-/*
- * received_proxy_response is split in two for TCP handling.
- */
-static REQUEST *received_proxy_response_p2(RADIUS_PACKET *packet,
- REQUEST *request);
-#endif
-
REQUEST *received_proxy_response(RADIUS_PACKET *packet)
{
-#ifndef WITH_TCP
char buffer[128];
- home_server *home;
-#endif
REQUEST *request;
/*
*/
request = lookup_in_proxy_hash(packet);
-#ifdef WITH_TCP
- return received_proxy_response_p2(packet, request);
-}
-
-static REQUEST *received_proxy_response_p2(RADIUS_PACKET *packet,
- REQUEST *request)
-{
- char buffer[128];
- fr_tcp_radius_t *tcp;
-#endif
-
if (!request) {
radlog(L_PROXY, "No outstanding request was found for reply from host %s port %d - ID %d",
inet_ntop(packet->src_ipaddr.af,
request->priority = RAD_LISTEN_PROXY;
tv_add(&request->when, request->delay);
-#ifdef WITH_TCP
- tcp = fr_listen2tcp(request->proxy_listener);
- if (tcp) {
- if ((tcp->lifetime > 0) &&
- (now.tv_sec > (tcp->opened + tcp->lifetime))) {
- proxy_close_tcp_listener(request->proxy_listener);
- } else
-
- /*
- * No outstanding packets. Set up idle timeout
- * timer, but only if we didn't already do so
- * this second.
- */
- if ((tcp->used == 0) &&
- (request->home_server->idle_timeout != 0) &&
- (tcp->last_packet != now.tv_sec)) {
- struct timeval when;
-
- when = now;
- when.tv_sec += request->home_server->idle_timeout;
-
- fr_event_insert(el, tcp_idle_timeout,
- request->proxy_listener,
- &when, (fr_event_t **) &tcp->ev);
- }
-
- tcp->last_packet = request->proxy_reply->timestamp = now.tv_sec;
- }
-#endif
-
/*
* Wait a bit will take care of max_request_time
*/
return request;
}
+#endif /* WITH_PROXY */
+
#ifdef WITH_TCP
-REQUEST *received_proxy_tcp_response(RADIUS_PACKET *packet,
- fr_tcp_radius_t *tcp)
+static void tcp_socket_lifetime(void *ctx)
{
- REQUEST *request;
+ rad_listen_t *listener = ctx;
+ char buffer[256];
- /*
- * Not found. Print the same message as all the other
- * code paths.
- */
- if (!tcp || !tcp->ids[packet->id]) {
- return received_proxy_response_p2(packet, NULL);
- }
+ listener->print(listener, buffer, sizeof(buffer));
- /*
- * Find request from: tcp->ids[packet->id] = &request->proxy
- */
- request = fr_packet2myptr(REQUEST, proxy, tcp->ids[packet->id]);
+ DEBUG("Reached maximum lifetime on socket %s", buffer);
- /*
- * TCP sockets don't do re-transmits. Just nuke it.
- */
- remove_from_proxy_hash(request);
+ listener->status = RAD_LISTEN_STATUS_CLOSED;
+ event_new_fd(listener);
+}
+
+static void tcp_socket_idle_timeout(void *ctx)
+{
+ rad_listen_t *listener = ctx;
+ listen_socket_t *sock = listener->data;
+ char buffer[256];
+
+ fr_event_now(el, &now); /* should always succeed... */
+
+ rad_assert(sock->home != NULL);
/*
- * FIXME: Update RFC 3539 watchdog timer.
+ * We implement idle timeout by polling, because it's
+ * cheaper than resetting the idle timeout every time
+ * we send / receive a packet.
*/
+ if ((sock->last_packet + sock->home->idle_timeout) > now.tv_sec) {
+ struct timeval when;
+ void *fun = tcp_socket_idle_timeout;
+
+ when.tv_sec = sock->last_packet;
+ when.tv_sec += sock->home->idle_timeout;
+ when.tv_usec = 0;
+
+ if (sock->home->lifetime &&
+ (sock->opened + sock->home->lifetime < when.tv_sec)) {
+ when.tv_sec = sock->opened + sock->home->lifetime;
+ fun = tcp_socket_lifetime;
+ }
+
+ if (!fr_event_insert(el, fun, listener, &when, &sock->ev)) {
+ rad_panic("Failed to insert event");
+ }
+
+ return;
+ }
+
+ listener->print(listener, buffer, sizeof(buffer));
+
+ DEBUG("Reached idle timeout on socket %s", buffer);
- return received_proxy_response_p2(packet, request);
+ listener->status = RAD_LISTEN_STATUS_CLOSED;
+ event_new_fd(listener);
}
-#endif /* WITH_TCP */
-#endif /* WITH_PROXY */
+#endif
void event_new_fd(rad_listen_t *this)
{
char buffer[1024];
if (this->status == RAD_LISTEN_STATUS_KNOWN) return;
-
+
this->print(this, buffer, sizeof(buffer));
-
+
if (this->status == RAD_LISTEN_STATUS_INIT) {
if (just_started) {
DEBUG("Listening on %s", buffer);
} else {
DEBUG2(" ... adding new socket %s", buffer);
}
+
+#ifdef WITH_PROXY
+ /*
+ * Add it to the list of sockets we can use.
+ * Server sockets (i.e. auth/acct) are never
+ * added to the packet list.
+ */
+ if (this->type == RAD_LISTEN_PROXY) {
+ listen_socket_t *sock = this->data;
+
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ if (!fr_packet_list_socket_add(proxy_list, this->fd,
+ &sock->other_ipaddr, sock->other_port,
+ this)) {
+ radlog(L_ERR, "Fatal error adding socket: %s",
+ fr_strerror());
+ exit(1);
+
+ }
+
+ if (sock->home) {
+ sock->home->num_connections++;
+
+ /*
+ * If necessary, add it to the list of
+ * new proxy listeners.
+ */
+ if (sock->home->lifetime || sock->home->idle_timeout) {
+ this->next = proxy_listener_list;
+ proxy_listener_list = this;
+ }
+ }
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+ /*
+ * Tell the main thread that we've added
+ * a proxy listener, but only if we need
+ * to update the event list. Do this
+ * with the mutex unlocked, to reduce
+ * contention.
+ */
+ if (sock->home) {
+ if (sock->home->lifetime || sock->home->idle_timeout) {
+ radius_signal_self(RADIUS_SIGNAL_SELF_NEW_FD);
+ }
+ }
+ }
+#endif
+
+#ifdef WITH_DETAIL
+ /*
+ * Detail files are always known, and aren't
+ * put into the socket event loop.
+ */
+ if (this->type == RAD_LISTEN_DETAIL) {
+ this->status = RAD_LISTEN_STATUS_KNOWN;
+
+ /*
+ * Set up the first poll interval.
+ */
+ event_poll_detail(this);
+ return;
+ }
+#endif
+
+ FD_MUTEX_LOCK(&fd_mutex);
if (!fr_event_fd_insert(el, 0, this->fd,
event_socket_handler, this)) {
radlog(L_ERR, "Failed remembering handle for proxy socket!");
exit(1);
}
+ FD_MUTEX_UNLOCK(&fd_mutex);
-#ifdef WITH_TCP
- if (this->type == RAD_LISTEN_PROXY) {
- fr_tcp_radius_t *tcp = fr_listen2tcp(this);
+ this->status = RAD_LISTEN_STATUS_KNOWN;
+ return;
+ }
+
+ /*
+ * Something went wrong with the socket: make it harmless.
+ */
+ if (this->status == RAD_LISTEN_STATUS_REMOVE_FD) {
+ int devnull;
+
+ /*
+ * Remove it from the list of live FD's.
+ */
+ FD_MUTEX_LOCK(&fd_mutex);
+ fr_event_fd_delete(el, 0, this->fd);
+ FD_MUTEX_UNLOCK(&fd_mutex);
+
+#ifdef WITH_PROXY
+ if (this->type != RAD_LISTEN_PROXY)
+#endif
+ {
+ if (this->count != 0) {
+ fr_packet_list_walk(pl, this,
+ remove_all_requests);
+ }
+
+ if (this->count == 0) {
+ this->status = RAD_LISTEN_STATUS_FINISH;
+ goto finish;
+ }
+ }
+#ifdef WITH_PROXY
+ else {
+ int count = this->count;
- if (tcp && (tcp->lifetime > 0)) {
- struct timeval when;
+ /*
+ * Duplicate code
+ */
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ if (!fr_packet_list_socket_freeze(proxy_list,
+ this->fd)) {
+ radlog(L_ERR, "Fatal error freezing socket: %s",
+ fr_strerror());
+ exit(1);
+ }
- gettimeofday(&when, NULL);
- when.tv_sec += tcp->lifetime;
+ /*
+ * Doing this with the proxy mutex held
+ * is a Bad Thing. We should move to
+ * finer-grained mutexes.
+ */
+ count = this->count;
+ if (count > 0) {
+ fr_packet_list_walk(proxy_list, this,
+ remove_all_proxied_requests);
+ }
+ count = this->count; /* protected by mutex */
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- fr_event_insert(el, tcp_socket_lifetime,
- this,
- &when, (fr_event_t **) &tcp->ev);
+ if (count == 0) {
+ this->status = RAD_LISTEN_STATUS_FINISH;
+ goto finish;
}
}
#endif
- this->status = RAD_LISTEN_STATUS_KNOWN;
- return;
- }
-
- if (this->status == RAD_LISTEN_STATUS_CLOSED) {
- DEBUG2(" ... closing socket %s", buffer);
-
- fr_event_fd_delete(el, 0, this->fd);
- this->status = RAD_LISTEN_STATUS_FINISH;
-
-#ifdef WITH_TCP
/*
- * FIXME: mark ALL requests for this connection
- * as MASTER FORCE STOP! we can't reply, so
- * there's no point in doing anything
+ * Re-open the socket, pointing it to /dev/null.
+ * This means that all writes proceed without
+ * blocking, and all reads return "no data".
*
- * ALSO, create packet lists JUST for each proxy
+ * This leaves the socket active, so any child
+ * threads won't go insane. But it means that
+ * they cannot send or receive any packets.
+ *
+ * This is EXTRA work in the normal case, when
+ * sockets are closed without error. But it lets
+ * us have one simple processing method for all
+ * sockets.
+ */
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0) {
+ radlog(L_ERR, "FATAL failure opening /dev/null: %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (dup2(devnull, this->fd) < 0) {
+ radlog(L_ERR, "FATAL failure closing socket: %s",
+ strerror(errno));
+ exit(1);
+ }
+ close(devnull);
+
+ this->status = RAD_LISTEN_STATUS_CLOSED;
+
+ /*
+ * Fall through to the next section.
*/
+ }
+ /*
+ * Called ONLY from the main thread. On the following
+ * conditions:
+ *
+ * idle timeout
+ * max lifetime
+ *
+ * (and falling through from "forcibly close FD" above)
+ * client closed connection on us
+ * client sent us a bad packet.
+ */
+ if (this->status == RAD_LISTEN_STATUS_CLOSED) {
+ int count = this->count;
+ rad_assert(this->type != RAD_LISTEN_DETAIL);
+
+#ifdef WITH_PROXY
/*
- * Once we're done, the caller free's the
- * listener. However, we've got to ensure that
- * all of the requests using this listener are
- * marked as dead.
+ * Remove it from the list of active sockets, so
+ * that it isn't used when proxying new packets.
*/
if (this->type == RAD_LISTEN_PROXY) {
- int i;
- REQUEST *request;
- fr_tcp_radius_t *tcp = fr_listen2tcp(this);
-
- if (!tcp) return;
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ if (!fr_packet_list_socket_freeze(proxy_list,
+ this->fd)) {
+ radlog(L_ERR, "Fatal error freezing socket: %s",
+ fr_strerror());
+ exit(1);
+ }
+ count = this->count; /* protected by mutex */
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ }
+#endif
+
+ /*
+ * Requests are still using the socket. Wait for
+ * them to finish.
+ */
+ if (count != 0) {
+ struct timeval when;
+ listen_socket_t *sock = this->data;
+
+ /*
+ * Try again to clean up the socket in 30
+ * seconds.
+ */
+ gettimeofday(&when, NULL);
+ when.tv_sec += 30;
- for (i = 0; i < 256; i++) {
- if (!tcp->ids[i]) continue;
-
- request = fr_packet2myptr(REQUEST, proxy,
- tcp->ids[i]);
- request->proxy->sockfd = -1;
- request->proxy_listener = NULL;
- request->in_proxy_hash = FALSE;
- tcp->ids[i] = NULL;
- tcp->used--;
+ if (!fr_event_insert(el,
+ (fr_event_callback_t) event_new_fd,
+ this, &when, &sock->ev)) {
+ rad_panic("Failed to insert event");
}
- tcp->used = 0; /* just to be safe */
+
+ return;
+ }
+
+ /*
+ * No one is using this socket: we can delete it
+ * immediately.
+ */
+ this->status = RAD_LISTEN_STATUS_FINISH;
+ }
+
+finish:
+ if (this->status == RAD_LISTEN_STATUS_FINISH) {
+ listen_socket_t *sock = this->data;
+
+ rad_assert(this->count == 0);
+ DEBUG2(" ... closing socket %s", buffer);
- fr_event_delete(el, (fr_event_t **) &tcp->ev);
+ /*
+ * Remove it from the list of live FD's. Note
+ * that it MAY also have been removed above. We
+ * do it again here, to catch the case of sockets
+ * closing on idle timeout, or max
+ * lifetime... AFTER all requests have finished
+ * using it.
+ */
+ FD_MUTEX_LOCK(&fd_mutex);
+ fr_event_fd_delete(el, 0, this->fd);
+ FD_MUTEX_UNLOCK(&fd_mutex);
+
+#ifdef WITH_PROXY
+ /*
+ * Remove it from the list of sockets to be used
+ * when proxying.
+ */
+ if (this->type == RAD_LISTEN_PROXY) {
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ if (!fr_packet_list_socket_remove(proxy_list,
+ this->fd, NULL)) {
+ radlog(L_ERR, "Fatal error removing socket: %s",
+ fr_strerror());
+ exit(1);
+ }
+ if (sock->home) sock->home->num_connections--;
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
}
#endif
/*
- * Close the fd AFTER fixing up the requests and
- * listeners, so that they don't send/recv on the
- * wrong socket (if someone manages to open
- * another one).
+ * Remove any pending cleanups.
+ */
+ if (sock->ev) fr_event_delete(el, &sock->ev);
+
+ /*
+ * And finally, close the socket.
*/
- close(this->fd);
- this->fd = -1;
+ listen_free(&this);
}
}
}
#endif
+#ifdef WITH_PROXY
+ /*
+ * Add event handlers for idle timeouts && maximum lifetime.XXX
+ */
if ((flag & RADIUS_SIGNAL_SELF_NEW_FD) != 0) {
- rad_listen_t *this;
-
- for (this = mainconfig.listen;
- this != NULL;
- this = this->next) {
- event_new_fd(this);
+ struct timeval when;
+ void *fun = NULL;
+
+ if (!fr_event_now(el, &now)) gettimeofday(&now, NULL);
+
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+
+ while (proxy_listener_list) {
+ rad_listen_t *this = proxy_listener_list;
+ listen_socket_t *sock = this->data;
+
+ proxy_listener_list = this->next;
+ this->next = NULL;
+
+ if (!sock->home) continue; /* skip UDP sockets */
+
+ when = now;
+
+ if (!sock->home->idle_timeout) {
+ rad_assert(sock->home->lifetime != 0);
+
+ when.tv_sec += sock->home->lifetime;
+ fun = tcp_socket_lifetime;
+ } else {
+ rad_assert(sock->home->idle_timeout != 0);
+
+ when.tv_sec += sock->home->idle_timeout;
+ fun = tcp_socket_idle_timeout;
+ }
+
+ if (!fr_event_insert(el, fun, this, &when,
+ &(sock->ev))) {
+ rad_panic("Failed to insert event");
+ }
}
+
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
}
+#endif
}
#ifndef WITH_SELF_PIPE
*/
int radius_event_init(CONF_SECTION *cs, int spawn_flag)
{
- rad_listen_t *this, *head = NULL;
+ rad_listen_t *head = NULL;
if (el) return 0;
}
#endif /* WITH_SELF_PIPE */
-#ifdef WITH_PROXY
- /*
- * Mark the proxy Fd's as unused.
- */
- {
- int i;
-
- for (i = 0; i < 32; i++) proxy_fds[i] = -1;
- }
-#endif
-
DEBUG("%s: #### Opening IP addresses and Ports ####",
mainconfig.name);
_exit(1);
}
+ mainconfig.listen = head;
+
/*
* At this point, no one has any business *ever* going
* back to root uid.
*/
fr_suid_down_permanent();
- /*
- * Add all of the sockets to the event loop.
- */
- for (this = head;
- this != NULL;
- this = this->next) {
- char buffer[256];
-
- this->print(this, buffer, sizeof(buffer));
-
- switch (this->type) {
-#ifdef WITH_DETAIL
- case RAD_LISTEN_DETAIL:
- DEBUG("Listening on %s", buffer);
-
- /*
- * Detail files are always known, and aren't
- * put into the socket event loop.
- */
- this->status = RAD_LISTEN_STATUS_KNOWN;
-
- /*
- * Set up the first poll interval.
- */
- event_poll_detail(this);
- break;
-#endif
-
-#ifdef WITH_PROXY
- case RAD_LISTEN_PROXY:
- rad_assert(proxy_fds[this->fd & 0x1f] == -1);
- rad_assert(proxy_listeners[this->fd & 0x1f] == NULL);
-
- proxy_fds[this->fd & 0x1f] = this->fd;
- proxy_listeners[this->fd & 0x1f] = this;
- if (!fr_packet_list_socket_add(proxy_list,
- this->fd)) {
- rad_assert(0 == 1);
- }
- /* FALL-THROUGH */
-#endif
-
- default:
- break;
- }
-
- event_new_fd(this);
-
- this->status = RAD_LISTEN_STATUS_KNOWN;
- }
-
- mainconfig.listen = head;
-
return 1;
}
#endif
+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;
-
-#ifdef SO_BINDTODEVICE
- const char *interface;
-#endif
-
-#ifdef WITH_TCP
- int proto;
-
- int max_connections;
- int num_connections;
- struct listen_socket_t *parent;
-
- fr_ipaddr_t src_ipaddr;
- int src_port;
- RADCLIENT *client; /* for server sockets */
-
- 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;
-
static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
/*
#endif
);
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;
return 0;
}
+#ifdef WITH_TCP
+static int auth_tcp_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ 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;
+ }
+
+ /*
+ * Grab the packet currently being processed.
+ */
+ packet = sock->packet;
+ sock->packet = NULL;
+
+ 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) {
+ sock->packet = packet;
+ 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 */
+ rad_free(&packet);
+
+ 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;
+ }
+
+ 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_free(&packet);
+ 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_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);
+ return 0;
+ break;
+ } /* switch over packet types */
+
+ 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(&packet);
+ return 0;
+ }
+
+ packet->dst_ipaddr = sock->my_ipaddr;
+ packet->dst_port = sock->my_port;
+
+ *pfun = fun;
+ return 1;
+}
+
+static int auth_tcp_accept(rad_listen_t *listener,
+ UNUSED RAD_REQUEST_FUNP *pfun,
+ UNUSED REQUEST **prequest)
+{
+ 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;
+
+ 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.
+ */
+ if (errno == EWOULDBLOCK) {
+ return 0;
+ }
+
+ 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);
+ RAD_STATS_TYPE_INC(listener, 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;
+
+ this->fd = newfd;
+ this->status = RAD_LISTEN_STATUS_INIT;
+ this->recv = auth_tcp_recv;
+
+ /*
+ * 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
+
+
+/*
+ * This function is stupid and complicated.
+ */
static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
{
size_t len;
#endif
#ifdef WITH_TCP
- if (sock->proto == IPPROTO_TCP) {
+ if (this->recv == auth_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->src_ipaddr, buffer, bufsize);
+ ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
FORWARD;
ADDSTRING(", ");
- snprintf(buffer, bufsize, "%d", sock->src_port);
+ snprintf(buffer, bufsize, "%d", sock->other_port);
FORWARD;
ADDSTRING(") -> (");
- 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(", ");
- snprintf(buffer, bufsize, "%d", sock->port);
+ snprintf(buffer, bufsize, "%d", sock->my_port);
FORWARD;
if (this->server) {
if ((sock->proto == IPPROTO_TCP) &&
(this->type == RAD_LISTEN_PROXY)) {
ADDSTRING(" (");
- ip_ntoh(&sock->src_ipaddr, buffer, bufsize);
+ ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
FORWARD;
ADDSTRING(", ");
- snprintf(buffer, bufsize, "%d", sock->src_port);
+ snprintf(buffer, bufsize, "%d", sock->my_port);
FORWARD;
ADDSTRING(") -> home_server (");
- if ((sock->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+ if ((sock->other_ipaddr.af == AF_INET) &&
+ (sock->other_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
strlcpy(buffer, "*", bufsize);
} else {
- ip_ntoh(&sock->ipaddr, buffer, bufsize);
+ ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
}
FORWARD;
ADDSTRING(", ");
- snprintf(buffer, bufsize, "%d", sock->port);
+ snprintf(buffer, bufsize, "%d", sock->other_port);
FORWARD;
ADDSTRING(")");
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;
if (this->server) {
return -1;
}
free(proto);
+
+ /*
+ * TCP requires a destination IP for sockets.
+ * UDP doesn't, so it's allowed.
+ */
+ 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
}
- sock->ipaddr = ipaddr;
- sock->port = listen_port;
+ sock->my_ipaddr = ipaddr;
+ sock->my_port = listen_port;
/*
* If we can bind to interfaces, do so,
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);
+ ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)),
+ sock->my_port);
return -1;
}
#ifdef WITH_TCP
if (sock->proto == IPPROTO_TCP) {
- cf_log_err(cf_sectiontoitem(cs), "TCP is not yet finished");
- return -1;
+ /*
+ * Re-write the listener receive function to
+ * allow us to accept the socket.
+ */
+ this->recv = auth_tcp_accept;
}
#endif
rad_assert(request->listener == listener);
rad_assert(listener->send == auth_socket_send);
-#ifdef WITH_TCP
- if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
-#endif
-
return rad_send(request->reply, request->packet,
request->client->secret);
}
rad_assert(request->listener == listener);
rad_assert(listener->send == acct_socket_send);
-#ifdef WITH_TCP
- if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
-#endif
-
/*
* Accounting reject's are silently dropped.
*
*/
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);
-#ifdef WITH_TCP
- if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
-#endif
-
- request->proxy->src_ipaddr = sock->ipaddr;
- request->proxy->src_port = sock->port;
-
return rad_send(request->proxy, request->packet,
request->home_server->secret);
}
int code, src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
RADCLIENT *client;
fr_ipaddr_t src_ipaddr;
&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);
- }
-
return 0;
}
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;
}
return 0;
}
- request = received_proxy_tcp_response(packet,
- fr_listen2tcp(listener));
+ packet->src_ipaddr = sock->other_ipaddr;
+ packet->src_port = sock->other_port;
+ packet->dst_ipaddr = sock->my_ipaddr;
+ packet->dst_port = sock->my_port;
+
+ /*
+ * 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_response(packet);
if (!request) {
return 0;
}
rad_assert(fun != NULL);
+ sock->opened = sock->last_packet = request->timestamp;
+
*pfun = fun;
*prequest = request;
* 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", 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;
case RAD_LISTEN_ACCT:
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;
+ sock->my_port = PW_COA_UDP_PORT;
break;
#endif
/*
* Copy fr_socket() here, as we may need to bind to a device.
*/
- this->fd = socket(sock->ipaddr.af, sock_type, 0);
+ 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;
/*
* 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)
/*
* 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);
-
- 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));
+ if (sock->my_port != 0) {
+ 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;
}
-
- if (!fr_sockaddr2ipaddr(&src, sizeof_src,
- &sock->ipaddr, &sock->port)) {
- radlog(L_ERR, "Socket has unsupported address family");
- 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;
+ }
}
}
} else
#endif
-#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));
- return -1;
- }
- }
-#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.
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;
- /*
- * 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);
- last = &(tmp->next);
- }
+ 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;
+
+#ifdef WITH_TCP
+ sock->proto = home->proto;
+ sock->last_packet = time(NULL);
+
+ if (home->proto == IPPROTO_TCP) {
+ this->recv = proxy_socket_tcp_recv;
- if (!old) {
/*
- * 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);
-
- sock = this->data;
- sock->ipaddr = *ipaddr;
+ this->fd = fr_tcp_client_socket(&home->src_ipaddr,
+ &home->ipaddr, home->port);
+ } 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) {
+ DEBUG("Failed opening client socket: %s", 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
+ */
+ event_new_fd(this);
+
+ 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 (this->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.
*/
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:
+ /*
+ * 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
+ 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) &&
+ (*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;
+#ifdef WITH_TCP
+ home.proto = IPPROTO_UDP;
+#endif
+
+ 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 (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 (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;
+ home.ipaddr.af = home.src_ipaddr.af;
+ /* everything else is already set to zero */
- /*
- * 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;
- }
- }
-
- 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;
+
+
return 0;
}
if (master_listen[this->type].free) {
master_listen[this->type].free(this);
}
+
#ifdef WITH_TCP
- if (this->type == RAD_LISTEN_PROXY) {
+ 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;
- free(sock->tcp);
+ rad_free(&sock->packet);
}
#endif
+
free(this->data);
free(this);
*head = NULL;
}
-#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;
- }
-
- this = NULL;
-
- /*
- * FIXME: Move to RBTrees.
- */
- for (i = 0; i < home->max_connections; i++) {
- if (home->listeners[i]) continue;
-
- 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));
-
- /*
- * 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;
- }
-
- 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;
- }
- }
-
- /*
- * 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);
-}
-#endif
-
#ifdef WITH_STATS
RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
int port)
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;
}
}
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 this;
}
- if ((sock->port == port) &&
- ((sock->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) {
+ if ((sock->my_port == port) &&
+ ((sock->my_ipaddr.af == AF_INET) &&
+ (sock->my_ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) {
return this;
}
#ifdef HAVE_STRUCT_SOCKADDR_IN6
- if ((sock->port == port) &&
- (sock->ipaddr.af == AF_INET6) &&
- (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr))) {
+ if ((sock->my_port == port) &&
+ (sock->my_ipaddr.af == AF_INET6) &&
+ (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr))) {
return this;
}
#endif
* this packet.
*/
retry:
- radclient->request->src_ipaddr.af = AF_UNSPEC;
- rcode = fr_packet_list_id_alloc(pl, radclient->request);
+ radclient->request->src_ipaddr.af = server_ipaddr.af;
+ rcode = fr_packet_list_id_alloc(pl, radclient->request, NULL);
if (rcode < 0) {
int mysockfd;
#ifdef WITH_TCP
if (proto) {
- mysockfd = fr_tcp_client_socket(&server_ipaddr,
+ mysockfd = fr_tcp_client_socket(NULL,
+ &server_ipaddr,
server_port);
} else
#endif
fprintf(stderr, "radclient: Can't open new socket\n");
exit(1);
}
- if (!fr_packet_list_socket_add(pl, mysockfd)) {
+ if (!fr_packet_list_socket_add(pl, mysockfd,
+ &server_ipaddr,
+ server_port, NULL)) {
fprintf(stderr, "radclient: Can't add new socket\n");
exit(1);
}
}
#ifdef WITH_TCP
if (proto) {
- sockfd = fr_tcp_client_socket(&server_ipaddr, server_port);
+ sockfd = fr_tcp_client_socket(NULL, &server_ipaddr, server_port);
} else
#endif
sockfd = fr_socket(&client_ipaddr, client_port);
exit(1);
}
- if (!fr_packet_list_socket_add(pl, sockfd)) {
+ if (!fr_packet_list_socket_add(pl, sockfd, &server_ipaddr,
+ server_port, NULL)) {
fprintf(stderr, "radclient: Out of memory\n");
exit(1);
}
}
#endif
-#ifdef WITH_PROXY
-#ifndef WITH_TCP
-#define home_server_free free
-#else
-static void home_server_free(void *data)
-{
- int i;
- home_server *home = data;
-
- if (home->proto == IPPROTO_TCP) {
- for (i = 0; i < home->max_connections; i++) {
- if (!home->listeners[i]) continue;
-
- listen_free(&home->listeners[i]);
- }
- }
-
- free(home);
-}
-
-#endif /* WITH_TCP */
-#endif /* WITH_PROXY */
-
void realms_free(void)
{
#ifdef WITH_PROXY
#ifdef WITH_PROXY
-#ifdef WITH_TCP
-static CONF_PARSER tcp_config[] = {
+static CONF_PARSER limit_config[] = {
{ "max_connections", PW_TYPE_INTEGER,
offsetof(home_server, max_connections), NULL, "16" },
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
-#endif
static struct in_addr hs_ip4addr;
static struct in6_addr hs_ip6addr;
offsetof(home_server, coa_mrd), 0, Stringify(30) },
#endif
-#ifdef WITH_TCP
- { "limit", PW_TYPE_SUBSECTION, 0, NULL, (const void *) tcp_config },
-#endif
+ { "limit", PW_TYPE_SUBSECTION, 0, NULL, (const void *) limit_config },
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
home_server *home;
int dual = FALSE;
CONF_PAIR *cp;
-#ifdef WITH_TCP
- CONF_SECTION *limit;
- int max_connections;
-#endif
free(hs_virtual_server); /* used only for printing during parsing */
hs_virtual_server = NULL;
return 0;
}
-#ifdef WITH_TCP
- max_connections = 16;
- cp = NULL;
- limit = cf_subsection_find_next(cs, NULL, "limit");
- if (limit) cp = cf_pair_find(limit, "max_connections");
- if (cp) {
- const char *value = cf_pair_value(cp);
-
- if (value) max_connections = atoi(value);
-
- if ((max_connections > 1024) ||
- (max_connections == 0)) max_connections = 1024;
-
- /*
- * Set max_connections to 1 for non-TCP sockets.
- */
- cp = cf_pair_find(cs, "proto");
- if (!cp ||
- (((value = cf_pair_value(cp)) != NULL) &&
- (strcmp(value, "tcp") != 0))) {
- max_connections = 1;
- }
- }
-#endif
-
- home = rad_malloc(sizeof(*home)
-#ifdef WITH_TCP
- + (sizeof(home->listeners[0]) * max_connections)
-#endif
- );
- memset(home, 0, sizeof(*home)
-#ifdef WITH_TCP
- + (sizeof(home->listeners[0]) * max_connections)
-#endif
- );
+ home = rad_malloc(sizeof(*home));
+ memset(home, 0, sizeof(*home));
home->name = name2;
home->cs = cs;
goto error;
}
}
-
- home->listeners[0] = NULL;
-
- /*
- * We need SOME value. Set it high enough that no one
- * will run into it.
- */
-
- if (home->proto == IPPROTO_TCP) {
- home->max_connections = max_connections;
-
- /*
- * FIXME: Parse "min_connections", too, and set
- * those up.
- */
- memset(home->listeners, 0,
- home->max_connections * sizeof(home->listeners[0]));
- }
#endif
- if ((home->ipaddr.af != AF_UNSPEC) && /* could be virtual server */
+ if (!home->server &&
rbtree_finddata(home_servers_byaddr, home)) {
cf_log_err(cf_sectiontoitem(cs), "Duplicate home server");
goto error;
}
/*
- * Look up the name using the *same* address family as
- * for the home server.
+ * If the home is a virtual server, don't look up source IP.
*/
- if (hs_srcipaddr && (home->ipaddr.af != AF_UNSPEC)) {
- if (ip_hton(hs_srcipaddr, home->ipaddr.af, &home->src_ipaddr) < 0) {
- cf_log_err(cf_sectiontoitem(cs), "Failed parsing src_ipaddr");
- goto error;
+ if (!home->server) {
+ rad_assert(home->ipaddr.af != AF_UNSPEC);
+
+ /*
+ * Otherwise look up the source IP using the same
+ * address family as the destination IP.
+ */
+ if (hs_srcipaddr) {
+ if (ip_hton(hs_srcipaddr, home->ipaddr.af, &home->src_ipaddr) < 0) {
+ cf_log_err(cf_sectiontoitem(cs), "Failed parsing src_ipaddr");
+ goto error;
+ }
+
+ } else {
+ /*
+ * Source isn't specified: Source is
+ * the correct address family, but all zeros.
+ */
+ home->src_ipaddr.af = home->ipaddr.af;
}
}
+
free(hs_srcipaddr);
hs_srcipaddr = NULL;
goto error;
}
- if ((home->ipaddr.af != AF_UNSPEC) && /* could be virtual server */
+ if (!home->server &&
!rbtree_insert(home_servers_byaddr, home)) {
rbtree_deletebydata(home_servers_byname, home);
cf_log_err(cf_sectiontoitem(cs),
if (home->coa_mrd > 60 ) home->coa_mrd = 60;
#endif
+ if (home->max_connections > 1024) home->max_connections = 1024;
+
#ifdef WITH_TCP
+ /*
+ * UDP sockets can't be connection limited.
+ */
+ if (home->proto != IPPROTO_TCP) home->max_connections = 0;
+#endif
+
if ((home->idle_timeout > 0) && (home->idle_timeout < 5))
home->idle_timeout = 5;
if ((home->lifetime > 0) && (home->lifetime < 5))
home->lifetime = 5;
if ((home->lifetime > 0) && (home->idle_timeout > home->lifetime))
home->idle_timeout = 0;
-#endif
if (dual) {
home_server *home2 = rad_malloc(sizeof(*home2));
return 0;
}
- if ((home->ipaddr.af != AF_UNSPEC) &&
+ if (!home->server &&
!rbtree_insert(home_servers_byaddr, home2)) {
rbtree_deletebydata(home_servers_byname, home2);
cf_log_err(cf_sectiontoitem(cs),
return 0;
}
-#ifdef WITH_TCP
- home2->proto = home->proto;
-#endif
-
#ifdef WITH_STATS
home2->number = home_server_max_number++;
if (!rbtree_insert(home_servers_bynumber, home2)) {
rbtree_deletebydata(home_servers_byname, home2);
- if (home2->ipaddr.af != AF_UNSPEC) {
+ if (!home2->server) {
rbtree_deletebydata(home_servers_byname, home2);
}
cf_log_err(cf_sectiontoitem(cs),
free(q);
return 0;
}
+ home->src_ipaddr.af = home->ipaddr.af;
} else {
home->ipaddr.af = AF_UNSPEC;
home->server = server;
}
#ifdef WITH_PROXY
- home_servers_byaddr = rbtree_create(home_server_addr_cmp, home_server_free, 0);
+ home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
if (!home_servers_byaddr) {
realms_free();
return 0;
/*
* Update the various fields as appropriate.
*/
+ request->proxy->src_ipaddr = found->src_ipaddr;
+ request->proxy->src_port = 0;
request->proxy->dst_ipaddr = found->ipaddr;
request->proxy->dst_port = found->port;
request->home_server = found;
}
#endif
-
-#ifdef WITH_PROXY
-static int home_server_create_callback(void *ctx, void *data)
-{
- rad_listen_t *head = ctx;
- home_server *home = data;
- rad_listen_t *this;
-
- /*
- * If there WAS a src address defined, ensure that a
- * proxy listener has been defined.
- */
- if (home->src_ipaddr.af != AF_UNSPEC) {
- this = proxy_new_listener(&home->src_ipaddr, TRUE);
-
- /*
- * Failed to create it: Die
- */
- if (!this) return 1;
-
- this->next = head->next;
- head->next = this;
- }
-
- return 0;
-}
-
-/*
- * Taking a void* here solves some header issues.
- */
-int home_server_create_listeners(void *ctx)
-{
- rad_listen_t *head = ctx;
-
- if (!home_servers_byaddr) return 0;
-
- rad_assert(head != NULL);
-
- /*
- * Add the listeners to the TAIL of the list.
- */
- while (head->next) head = head->next;
-
- if (rbtree_walk(home_servers_byaddr, InOrder,
- home_server_create_callback, head) != 0) {
- return -1;
- }
-
- return 0;
-}
-#endif