X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Flisten.c;h=9143fa2e8984b1f160c57d396d65818000efc299;hb=d912e4a79e9a74aa9e2d3ae4b8f0ed6012fec9fa;hp=e2f4f45847483b4b6de08aebbde47910ba97cb41;hpb=b1fbfb0dae9b90efd9c33c176b66fe99cbb795c4;p=freeradius.git diff --git a/src/main/listen.c b/src/main/listen.c index e2f4f45..9143fa2 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -25,26 +25,43 @@ RCSID("$Id$") #include -#include #include #include #include +#include #include +#include +#ifdef WITH_UDPFROMTO +#include +#endif + +#ifdef HAVE_SYS_RESOURCE_H #include +#endif #ifdef HAVE_NET_IF_H #include #endif -#ifdef HAVE_SYS_STAT_H -#include +#ifdef HAVE_FCNTL_H +#include #endif -#include -#define USEC (1000000) +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. @@ -62,57 +79,206 @@ typedef struct rad_listen_master_t { rad_listen_decode_t decode; } rad_listen_master_t; -typedef struct listen_socket_t { - /* - * For normal sockets. - */ - lrad_ipaddr_t ipaddr; - int port; - RADCLIENT_LIST *clients; -} listen_socket_t; - -typedef struct listen_detail_t { - const char *filename; - VALUE_PAIR *vps; - FILE *fp; - int state; - time_t timestamp; - lrad_ipaddr_t client_ip; - int load_factor; /* 1..100 */ - - int has_rtt; - int srtt; - int rttvar; - int delay_time; - struct timeval last_packet; -} listen_detail_t; - +static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type); /* * Find a per-socket client. */ RADCLIENT *client_listener_find(const rad_listen_t *listener, - const lrad_ipaddr_t *ipaddr) + const fr_ipaddr_t *ipaddr, int src_port) { - const RADCLIENT_LIST *clients; +#ifdef WITH_DYNAMIC_CLIENTS + int rcode; + REQUEST *request; + RADCLIENT *created; +#endif + time_t now; + RADCLIENT *client; + RADCLIENT_LIST *clients; + listen_socket_t *sock; rad_assert(listener != NULL); rad_assert(ipaddr != NULL); - rad_assert((listener->type == RAD_LISTEN_AUTH) || - (listener->type == RAD_LISTEN_ACCT) || - (listener->type == RAD_LISTEN_VQP)); - - clients = ((listen_socket_t *)listener->data)->clients; - if (!clients) clients = mainconfig.clients; + sock = listener->data; + clients = sock->clients; + /* + * This HAS to have been initialized previously. + */ rad_assert(clients != NULL); - return client_find(clients, ipaddr); + client = client_find(clients, ipaddr,sock->proto); + if (!client) { + char name[256], buffer[128]; + +#ifdef WITH_DYNAMIC_CLIENTS + unknown: /* used only for dynamic clients */ +#endif + + /* + * 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; + + last_printed = now; + } + + listener->print(listener, name, sizeof(name)); + + radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d" +#ifdef WITH_TCP + " proto %s" +#endif + , name, inet_ntop(ipaddr->af, &ipaddr->ipaddr, + buffer, sizeof(buffer)), src_port +#ifdef WITH_TCP + , (sock->proto == IPPROTO_UDP) ? "udp" : "tcp" +#endif + ); + return NULL; + } + +#ifndef WITH_DYNAMIC_CLIENTS + return client; /* return the found client. */ +#else + + /* + * No server defined, and it's not dynamic. Return it. + */ + if (!client->client_server && !client->dynamic) return client; + + now = time(NULL); + + /* + * It's a dynamically generated client, check it. + */ + if (client->dynamic && (src_port != 0)) { + /* + * Lives forever. Return it. + */ + if (client->lifetime == 0) return client; + + /* + * Rate-limit the deletion of known clients. + * This makes them last a little longer, but + * prevents the server from melting down if (say) + * 10k clients all expire at once. + */ + if (now == client->last_new_client) return client; + + /* + * It's not dead yet. Return it. + */ + if ((client->created + client->lifetime) > now) return client; + + /* + * This really puts them onto a queue for later + * deletion. + */ + client_delete(clients, client); + + /* + * Go find the enclosing network again. + */ + client = client_find(clients, ipaddr, sock->proto); + + /* + * WTF? + */ + if (!client) goto unknown; + if (!client->client_server) goto unknown; + + /* + * At this point, 'client' is the enclosing + * network that configures where dynamic clients + * can be defined. + */ + rad_assert(client->dynamic == 0); + + } else if (!client->dynamic && client->rate_limit) { + /* + * The IP is unknown, so we've found an enclosing + * network. Enable DoS protection. We only + * allow one new client per second. Known + * clients aren't subject to this restriction. + */ + if (now == client->last_new_client) goto unknown; + } + + client->last_new_client = now; + + request = request_alloc(); + if (!request) goto unknown; + + request->listener = listener; + request->client = client; + request->packet = rad_recv(listener->fd, 0x02); /* MSG_PEEK */ + if (!request->packet) { /* badly formed, etc */ + request_free(&request); + goto unknown; + } + request->reply = rad_alloc_reply(request->packet); + if (!request->reply) { + request_free(&request); + goto unknown; + } + request->packet->timestamp = request->timestamp; + request->number = 0; + request->priority = listener->type; + request->server = client->client_server; + request->root = &mainconfig; + + /* + * Run a fake request through the given virtual server. + * Look for FreeRADIUS-Client-IP-Address + * FreeRADIUS-Client-Secret + * ... + * + * and create the RADCLIENT structure from that. + */ + DEBUG("server %s {", request->server); + + rcode = module_authorize(0, request); + + DEBUG("} # server %s", request->server); + + if (rcode != RLM_MODULE_OK) { + request_free(&request); + goto unknown; + } + + /* + * If the client was updated by rlm_dynamic_clients, + * don't create the client from attribute-value pairs. + */ + if (request->client == client) { + created = client_create(clients, request); + } else { + created = request->client; + + /* + * This frees the client if it isn't valid. + */ + if (!client_validate(clients, client, created)) goto unknown; + } + request_free(&request); + + if (!created) goto unknown; + + return created; +#endif } static int listen_bind(rad_listen_t *this); + /* * Process and reply to a server-status request. * Like rad_authenticate and rad_accounting this should @@ -124,8 +290,11 @@ static int rad_status_server(REQUEST *request) DICT_VALUE *dval; switch (request->listener->type) { +#ifdef WITH_STATS + 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 { @@ -150,8 +319,9 @@ static int rad_status_server(REQUEST *request) } break; +#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 { @@ -169,41 +339,315 @@ static int rad_status_server(REQUEST *request) break; } break; +#endif + +#ifdef WITH_COA + /* + * This is a vendor extension. Suggested by Glen + * Zorn in IETF 72, and rejected by the rest of + * the WG. We like it, so it goes in here. + */ + case RAD_LISTEN_COA: + dval = dict_valbyname(PW_RECV_COA_TYPE, 0, "Status-Server"); + if (dval) { + rcode = module_recv_coa(dval->value, request); + } else { + rcode = RLM_MODULE_OK; + } + + switch (rcode) { + case RLM_MODULE_OK: + case RLM_MODULE_UPDATED: + request->reply->code = PW_COA_ACK; + break; + + default: + request->reply->code = 0; /* don't reply */ + break; + } + break; +#endif + + default: + return 0; + } + +#ifdef WITH_STATS + /* + * Full statistics are available only on a statistics + * socket. + */ + if (request->listener->type == RAD_LISTEN_NONE) { + request_stats_reply(request); + } +#endif + + return 0; +} + +#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; + 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; + } + + 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_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); + DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); + rad_free(&sock->packet); + return 0; + } + fun = rad_status_server; + break; default: + RAD_STATS_INC(radius_auth_stats.total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); + + DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED", + packet->code, client->shortname, packet->src_port); + rad_free(&sock->packet); + return 0; + } /* 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(&sock->packet); + return 0; + } + + *pfun = fun; + sock->packet = NULL; /* we have no need for more partial reads */ + 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; listen_socket_t *sock = this->data; const char *name; - char ip_buf[256]; - - if ((sock->ipaddr.af == AF_INET) && - (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) { - strcpy(ip_buf, "*"); - } else { - ip_ntoh(&sock->ipaddr, ip_buf, sizeof(ip_buf)); - } switch (this->type) { +#ifdef WITH_STATS + case RAD_LISTEN_NONE: /* what a hack... */ + name = "status"; + break; +#endif + case RAD_LISTEN_AUTH: name = "authentication"; break; +#ifdef WITH_ACCOUNTING case RAD_LISTEN_ACCT: name = "accounting"; break; +#endif +#ifdef WITH_PROXY case RAD_LISTEN_PROXY: name = "proxy"; break; +#endif #ifdef WITH_VMPS case RAD_LISTEN_VQP: @@ -211,19 +655,134 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) break; #endif +#ifdef WITH_DHCP + case RAD_LISTEN_DHCP: + name = "dhcp"; + break; +#endif + +#ifdef WITH_COA + case RAD_LISTEN_COA: + name = "coa"; + break; +#endif + default: name = "??"; break; } - if (!this->server) { - return snprintf(buffer, bufsize, "%s address %s port %d", - name, ip_buf, sock->port); - } +#define FORWARD len = strlen(buffer); if (len >= (bufsize + 1)) return 0;buffer += len;bufsize -= len +#define ADDSTRING(_x) strlcpy(buffer, _x, bufsize);FORWARD - return snprintf(buffer, bufsize, "%s address %s port %d as server %s", - name, ip_buf, sock->port, this->server); -} + ADDSTRING(name); + + if (sock->interface) { + ADDSTRING(" interface "); + ADDSTRING(sock->interface); + } + +#ifdef WITH_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->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; + } + + /* + * 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 + + ADDSTRING(" address "); + + if ((sock->my_ipaddr.af == AF_INET) && + (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) { + strlcpy(buffer, "*", bufsize); + } else { + ip_ntoh(&sock->my_ipaddr, buffer, bufsize); + } + FORWARD; + + ADDSTRING(" port "); + snprintf(buffer, bufsize, "%d", sock->my_port); + FORWARD; + + if (this->server) { + ADDSTRING(" as server "); + ADDSTRING(this->server); + } + +#undef ADDSTRING +#undef FORWARD + + return 1; +} + +extern int check_config; /* radiusd.c */ /* @@ -233,10 +792,10 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) { int rcode; int listen_port; - lrad_ipaddr_t ipaddr; + fr_ipaddr_t ipaddr; listen_socket_t *sock = this->data; - const char *section_name = NULL; - CONF_SECTION *client_cs; + char *section_name = NULL; + CONF_SECTION *client_cs, *parentcs; /* * Try IPv4 first @@ -266,19 +825,72 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) &listen_port, "0"); if (rcode < 0) return -1; - sock->ipaddr = ipaddr; - sock->port = listen_port; + if ((listen_port < 0) || (listen_port > 65535)) { + cf_log_err(cf_sectiontoitem(cs), + "Invalid value for \"port\""); + return -1; + } + + sock->proto = IPPROTO_UDP; - /* - * And bind it to the port. - */ - if (listen_bind(this) < 0) { - char buffer[128]; + if (cf_pair_find(cs, "proto")) { +#ifndef WITH_TCP cf_log_err(cf_sectiontoitem(cs), - "Error binding to port for %s port %d", - ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)), - sock->port); + "System does not support the TCP protocol. Delete this line from the configuration file."); return -1; +#else + char *proto = NULL; + + + rcode = cf_item_parse(cs, "proto", PW_TYPE_STRING_PTR, + &proto, "udp"); + if (rcode < 0) return -1; + + if (strcmp(proto, "udp") == 0) { + sock->proto = IPPROTO_UDP; + + } else if (strcmp(proto, "tcp") == 0) { + sock->proto = IPPROTO_TCP; + + rcode = cf_item_parse(cs, "max_connections", PW_TYPE_INTEGER, + &sock->max_connections, "64"); + if (rcode < 0) return -1; + + } else { + cf_log_err(cf_sectiontoitem(cs), + "Unknown proto name \"%s\"", proto); + free(proto); + return -1; + } + free(proto); + + /* + * TCP requires a destination IP for sockets. + * UDP doesn't, so it's allowed. + */ + 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->my_ipaddr = ipaddr; + sock->my_port = listen_port; + + if (check_config) { + if (home_server_find(&sock->my_ipaddr, sock->my_port, sock->proto)) { + char buffer[128]; + + DEBUG("ERROR: We have been asked to listen on %s port %d, which is also listed as a home server. This can create a proxy loop.", + ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)), + sock->my_port); + return -1; + } + + return 0; /* don't do anything */ } /* @@ -286,14 +898,8 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) * else don't. */ if (cf_pair_find(cs, "interface")) { -#ifndef SO_BINDTODEVICE - cf_log_err(cf_sectiontoitem(cs), - "System does not support binding to interfaces. Delete this line from the configuration file."); - return -1; -#else const char *value; CONF_PAIR *cp = cf_pair_find(cs, "interface"); - struct ifreq ifreq; rad_assert(cp != NULL); value = cf_pair_value(cp); @@ -302,71 +908,130 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) "No interface name given"); return -1; } + sock->interface = value; + } - strcpy(ifreq.ifr_name, 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 (setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE, - (char *)&ifreq, sizeof(ifreq)) < 0) { + 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), - "Failed binding to interface %s: %s", - value, strerror(errno)); + "No broadcast value given"); return -1; - } /* else it worked. */ + } + + /* + * Hack... whatever happened to cf_section_parse? + */ + sock->broadcast = (strcmp(value, "yes") == 0); #endif } - - /* - * If we have a server, prefer to use clients defined - * in that server, and ignore any "clients = " - * directive, UNLESS there are no clients in the server. - */ - client_cs = NULL; - client_cs = cf_section_sub_find_name2(mainconfig.config, - "server", - this->server); - +#endif + /* - * Found a "server foo" section, but there are no - * clients in it. Don't use this section. + * And bind it to the port. */ - if (client_cs && - (cf_section_sub_find(client_cs, "client") == NULL)) { - client_cs = NULL; + 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 /* - * No clients, look for the name of a section that holds - * a list of clients. + * Proxy sockets don't have clients. */ - if (!client_cs) { - rcode = cf_item_parse(cs, "clients", PW_TYPE_STRING_PTR, - §ion_name, NULL); - if (rcode < 0) return -1; /* bad string */ - if (rcode == 0) { - /* - * Explicit list given: use it. - */ + if (this->type == RAD_LISTEN_PROXY) return 0; +#endif + + /* + * The more specific configurations are preferred to more + * generic ones. + */ + client_cs = NULL; + parentcs = cf_top_section(cs); + rcode = cf_item_parse(cs, "clients", PW_TYPE_STRING_PTR, + §ion_name, NULL); + if (rcode < 0) return -1; /* bad string */ + if (rcode == 0) { + /* + * Explicit list given: use it. + */ + client_cs = cf_section_sub_find_name2(parentcs, + "clients", + section_name); + if (!client_cs) { client_cs = cf_section_find(section_name); + } + if (!client_cs) { + cf_log_err(cf_sectiontoitem(cs), + "Failed to find clients %s {...}", + section_name); free(section_name); - if (!client_cs) { - cf_log_err(cf_sectiontoitem(cs), - "Failed to find clients %s {...}", - section_name); - return -1; - } - } /* else there was no "clients = " entry. */ + return -1; + } + free(section_name); + } /* else there was no "clients = " entry. */ + + if (!client_cs) { + CONF_SECTION *server_cs; + + server_cs = cf_section_sub_find_name2(parentcs, + "server", + this->server); + /* + * Found a "server foo" section. If there are clients + * in it, use them. + */ + if (server_cs && + (cf_section_sub_find(server_cs, "client") != NULL)) { + client_cs = server_cs; + } } /* - * Still nothing. Make it global. + * Still nothing. Look for global clients. */ - if (!client_cs) client_cs = mainconfig.config; + if (!client_cs) client_cs = parentcs; sock->clients = clients_parse_section(client_cs); if (!sock->clients) { + cf_log_err(cf_sectiontoitem(cs), + "Failed to load clients for this listen section"); return -1; } +#ifdef WITH_TCP + if (sock->proto == IPPROTO_TCP) { + /* + * Re-write the listener receive function to + * allow us to accept the socket. + */ + this->recv = auth_tcp_accept; + } +#endif + return 0; } @@ -383,6 +1048,7 @@ static int auth_socket_send(rad_listen_t *listener, REQUEST *request) } +#ifdef WITH_ACCOUNTING /* * Send an accounting response packet (or not) */ @@ -402,8 +1068,9 @@ static int acct_socket_send(rad_listen_t *listener, REQUEST *request) return rad_send(request->reply, request->packet, request->client->secret); } +#endif - +#ifdef WITH_PROXY /* * Send a packet to a home server. * @@ -411,17 +1078,81 @@ static int acct_socket_send(rad_listen_t *listener, REQUEST *request) */ static int proxy_socket_send(rad_listen_t *listener, REQUEST *request) { - listen_socket_t *sock = listener->data; - rad_assert(request->proxy_listener == listener); rad_assert(listener->send == proxy_socket_send); - request->proxy->src_ipaddr = sock->ipaddr; - request->proxy->src_port = sock->port; - return rad_send(request->proxy, request->packet, request->home_server->secret); } +#endif + +#ifdef WITH_STATS +/* + * Check if an incoming request is "ok" + * + * It takes packets, not requests. It sees if the packet looks + * OK. If so, it does a number of sanity checks on it. + */ +static int stats_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + ssize_t rcode; + int code, src_port; + RADIUS_PACKET *packet; + RADCLIENT *client; + fr_ipaddr_t src_ipaddr; + + rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); + if (rcode < 0) return 0; + + RAD_STATS_TYPE_INC(listener, total_requests); + + if (rcode < 20) { /* AUTH_HDR_LEN */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + return 0; + } + + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); + return 0; + } + + /* + * We only understand Status-Server on this socket. + */ + if (code != PW_STATUS_SERVER) { + DEBUG("Ignoring packet code %d sent to Status-Server port", + code); + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); + return 0; + } + + /* + * Now that we've sanity checked everything, receive the + * packet. + */ + packet = rad_recv(listener->fd, 1); /* require message authenticator */ + if (!packet) { + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + DEBUG("%s", fr_strerror()); + return 0; + } + + if (!received_request(listener, packet, prequest, client)) { + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); + rad_free(&packet); + return 0; + } + + *pfun = rad_status_server; + return 1; +} +#endif /* @@ -437,32 +1168,23 @@ static int auth_socket_recv(rad_listen_t *listener, int code, src_port; RADIUS_PACKET *packet; RAD_REQUEST_FUNP fun = NULL; - char buffer[128]; RADCLIENT *client; - lrad_ipaddr_t src_ipaddr; + fr_ipaddr_t src_ipaddr; rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); if (rcode < 0) return 0; - RAD_SNMP_TYPE_INC(listener, total_requests); + RAD_STATS_TYPE_INC(listener, total_requests); if (rcode < 20) { /* AUTH_HDR_LEN */ - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); + RAD_STATS_TYPE_INC(listener, total_malformed_requests); return 0; } if ((client = client_listener_find(listener, - &src_ipaddr)) == NULL) { + &src_ipaddr, src_port)) == NULL) { rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_invalid_requests); - - /* - * This is debugging rather than logging, so that - * DoS attacks don't affect us. - */ - DEBUG("Ignoring request from unknown client %s port %d", - inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr, - buffer, sizeof(buffer)), src_port); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); return 0; } @@ -471,15 +1193,15 @@ static int auth_socket_recv(rad_listen_t *listener, */ switch(code) { case PW_AUTHENTICATION_REQUEST: - RAD_SNMP_CLIENT_INC(listener, client, requests); + 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_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, packets_dropped); + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); return 0; } @@ -488,8 +1210,8 @@ static int auth_socket_recv(rad_listen_t *listener, default: rad_recv_discard(listener->fd); - RAD_SNMP_INC(rad_snmp.auth.total_unknown_types); - RAD_SNMP_CLIENT_INC(listener, client, unknown_types); + RAD_STATS_INC(radius_auth_stats.total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED", code, client->shortname, src_port); @@ -501,16 +1223,16 @@ static int auth_socket_recv(rad_listen_t *listener, * Now that we've sanity checked everything, receive the * packet. */ - packet = rad_recv(listener->fd); + packet = rad_recv(listener->fd, client->message_authenticator); if (!packet) { - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); - radlog(L_ERR, "%s", librad_errstr); + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + DEBUG("%s", fr_strerror()); return 0; } if (!received_request(listener, packet, prequest, client)) { - RAD_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, packets_dropped); + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); rad_free(&packet); return 0; } @@ -520,6 +1242,7 @@ static int auth_socket_recv(rad_listen_t *listener, } +#ifdef WITH_ACCOUNTING /* * Receive packets from an accounting socket */ @@ -530,32 +1253,23 @@ static int acct_socket_recv(rad_listen_t *listener, int code, src_port; RADIUS_PACKET *packet; RAD_REQUEST_FUNP fun = NULL; - char buffer[128]; RADCLIENT *client; - lrad_ipaddr_t src_ipaddr; + fr_ipaddr_t src_ipaddr; rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); if (rcode < 0) return 0; - RAD_SNMP_TYPE_INC(listener, total_requests); + RAD_STATS_TYPE_INC(listener, total_requests); if (rcode < 20) { /* AUTH_HDR_LEN */ - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); + RAD_STATS_TYPE_INC(listener, total_malformed_requests); return 0; } if ((client = client_listener_find(listener, - &src_ipaddr)) == NULL) { + &src_ipaddr, src_port)) == NULL) { rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_invalid_requests); - - /* - * This is debugging rather than logging, so that - * DoS attacks don't affect us. - */ - DEBUG("Ignoring request from unknown client %s port %d", - inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr, - buffer, sizeof(buffer)), src_port); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); return 0; } @@ -564,15 +1278,15 @@ static int acct_socket_recv(rad_listen_t *listener, */ switch(code) { case PW_ACCOUNTING_REQUEST: - RAD_SNMP_CLIENT_INC(listener, client, requests); + 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_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, unknown_types); + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); return 0; @@ -582,8 +1296,8 @@ static int acct_socket_recv(rad_listen_t *listener, default: rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_unknown_types); - RAD_SNMP_CLIENT_INC(listener, client, unknown_types); + RAD_STATS_TYPE_INC(listener, total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED", code, client->shortname, src_port); @@ -594,10 +1308,10 @@ static int acct_socket_recv(rad_listen_t *listener, * Now that we've sanity checked everything, receive the * packet. */ - packet = rad_recv(listener->fd); + packet = rad_recv(listener->fd, 0); if (!packet) { - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); - radlog(L_ERR, "%s", librad_errstr); + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + radlog(L_ERR, "%s", fr_strerror()); return 0; } @@ -605,8 +1319,8 @@ static int acct_socket_recv(rad_listen_t *listener, * There can be no duplicate accounting packets. */ if (!received_request(listener, packet, prequest, client)) { - RAD_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, packets_dropped); + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); rad_free(&packet); return 0; } @@ -614,840 +1328,522 @@ static int acct_socket_recv(rad_listen_t *listener, *pfun = fun; return 1; } +#endif +#ifdef WITH_COA /* - * Recieve packets from a proxy socket. + * 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 proxy_socket_recv(rad_listen_t *listener, - RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +static int rad_coa_reply(REQUEST *request) { - REQUEST *request; - RADIUS_PACKET *packet; - RAD_REQUEST_FUNP fun = NULL; - char buffer[128]; + VALUE_PAIR *s1, *s2; - packet = rad_recv(listener->fd); - if (!packet) { - radlog(L_ERR, "%s", librad_errstr); - return 0; + /* + * Inform the user about RFC requirements. + */ + s1 = pairfind(request->proxy->vps, PW_STATE, 0); + if (s1) { + s2 = pairfind(request->proxy_reply->vps, PW_STATE, 0); + + if (!s2) { + DEBUG("WARNING: Client was sent State in CoA, and did not respond with State."); + + } else if ((s1->length != s2->length) || + (memcmp(s1->vp_octets, s2->vp_octets, + s1->length) != 0)) { + DEBUG("WARNING: Client was sent State in CoA, and did not respond with the same State."); + } } + return RLM_MODULE_OK; +} + +/* + * Receive a CoA packet. + */ +static int rad_coa_recv(REQUEST *request) +{ + int rcode = RLM_MODULE_OK; + int ack, nak; + VALUE_PAIR *vp; + /* - * FIXME: Client MIB updates? + * Get the correct response */ - switch(packet->code) { - case PW_AUTHENTICATION_ACK: - case PW_ACCESS_CHALLENGE: - case PW_AUTHENTICATION_REJECT: - fun = rad_authenticate; + switch (request->packet->code) { + case PW_COA_REQUEST: + ack = PW_COA_ACK; + nak = PW_COA_NAK; break; - case PW_ACCOUNTING_RESPONSE: - fun = rad_accounting; + case PW_DISCONNECT_REQUEST: + ack = PW_DISCONNECT_ACK; + nak = PW_DISCONNECT_NAK; break; - default: - /* - * FIXME: Update MIB for packet types? - */ - radlog(L_ERR, "Invalid packet code %d sent to a proxy port " - "from home server %s port %d - ID %d : IGNORED", - packet->code, - ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), - packet->src_port, packet->id); - rad_free(&packet); - return 0; + default: /* shouldn't happen */ + return RLM_MODULE_FAIL; } - request = received_proxy_response(packet); - if (!request) { - return 0; - } +#ifdef WITH_PROXY +#define WAS_PROXIED (request->proxy) +#else +#define WAS_PROXIED (0) +#endif - rad_assert(fun != NULL); - *pfun = fun; - *prequest = request; - - return 1; -} - - -static int client_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) -{ - if (!request->reply->code) return 0; - - rad_encode(request->reply, request->packet, - request->client->secret); - rad_sign(request->reply, request->packet, - request->client->secret); - - return 0; -} - - -static int client_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) -{ - if (rad_verify(request->packet, NULL, - request->client->secret) < 0) { - return -1; - } - - return rad_decode(request->packet, NULL, - request->client->secret); -} - -static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) -{ - rad_encode(request->proxy, NULL, request->home_server->secret); - rad_sign(request->proxy, NULL, request->home_server->secret); - - return 0; -} - - -static int proxy_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) -{ - if (rad_verify(request->proxy_reply, request->proxy, - request->home_server->secret) < 0) { - return -1; - } - - return rad_decode(request->proxy_reply, request->proxy, - request->home_server->secret); -} - - -#define STATE_UNOPENED (0) -#define STATE_UNLOCKED (1) -#define STATE_HEADER (2) -#define STATE_READING (3) -#define STATE_QUEUED (4) -#define STATE_RUNNING (5) -#define STATE_NO_REPLY (6) -#define STATE_REPLIED (7) - -/* - * If we're limiting outstanding packets, then mark the response - * as being sent. - */ -static int detail_send(rad_listen_t *listener, REQUEST *request) -{ - int rtt; - struct timeval now; - listen_detail_t *data = listener->data; - - rad_assert(request->listener == listener); - rad_assert(listener->send == detail_send); - - /* - * This request timed out. Remember that, and tell the - * caller it's OK to read more "detail" file stuff. - */ - if (request->reply->code == 0) { - radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL); - data->state = STATE_NO_REPLY; - return 0; - } - - /* - * We call gettimeofday a lot. But here it should be OK, - * because there's nothing else to do. - */ - gettimeofday(&now, NULL); - - /* - * If we haven't sent a packet in the last second, reset - * the RTT. - */ - now.tv_sec -= 1; - if (timercmp(&data->last_packet, &now, <)) { - data->has_rtt = FALSE; - } - now.tv_sec += 1; - - /* - * Only one detail packet may be outstanding at a time, - * so it's safe to update some entries in the detail - * structure. - * - * We keep smoothed round trip time (SRTT), but not round - * trip timeout (RTO). We use SRTT to calculate a rough - * load factor. - */ - rtt = now.tv_sec - request->received.tv_sec; - rtt *= USEC; - rtt += now.tv_usec; - rtt -= request->received.tv_usec; - - /* - * If we're proxying, the RTT is our processing time, - * plus the network delay there and back, plus the time - * on the other end to process the packet. Ideally, we - * should remove the network delays from the RTT, but we - * don't know what they are. - * - * So, to be safe, we over-estimate the total cost of - * processing the packet. - */ - if (!data->has_rtt) { - data->has_rtt = TRUE; - data->srtt = rtt; - data->rttvar = rtt / 2; + if (!WAS_PROXIED) { + /* + * RFC 5176 Section 3.3. If we have a CoA-Request + * with Service-Type = Authorize-Only, it MUST + * have a State attribute in it. + */ + vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0); + if (request->packet->code == PW_COA_REQUEST) { + if (vp && (vp->vp_integer == 17)) { + vp = pairfind(request->packet->vps, PW_STATE, 0); + if (!vp || (vp->length == 0)) { + RDEBUG("ERROR: CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute"); + request->reply->code = PW_COA_NAK; + return RLM_MODULE_FAIL; + } + } + } else if (vp) { + /* + * RFC 5176, Section 3.2. + */ + RDEBUG("ERROR: Disconnect-Request MUST NOT contain a Service-Type attribute"); + request->reply->code = PW_DISCONNECT_NAK; + return RLM_MODULE_FAIL; + } + rcode = module_recv_coa(0, request); + switch (rcode) { + case RLM_MODULE_FAIL: + case RLM_MODULE_INVALID: + case RLM_MODULE_REJECT: + case RLM_MODULE_USERLOCK: + default: + request->reply->code = nak; + break; + + case RLM_MODULE_HANDLED: + return rcode; + + case RLM_MODULE_NOOP: + case RLM_MODULE_NOTFOUND: + case RLM_MODULE_OK: + case RLM_MODULE_UPDATED: + request->reply->code = ack; + break; + } } else { - data->rttvar -= data->rttvar >> 2; - data->rttvar += (data->srtt - rtt); - data->srtt -= data->srtt >> 3; - data->srtt += rtt >> 3; + /* + * Start the reply code with the proxy reply + * code. + */ + request->reply->code = request->proxy_reply->code; } /* - * Calculate the time we wait before sending the next - * packet. - * - * rtt / (rtt + delay) = load_factor / 100 + * Copy State from the request to the reply. + * See RFC 5176 Section 3.3. */ - data->delay_time = (data->srtt * (100 - data->load_factor)) / (data->load_factor); + vp = paircopy2(request->packet->vps, PW_STATE, 0); + if (vp) pairadd(&request->reply->vps, vp); /* - * FIXME: Push this delay to the event handler! - */ - DEBUG2("RTT %d\tdelay %d", data->srtt, data->delay_time); - - usleep(data->delay_time); - data->last_packet = now; - data->state = STATE_REPLIED; - - return 0; -} - - -/* - * Open the detail file.. - * - * FIXME: create it, if it's not already there, so that the main - * server select() will wake us up if there's anything to read. - */ -static int detail_open(rad_listen_t *this) -{ - struct stat st; - char buffer[2048]; - listen_detail_t *data = this->data; - - rad_assert(data->state == STATE_UNOPENED); - snprintf(buffer, sizeof(buffer), "%s.work", data->filename); - - /* - * Open detail.work first, so we don't lose - * accounting packets. It's probably better to - * duplicate them than to lose them. - * - * Note that we're not writing to the file, but - * we've got to open it for writing in order to - * establish the lock, to prevent rlm_detail from - * writing to it. + * We may want to over-ride the reply. */ - this->fd = open(buffer, O_RDWR); - if (this->fd < 0) { + rcode = module_send_coa(0, request); + switch (rcode) { /* - * Try reading the detail file. If it - * doesn't exist, we can't do anything. - * - * Doing the stat will tell us if the file - * exists, even if we don't have permissions - * to read it. + * 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 (stat(data->filename, &st) < 0) { - return 0; - } - - /* - * Open it BEFORE we rename it, just to - * be safe... - */ - this->fd = open(data->filename, O_RDWR); - if (this->fd < 0) { - radlog(L_ERR, "Failed to open %s: %s", - data->filename, strerror(errno)); - return 0; - } - - /* - * Rename detail to detail.work - */ - if (rename(data->filename, buffer) < 0) { - close(this->fd); - this->fd = -1; - return 0; - } - } /* else detail.work existed, and we opened it */ - - rad_assert(data->vps == NULL); - - rad_assert(data->fp == NULL); - data->fp = fdopen(this->fd, "r"); - if (!data->fp) { - radlog(L_ERR, "Failed to re-open %s: %s", - data->filename, strerror(errno)); - return 0; - } - - data->state = STATE_UNLOCKED; - - data->client_ip.af = AF_UNSPEC; - data->timestamp = 0; - - return 1; -} - -/* - * FIXME: this should be dynamically allocated. - */ -static const RADCLIENT detail_client = { - { /* ipaddr */ - AF_INET, - {{ INADDR_NONE }} - }, - 32, - "", - "secret", - "UNKNOWN-CLIENT", - "other", - "", - "", - -1 -#ifdef WITH_SNMP - , NULL, NULL -#endif -}; - -/* - * FIXME: add a configuration "exit when done" so that the detail - * file reader can be used as a one-off tool to update stuff. - * - * The time sequence for reading from the detail file is: - * - * t_0 signalled that the server is idle, and we - * can read from the detail file. - * - * t_rtt the packet has been processed successfully, - * wait for t_delay to enforce load factor. - * - * t_rtt + t_delay wait for signal that the server is idle. - * - */ -static int detail_recv(rad_listen_t *listener, - RAD_REQUEST_FUNP *pfun, REQUEST **prequest) -{ - char key[256], value[1024]; - VALUE_PAIR *vp, **tail; - RADIUS_PACKET *packet; - char buffer[2048]; - listen_detail_t *data = listener->data; - - switch (data->state) { - case STATE_UNOPENED: - rad_assert(listener->fd < 0); - - /* - * FIXME: If the file doesn't exist, then - * return "sleep for 1s", to avoid busy - * looping. - */ - if (!detail_open(listener)) return 0; - - rad_assert(data->state == STATE_UNLOCKED); - rad_assert(listener->fd >= 0); - - /* FALL-THROUGH */ - - /* - * Try to lock fd. If we can't, return. - * If we can, continue. This means that - * the server doesn't block while waiting - * for the lock to open... - */ - case STATE_UNLOCKED: - /* - * Note that we do NOT block waiting for - * the lock. We've re-named the file - * above, so we've already guaranteed - * that any *new* detail writer will not - * be opening this file. The only - * purpose of the lock is to catch a race - * condition where the execution - * "ping-pongs" between radiusd & - * radrelay. - */ - if (rad_lockfd_nonblock(listener->fd, 0) < 0) { - return 0; - } + case RLM_MODULE_FAIL: + case RLM_MODULE_INVALID: + case RLM_MODULE_REJECT: + case RLM_MODULE_USERLOCK: + default: /* - * Look for the header + * Over-ride an ACK with a NAK */ - data->state = STATE_HEADER; - data->vps = NULL; - - /* FALL-THROUGH */ - - case STATE_HEADER: - do_header: + request->reply->code = nak; + break; + + case RLM_MODULE_HANDLED: + return rcode; + + case RLM_MODULE_NOOP: + case RLM_MODULE_NOTFOUND: + case RLM_MODULE_OK: + case RLM_MODULE_UPDATED: /* - * End of file. Delete it, and re-set - * everything. + * Do NOT over-ride a previously set value. + * Otherwise an "ok" here will re-write a + * NAK to an ACK. */ - if (feof(data->fp)) { - cleanup: - snprintf(buffer, sizeof(buffer), - "%s.work", data->filename); - unlink(buffer); - fclose(data->fp); /* closes listener->fd */ - data->fp = NULL; - listener->fd = -1; - data->state = STATE_UNOPENED; - rad_assert(data->vps == NULL); - return 0; + if (request->reply->code == 0) { + request->reply->code = ack; } - - /* - * Else go read something. - */ break; - /* - * Read more value-pair's, unless we're - * at EOF. In that case, queue whatever - * we have. - */ - case STATE_READING: - if (!feof(data->fp)) break; - data->state = STATE_QUEUED; - - /* FALL-THROUGH */ - - case STATE_QUEUED: - goto alloc_packet; - - /* - * We still have an outstanding packet. - * Don't read any more. - */ - case STATE_RUNNING: - return 0; - - /* - * If there's no reply, keep - * retransmitting the current packet - * forever. - */ - case STATE_NO_REPLY: - data->state = STATE_QUEUED; - goto alloc_packet; - - /* - * We have a reply. Clean up the old - * request, and go read another one. - */ - case STATE_REPLIED: - pairfree(&data->vps); - data->state = STATE_HEADER; - goto do_header; } - - tail = &data->vps; - while (*tail) tail = &(*tail)->next; - /* - * Read a header, OR a value-pair. - */ - while (fgets(buffer, sizeof(buffer), data->fp)) { - /* - * Badly formatted file: delete it. - * - * FIXME: Maybe flag an error? - */ - if (!strchr(buffer, '\n')) { - pairfree(&data->vps); - goto cleanup; - } - - /* - * We're reading VP's, and got a blank line. - * Queue the packet. - */ - if ((data->state == STATE_READING) && - (buffer[0] == '\n')) { - data->state = STATE_QUEUED; - break; - } - - /* - * Look for date/time header, and read VP's if - * found. If not, keep reading lines until we - * find one. - */ - if (data->state == STATE_HEADER) { - int y; + return RLM_MODULE_OK; +} - if (sscanf(buffer, "%*s %*s %*d %*d:%*d:%*d %d", &y)) { - data->state = STATE_READING; - } - continue; - } - /* - * We have a full "attribute = value" line. - * If it doesn't look reasonable, skip it. - * - * FIXME: print an error for badly formatted attributes? - */ - if (sscanf(buffer, "%255s = %1023s", key, value) != 2) { - continue; - } - - /* - * Skip non-protocol attributes. - */ - if (!strcasecmp(key, "Request-Authenticator")) continue; +/* + * Check if an incoming request is "ok" + * + * It takes packets, not requests. It sees if the packet looks + * OK. If so, it does a number of sanity checks on it. + */ +static int coa_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + ssize_t rcode; + int code, src_port; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + RADCLIENT *client; + fr_ipaddr_t src_ipaddr; - /* - * Set the original client IP address, based on - * what's in the detail file. - * - * Hmm... we don't set the server IP address. - * or port. Oh well. - */ - if (!strcasecmp(key, "Client-IP-Address")) { - data->client_ip.af = AF_INET; - ip_hton(value, AF_INET, &data->client_ip); - continue; - } + rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); + if (rcode < 0) return 0; - /* - * The original time at which we received the - * packet. We need this to properly calculate - * Acct-Delay-Time. - */ - if (!strcasecmp(key, "Timestamp")) { - data->timestamp = atoi(value); - continue; - } + RAD_STATS_TYPE_INC(listener, total_requests); - /* - * Read one VP. - * - * FIXME: do we want to check for non-protocol - * attributes like radsqlrelay does? - */ - vp = NULL; - if ((userparse(buffer, &vp) > 0) && - (vp != NULL)) { - *tail = vp; - tail = &(vp->next); - } + if (rcode < 20) { /* AUTH_HDR_LEN */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + return 0; } - /* - * Some kind of error. - * - * FIXME: Leave the file in-place, and warn the - * administrator? - */ - if (ferror(data->fp)) goto cleanup; + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); + return 0; + } /* - * Process the packet. + * Some sanity checks, based on the packet code. */ - alloc_packet: - rad_assert(data->state == STATE_QUEUED); + switch(code) { + case PW_COA_REQUEST: + case PW_DISCONNECT_REQUEST: + fun = rad_coa_recv; + break; - /* - * We're done reading the file, but we didn't read - * anything. Clean up, and don't return anything. - */ - if (!data->vps) { - data->state = STATE_HEADER; + default: + rad_recv_discard(listener->fd); + DEBUG("Invalid packet code %d sent to coa port from client %s port %d : IGNORED", + code, client->shortname, src_port); return 0; - } + break; + } /* switch over packet types */ /* - * Allocate the packet. If we fail, it's a serious - * problem. + * Now that we've sanity checked everything, receive the + * packet. */ - packet = rad_alloc(1); + packet = rad_recv(listener->fd, client->message_authenticator); if (!packet) { - data->state = STATE_NO_REPLY; /* try again later */ - return 0; /* maybe memory will magically free up... */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + DEBUG("%s", fr_strerror()); + return 0; } - memset(packet, 0, sizeof(*packet)); - packet->sockfd = -1; - packet->src_ipaddr.af = AF_INET; - packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE); - packet->code = PW_ACCOUNTING_REQUEST; - packet->timestamp = time(NULL); - - /* - * Remember where it came from, so that we don't - * proxy it to the place it came from... - */ - if (data->client_ip.af != AF_UNSPEC) { - packet->src_ipaddr = data->client_ip; + if (!received_request(listener, packet, prequest, client)) { + rad_free(&packet); + return 0; } - vp = pairfind(packet->vps, PW_PACKET_SRC_IP_ADDRESS); - if (vp) { - packet->src_ipaddr.af = AF_INET; - packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; - } else { - vp = pairfind(packet->vps, PW_PACKET_SRC_IPV6_ADDRESS); - if (vp) { - packet->src_ipaddr.af = AF_INET6; - memcpy(&packet->src_ipaddr.ipaddr.ip6addr, - &vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr)); - } - } + *pfun = fun; + return 1; +} +#endif + +#ifdef WITH_PROXY +/* + * Recieve packets from a proxy socket. + */ +static int proxy_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + REQUEST *request; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + char buffer[128]; - vp = pairfind(packet->vps, PW_PACKET_DST_IP_ADDRESS); - if (vp) { - packet->dst_ipaddr.af = AF_INET; - packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; - } else { - vp = pairfind(packet->vps, PW_PACKET_DST_IPV6_ADDRESS); - if (vp) { - packet->dst_ipaddr.af = AF_INET6; - memcpy(&packet->dst_ipaddr.ipaddr.ip6addr, - &vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr)); - } + packet = rad_recv(listener->fd, 0); + if (!packet) { + radlog(L_ERR, "%s", fr_strerror()); + return 0; } /* - * We've got to give SOME value for Id & ports, so that - * the packets can be added to the request queue. - * However, we don't want to keep track of used/unused - * id's and ports, as that's a lot of work. This hack - * ensures that (if we have real random numbers), that - * there will be a collision on every 2^(16+15+15+24 - 1) - * packets, on average. That means we can read 2^37 - * packets before having a collision, which means it's - * effectively impossible. + * FIXME: Client MIB updates? */ - packet->id = lrad_rand() & 0xffff; - packet->src_port = 1024 + (lrad_rand() & 0x7fff); - packet->dst_port = 1024 + (lrad_rand() & 0x7fff); + switch(packet->code) { + case PW_AUTHENTICATION_ACK: + case PW_ACCESS_CHALLENGE: + case PW_AUTHENTICATION_REJECT: + fun = rad_authenticate; + break; - packet->dst_ipaddr.af = AF_INET; - packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl((INADDR_LOOPBACK & ~0xffffff) | (lrad_rand() & 0xffffff)); +#ifdef WITH_ACCOUNTING + case PW_ACCOUNTING_RESPONSE: + fun = rad_accounting; + break; +#endif - /* - * If everything's OK, this is a waste of memory. - * Otherwise, it lets us re-send the original packet - * contents, unmolested. - */ - packet->vps = paircopy(data->vps); +#ifdef WITH_COA + case PW_DISCONNECT_ACK: + case PW_DISCONNECT_NAK: + case PW_COA_ACK: + case PW_COA_NAK: + fun = rad_coa_reply; + break; +#endif - /* - * Look for Acct-Delay-Time, and update - * based on Acct-Delay-Time += (time(NULL) - timestamp) - */ - vp = pairfind(packet->vps, PW_ACCT_DELAY_TIME); - if (!vp) { - vp = paircreate(PW_ACCT_DELAY_TIME, PW_TYPE_INTEGER); - rad_assert(vp != NULL); - pairadd(&packet->vps, vp); - } - if (data->timestamp != 0) { - vp->vp_integer += time(NULL) - data->timestamp; + default: + /* + * FIXME: Update MIB for packet types? + */ + radlog(L_ERR, "Invalid packet code %d sent to a proxy port " + "from home server %s port %d - ID %d : IGNORED", + packet->code, + ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), + packet->src_port, packet->id); + rad_free(&packet); + return 0; } - *pfun = rad_accounting; - - if (debug_flag) { - printf("detail_recv: Read packet from %s\n", data->filename); - for (vp = packet->vps; vp; vp = vp->next) { - putchar('\t'); - vp_print(stdout, vp); - putchar('\n'); - } + request = received_proxy_response(packet); + if (!request) { + rad_free(&packet); + return 0; } +#ifdef WITH_COA /* - * FIXME: many of these checks may not be necessary when - * reading from the detail file. - * - * Try again later... + * Distinguish proxied CoA requests from ones we + * originate. */ - if (!received_request(listener, packet, prequest, &detail_client)) { - rad_free(&packet); - data->state = STATE_NO_REPLY; /* try again later */ - return 0; + if ((fun == rad_coa_reply) && + (request->packet->code == request->proxy->code)) { + fun = rad_coa_recv; } +#endif - data->state = STATE_RUNNING; + rad_assert(fun != NULL); + *pfun = fun; + *prequest = request; return 1; } - +#ifdef WITH_TCP /* - * Free detail-specific stuff. + * Recieve packets from a proxy socket. */ -static void detail_free(rad_listen_t *this) +static int proxy_socket_tcp_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) { - listen_detail_t *data = this->data; + REQUEST *request; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + listen_socket_t *sock = listener->data; + char buffer[128]; - free(data->filename); - pairfree(&data->vps); + packet = fr_tcp_recv(listener->fd, 0); + if (!packet) { + listener->status = RAD_LISTEN_STATUS_REMOVE_FD; + event_new_fd(listener); + return 0; + } - if (data->fp != NULL) fclose(data->fp); -} + /* + * FIXME: Client MIB updates? + */ + switch(packet->code) { + case PW_AUTHENTICATION_ACK: + case PW_ACCESS_CHALLENGE: + case PW_AUTHENTICATION_REJECT: + fun = rad_authenticate; + break; +#ifdef WITH_ACCOUNTING + case PW_ACCOUNTING_RESPONSE: + fun = rad_accounting; + break; +#endif -static int detail_print(rad_listen_t *this, char *buffer, size_t bufsize) -{ - if (!this->server) { - return snprintf(buffer, bufsize, "%s", - ((listen_detail_t *)(this->data))->filename); + default: + /* + * FIXME: Update MIB for packet types? + */ + radlog(L_ERR, "Invalid packet code %d sent to a proxy port " + "from home server %s port %d - ID %d : IGNORED", + packet->code, + ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), + packet->src_port, packet->id); + rad_free(&packet); + return 0; } - return snprintf(buffer, bufsize, "detail file %s as server %s", - ((listen_detail_t *)(this->data))->filename, - this->server); -} - -static int detail_encode(UNUSED rad_listen_t *this, UNUSED REQUEST *request) -{ - /* - * We never encode responses "sent to" the detail file. - */ - return 0; -} + packet->src_ipaddr = sock->other_ipaddr; + packet->src_port = sock->other_port; + packet->dst_ipaddr = sock->my_ipaddr; + packet->dst_port = sock->my_port; -static int detail_decode(UNUSED rad_listen_t *this, UNUSED REQUEST *request) -{ /* - * We never decode responses read from the detail file. + * 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... */ - return 0; -} + request = received_proxy_response(packet); + if (!request) { + return 0; + } + rad_assert(fun != NULL); + sock->opened = sock->last_packet = request->timestamp; -static const CONF_PARSER detail_config[] = { - { "filename", PW_TYPE_STRING_PTR, - offsetof(listen_detail_t, filename), NULL, NULL }, - { "load_factor", PW_TYPE_INTEGER, - offsetof(listen_detail_t, load_factor), NULL, Stringify(10)}, + *pfun = fun; + *prequest = request; - { NULL, -1, 0, NULL, NULL } /* end the list */ -}; + return 1; +} +#endif +#endif -/* - * Parse a detail section. - */ -static int detail_parse(CONF_SECTION *cs, rad_listen_t *this) +static int client_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) { - int rcode; - listen_detail_t *data; + if (!request->reply->code) return 0; - data = this->data; + rad_encode(request->reply, request->packet, + request->client->secret); + rad_sign(request->reply, request->packet, + request->client->secret); - rcode = cf_section_parse(cs, data, detail_config); - if (rcode < 0) { - cf_log_err(cf_sectiontoitem(cs), "Failed parsing listen section"); - return -1; - } + return 0; +} - if (!data->filename) { - cf_log_err(cf_sectiontoitem(cs), "No detail file specified in listen section"); - return -1; - } - if ((data->load_factor < 1) || (data->load_factor > 100)) { - cf_log_err(cf_sectiontoitem(cs), "Load factor must be between 1 and 100"); +static int client_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) +{ + if (rad_verify(request->packet, NULL, + request->client->secret) < 0) { return -1; } - data->vps = NULL; - data->fp = NULL; - data->state = STATE_UNOPENED; - detail_open(this); - - return 0; + return rad_decode(request->packet, NULL, + request->client->secret); } - -#ifdef WITH_SNMP -static int radius_snmp_recv(rad_listen_t *listener, - UNUSED RAD_REQUEST_FUNP *pfun, - UNUSED REQUEST **prequest) +#ifdef WITH_PROXY +static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) { - if (!mainconfig.do_snmp) return 0; + rad_encode(request->proxy, NULL, request->home_server->secret); + rad_sign(request->proxy, NULL, request->home_server->secret); - if ((rad_snmp.smux_fd >= 0) && - (rad_snmp.smux_event == SMUX_READ)) { - smux_read(); - } + return 0; +} - /* - * If we've got to re-connect, then do so now, - * before calling select again. - */ - if (rad_snmp.smux_event == SMUX_CONNECT) { - smux_connect(); - } +static int proxy_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) +{ /* - * Reset this every time, as the smux connect may have - * opened a new socket. + * rad_verify is run in event.c, received_proxy_response() */ - listener->fd = rad_snmp.smux_fd; - return 0; + return rad_decode(request->proxy_reply, request->proxy, + request->home_server->secret); } +#endif +#include "dhcpd.c" -static int radius_snmp_print(UNUSED rad_listen_t *this, char *buffer, size_t bufsize) -{ - return snprintf(buffer, bufsize, "SMUX with OID .1.3.6.1.4.1.11344.1.1.1"); -} - -#endif +#include "command.c" static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { +#ifdef WITH_STATS + { common_socket_parse, NULL, + stats_socket_recv, auth_socket_send, + socket_print, client_socket_encode, client_socket_decode }, +#else + /* + * This always gets defined. + */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */ +#endif +#ifdef WITH_PROXY /* proxying */ - { NULL, NULL, + { common_socket_parse, NULL, proxy_socket_recv, proxy_socket_send, socket_print, proxy_socket_encode, proxy_socket_decode }, +#endif /* authentication */ { common_socket_parse, NULL, auth_socket_recv, auth_socket_send, socket_print, client_socket_encode, client_socket_decode }, +#ifdef WITH_ACCOUNTING /* accounting */ { common_socket_parse, NULL, acct_socket_recv, acct_socket_send, socket_print, client_socket_encode, client_socket_decode}, +#endif +#ifdef WITH_DETAIL /* detail */ { detail_parse, detail_free, detail_recv, detail_send, detail_print, detail_encode, detail_decode }, +#endif #ifdef WITH_VMPS /* vlan query protocol */ { common_socket_parse, NULL, vqp_socket_recv, vqp_socket_send, socket_print, vqp_socket_encode, vqp_socket_decode }, -#else - { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, #endif - { NULL, NULL, NULL, NULL, NULL, NULL, NULL} /* RAD_LISTEN_SNMP */ +#ifdef WITH_DHCP + /* dhcp query protocol */ + { dhcp_socket_parse, NULL, + dhcp_socket_recv, dhcp_socket_send, + socket_print, dhcp_socket_encode, dhcp_socket_decode }, +#endif + +#ifdef WITH_COMMAND_SOCKET + /* TCP command socket */ + { command_socket_parse, command_socket_free, + command_domain_accept, command_domain_send, + command_socket_print, command_socket_encode, command_socket_decode }, +#endif + +#ifdef WITH_COA + /* Change of Authorization */ + { common_socket_parse, NULL, + coa_socket_recv, auth_socket_send, /* CoA packets are same as auth */ + socket_print, client_socket_encode, client_socket_decode }, +#endif + }; @@ -1457,96 +1853,323 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { */ static int listen_bind(rad_listen_t *this) { - rad_listen_t **last; + int rcode; + struct sockaddr_storage salocal; + socklen_t salen; listen_socket_t *sock = this->data; +#ifndef WITH_TCP +#define proto_for_port "udp" +#define sock_type SOCK_DGRAM +#else + const char *proto_for_port = "udp"; + int sock_type = SOCK_DGRAM; + + if (sock->proto == IPPROTO_TCP) { +#ifdef WITH_VMPS + if (this->type == RAD_LISTEN_VQP) { + radlog(L_ERR, "VQP does not support TCP transport"); + return -1; + } +#endif + + proto_for_port = "tcp"; + sock_type = SOCK_STREAM; + } +#endif /* * If the port is zero, then it means the appropriate * thing from /etc/services. */ - if (sock->port == 0) { + if (sock->my_port == 0) { struct servent *svp; switch (this->type) { case RAD_LISTEN_AUTH: - svp = getservbyname ("radius", "udp"); + svp = getservbyname ("radius", proto_for_port); if (svp != NULL) { - sock->port = ntohs(svp->s_port); + sock->my_port = ntohs(svp->s_port); } else { - sock->port = PW_AUTH_UDP_PORT; + sock->my_port = PW_AUTH_UDP_PORT; } break; +#ifdef WITH_ACCOUNTING case RAD_LISTEN_ACCT: - svp = getservbyname ("radacct", "udp"); + svp = getservbyname ("radacct", proto_for_port); if (svp != NULL) { - sock->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: + /* 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: + 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."); + default: + DEBUG("WARNING: Internal sanity check failed in binding to socket. Ignoring problem."); + return -1; + } + } + + /* + * Copy fr_socket() here, as we may need to bind to a device. + */ + this->fd = socket(sock->my_ipaddr.af, sock_type, 0); + if (this->fd < 0) { + radlog(L_ERR, "Failed opening socket: %s", strerror(errno)); + return -1; + } + + /* + * Bind to a device BEFORE touching IP addresses. + */ + if (sock->interface) { +#ifdef SO_BINDTODEVICE + struct ifreq ifreq; + strcpy(ifreq.ifr_name, sock->interface); + + fr_suid_up(); + rcode = setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE, + (char *)&ifreq, sizeof(ifreq)); + fr_suid_down(); + if (rcode < 0) { + close(this->fd); + radlog(L_ERR, "Failed binding to interface %s: %s", + sock->interface, strerror(errno)); + return -1; + } /* else it worked. */ +#else +#ifdef HAVE_STRUCT_SOCKADDR_IN6 +#ifdef HAVE_NET_IF_H + /* + * Odds are that any system supporting "bind to + * device" also supports IPv6, so this next bit + * isn't necessary. But it's here for + * completeness. + * + * If we're doing IPv6, and the scope hasn't yet + * been defined, set the scope to the scope of + * the interface. + */ + if (sock->my_ipaddr.af == AF_INET6) { + if (sock->my_ipaddr.scope == 0) { + sock->my_ipaddr.scope = if_nametoindex(sock->interface); + if (sock->my_ipaddr.scope == 0) { + close(this->fd); + radlog(L_ERR, "Failed finding interface %s: %s", + sock->interface, strerror(errno)); + return -1; + } + } /* else scope was defined: we're OK. */ + } else +#endif +#endif + /* + * IPv4: no link local addresses, + * and no bind to device. + */ + { + close(this->fd); + radlog(L_ERR, "Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface); + return -1; + } +#endif + } + +#ifdef WITH_TCP + if (sock->proto == IPPROTO_TCP) { + int on = 1; + + if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + close(this->fd); + radlog(L_ERR, "Failed to reuse address: %s", strerror(errno)); + return -1; + } + } +#endif + +#if defined(WITH_TCP) && defined(WITH_UDPFROMTO) + else /* UDP sockets get UDPfromto */ +#endif + +#ifdef WITH_UDPFROMTO + /* + * Initialize udpfromto for all sockets. + */ + if (udpfromto_init(this->fd) != 0) { + close(this->fd); + return -1; + } +#endif + + /* + * Set up sockaddr stuff. + */ + if (!fr_ipaddr2sockaddr(&sock->my_ipaddr, sock->my_port, &salocal, &salen)) { + close(this->fd); + return -1; + } + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if (sock->my_ipaddr.af == AF_INET6) { + /* + * Listening on '::' does NOT get you IPv4 to + * IPv6 mapping. You've got to listen on an IPv4 + * address, too. This makes the rest of the server + * design a little simpler. + */ +#ifdef IPV6_V6ONLY + + if (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr)) { + int on = 1; + + setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY, + (char *)&on, sizeof(on)); + } +#endif /* IPV6_V6ONLY */ + } +#endif /* HAVE_STRUCT_SOCKADDR_IN6 */ + + if (sock->my_ipaddr.af == AF_INET) { + UNUSED int flag; + +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* + * Disable PMTU discovery. On Linux, this + * also makes sure that the "don't fragment" + * flag is zero. + */ + flag = IP_PMTUDISC_DONT; + setsockopt(this->fd, IPPROTO_IP, IP_MTU_DISCOVER, + &flag, sizeof(flag)); +#endif + +#if defined(IP_DONTFRAG) + /* + * Ensure that the "don't fragment" flag is zero. + */ + flag = 0; + setsockopt(this->fd, IPPROTO_IP, IP_DONTFRAG, + &flag, sizeof(flag)); +#endif + } + +#ifdef WITH_DHCP +#ifdef SO_BROADCAST + if (sock->broadcast) { + int on = 1; + + if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { + radlog(L_ERR, "Can't set broadcast option: %s\n", + strerror(errno)); return -1; } } +#endif +#endif /* - * Find it in the old list, AFTER updating the port. If - * it's there, use that, rather than creating a new - * socket. This allows HUP's to re-use the old sockets, - * which means that packets waiting in the socket queue - * don't get lost. + * May be binding to priviledged ports. */ - for (last = &mainconfig.listen; - *last != NULL; - last = &((*last)->next)) { - listen_socket_t *other; - - if (this->type != (*last)->type) continue; - - if ((this->type == RAD_LISTEN_DETAIL) || - (this->type == RAD_LISTEN_SNMP)) continue; + if (sock->my_port != 0) { +#ifdef SO_REUSEADDR + int on = 1; - other = (listen_socket_t *)((*last)->data); + 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 ((sock->port == other->port) && - (sock->ipaddr.af == other->ipaddr.af) && - (lrad_ipaddr_cmp(&sock->ipaddr, &other->ipaddr) == 0)) { - this->fd = (*last)->fd; - (*last)->fd = -1; - return 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; + } + + /* + * 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; + } } } - this->fd = lrad_socket(&sock->ipaddr, sock->port); - if (this->fd < 0) { - radlog(L_ERR, "ERROR: Failed to open socket: %s", - librad_errstr); - return -1; - } +#ifdef WITH_TCP + if (sock->proto == IPPROTO_TCP) { + if (listen(this->fd, 8) < 0) { + close(this->fd); + radlog(L_ERR, "Failed in listen(): %s", strerror(errno)); + return -1; + } + } else +#endif -#if 0 -#ifdef O_NONBLOCK - if ((flags = fcntl(this->fd, F_GETFL, NULL)) < 0) { - radlog(L_ERR, "Failure in fcntl: %s)\n", strerror(errno)); - return -1; - } + if (fr_nonblock(this->fd) < 0) { + close(this->fd); + radlog(L_ERR, "Failed setting non-blocking on socket: %s", + strerror(errno)); + return -1; + } - flags |= O_NONBLOCK; - if( fcntl(this->fd, F_SETFL, flags) < 0) { - radlog(L_ERR, "Failure in fcntl: %s)\n", strerror(errno)); - return -1; - } -#endif -#endif + /* + * 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; } @@ -1570,17 +2193,45 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type) this->decode = master_listen[this->type].decode; switch (type) { +#ifdef WITH_STATS + case RAD_LISTEN_NONE: +#endif case RAD_LISTEN_AUTH: +#ifdef WITH_ACCOUNTING case RAD_LISTEN_ACCT: +#endif +#ifdef WITH_PROXY case RAD_LISTEN_PROXY: +#endif +#ifdef WITH_VMPS case RAD_LISTEN_VQP: +#endif +#ifdef WITH_COA + case RAD_LISTEN_COA: +#endif this->data = rad_malloc(sizeof(listen_socket_t)); memset(this->data, 0, sizeof(listen_socket_t)); break; +#ifdef WITH_DHCP + case RAD_LISTEN_DHCP: + this->data = rad_malloc(sizeof(dhcp_socket_t)); + memset(this->data, 0, sizeof(dhcp_socket_t)); + break; +#endif + +#ifdef WITH_DETAIL case RAD_LISTEN_DETAIL: - this->data = rad_malloc(sizeof(listen_detail_t)); - memset(this->data, 0, sizeof(listen_detail_t)); + this->data = NULL; + break; +#endif + +#ifdef WITH_COMMAND_SOCKET + case RAD_LISTEN_COMMAND: + this->data = rad_malloc(sizeof(fr_command_socket_t)); + memset(this->data, 0, sizeof(fr_command_socket_t)); + break; +#endif default: rad_assert("Unsupported option!" == NULL); @@ -1590,80 +2241,126 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type) return this; } - +#ifdef WITH_PROXY /* * Externally visible function for creating a new proxy LISTENER. * - * For now, don't take ipaddr or port. - * * Not thread-safe, but all calls to it are protected by the - * proxy mutex in request_list.c + * proxy mutex in event.c */ -rad_listen_t *proxy_new_listener() +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; - - this = listen_alloc(RAD_LISTEN_PROXY); + rad_listen_t *this; + listen_socket_t *sock; - /* - * Find an existing proxy socket to copy. - * - * FIXME: Make it per-realm, or per-home server! - */ - last_proxy_port = 0; - old = NULL; - last = &mainconfig.listen; - for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) { - if (tmp->type == RAD_LISTEN_PROXY) { - sock = tmp->data; - if (sock->port > last_proxy_port) { - last_proxy_port = sock->port + 1; - } - if (!old) old = sock; - } + if (!home) return 0; - last = &(tmp->next); + 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 (!old) return NULL; /* This is a serious error. */ + this = listen_alloc(RAD_LISTEN_PROXY); - /* - * FIXME: find a new IP address to listen on? - * - * This could likely be done in the "home server" - * configuration, to have per-home-server source IP's. - */ sock = this->data; - memcpy(&sock->ipaddr, &old->ipaddr, sizeof(sock->ipaddr)); + sock->other_ipaddr = home->ipaddr; + sock->other_port = home->port; + sock->home = home; + + sock->my_ipaddr = home->src_ipaddr; + sock->my_port = src_port; + sock->proto = home->proto; + +#ifdef WITH_TCP + sock->last_packet = time(NULL); + + if (home->proto == IPPROTO_TCP) { + this->recv = proxy_socket_tcp_recv; + + /* + * FIXME: connect() is blocking! + * We do this with the proxy mutex locked, which may + * cause large delays! + * + * http://www.developerweb.net/forum/showthread.php?p=13486 + */ + this->fd = fr_tcp_client_socket(&home->src_ipaddr, + &home->ipaddr, home->port); + } else +#endif + this->fd = fr_socket(&home->src_ipaddr, src_port); + + if (this->fd < 0) { + DEBUG("Failed opening client socket: %s", fr_strerror()); + listen_free(&this); + return 0; + } /* - * Keep going until we find an unused port. + * Figure out which port we were bound to. */ - for (port = last_proxy_port; port < 64000; port++) { - sock->port = port; - if (listen_bind(this) == 0) { - /* - * Add the new listener to the list of - * listeners. - */ - *last = this; - return this; + if (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 0; + } + + if (!fr_sockaddr2ipaddr(&src, sizeof_src, + &sock->my_ipaddr, &sock->my_port)) { + radlog(L_ERR, "Socket has unsupported address family"); + listen_free(&this); + return 0; } } - return 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 LRAD_NAME_NUMBER listen_compare[] = { +static const FR_NAME_NUMBER listen_compare[] = { +#ifdef WITH_STATS + { "status", RAD_LISTEN_NONE }, +#endif { "auth", RAD_LISTEN_AUTH }, +#ifdef WITH_ACCOUNTING { "acct", RAD_LISTEN_ACCT }, +#endif +#ifdef WITH_DETAIL { "detail", RAD_LISTEN_DETAIL }, +#endif +#ifdef WITH_PROXY + { "proxy", RAD_LISTEN_PROXY }, +#endif #ifdef WITH_VMPS { "vmps", RAD_LISTEN_VQP }, #endif +#ifdef WITH_DHCP + { "dhcp", RAD_LISTEN_DHCP }, +#endif +#ifdef WITH_COMMAND_SOCKET + { "control", RAD_LISTEN_COMMAND }, +#endif +#ifdef WITH_COA + { "coa", RAD_LISTEN_COA }, +#endif { NULL, 0 }, }; @@ -1676,7 +2373,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) listen_type = NULL; - DEBUG2(" listen {"); + cf_log_info(cs, "listen {"); rcode = cf_item_parse(cs, "type", PW_TYPE_STRING_PTR, &listen_type, ""); @@ -1688,26 +2385,42 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return NULL; } - type = lrad_str2int(listen_compare, listen_type, - RAD_LISTEN_NONE); - free(listen_type); - if (type == RAD_LISTEN_NONE) { + type = fr_str2int(listen_compare, listen_type, -1); + if (type < 0) { cf_log_err(cf_sectiontoitem(cs), "Invalid type \"%s\" in listen section.", listen_type); + free(listen_type); return NULL; } - + free(listen_type); + /* * Allow listen sections in the default config to * refer to a server. */ if (!server) { - rcode = cf_item_parse(cs, "server", PW_TYPE_STRING_PTR, + rcode = cf_item_parse(cs, "virtual_server", PW_TYPE_STRING_PTR, &server, NULL); + if (rcode == 1) { /* compatiblity with 2.0-pre */ + rcode = cf_item_parse(cs, "server", PW_TYPE_STRING_PTR, + &server, NULL); + } 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. */ @@ -1723,7 +2436,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return NULL; } - DEBUG2(" }"); + cf_log_info(cs, "}"); return this; } @@ -1734,12 +2447,16 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) */ int listen_init(CONF_SECTION *config, rad_listen_t **head) { + int override = FALSE; int rcode; - CONF_SECTION *cs; + CONF_SECTION *cs = NULL; rad_listen_t **last; rad_listen_t *this; - lrad_ipaddr_t server_ipaddr; + fr_ipaddr_t server_ipaddr; int auth_port = 0; +#ifdef WITH_PROXY + int defined_proxy = 0; +#endif /* * We shouldn't be called with a pre-existing list. @@ -1764,6 +2481,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) if (mainconfig.myip.af != AF_UNSPEC) { memcpy(&server_ipaddr, &mainconfig.myip, sizeof(server_ipaddr)); + override = TRUE; goto bind_it; } @@ -1780,7 +2498,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) 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 @@ -1793,16 +2511,30 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) sock = this->data; - sock->ipaddr = server_ipaddr; - sock->port = auth_port; + sock->my_ipaddr = server_ipaddr; + sock->my_port = auth_port; - if (listen_bind(this) < 0) { + sock->clients = clients_parse_section(config); + if (!sock->clients) { + cf_log_err(cf_sectiontoitem(config), + "Failed to find any clients for this listen section"); listen_free(&this); + return -1; + } + + if (listen_bind(this) < 0) { listen_free(head); - radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->port); + radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->my_port); + listen_free(&this); return -1; } - auth_port = sock->port; /* may have been updated in listen_bind */ + auth_port = sock->my_port; /* may have been updated in listen_bind */ + if (override) { + cs = cf_section_sub_find_name2(config, "server", + mainconfig.name); + if (cs) this->server = mainconfig.name; + } + *last = this; last = &(this->next); @@ -1810,9 +2542,10 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) /* * No acct for vmpsd */ - if (strcmp(progname, "vmpsd") == 0) goto do_proxy; + if (strcmp(progname, "vmpsd") == 0) goto add_sockets; #endif +#ifdef WITH_ACCOUNTING /* * Open Accounting Socket. * @@ -1828,19 +2561,32 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) * The accounting port is always the * authentication port + 1 */ - sock->ipaddr = server_ipaddr; - sock->port = auth_port + 1; + sock->my_ipaddr = server_ipaddr; + sock->my_port = auth_port + 1; + + sock->clients = clients_parse_section(config); + if (!sock->clients) { + cf_log_err(cf_sectiontoitem(config), + "Failed to find any clients for this listen section"); + return -1; + } if (listen_bind(this) < 0) { listen_free(&this); listen_free(head); - radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->port); + radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->my_port); return -1; } + if (override) { + cs = cf_section_sub_find_name2(config, "server", + mainconfig.name); + if (cs) this->server = mainconfig.name; + } + *last = this; last = &(this->next); - +#endif } else if (mainconfig.port > 0) { /* no bind address, but a port */ radlog(L_ERR, "The command-line says \"-p %d\", but there is no associated IP address to use", mainconfig.port); @@ -1849,9 +2595,34 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) /* * They specified an IP on the command-line, ignore - * all listen sections. + * all listen sections except the one in '-n'. */ - if (mainconfig.myip.af != AF_UNSPEC) goto do_proxy; + if (mainconfig.myip.af != AF_UNSPEC) { + CONF_SECTION *subcs; + const char *name2 = cf_section_name2(cs); + + cs = cf_section_sub_find_name2(config, "server", + mainconfig.name); + if (!cs) goto add_sockets; + + /* + * Should really abstract this code... + */ + for (subcs = cf_subsection_find_next(cs, NULL, "listen"); + subcs != NULL; + subcs = cf_subsection_find_next(cs, subcs, "listen")) { + this = listen_parse(subcs, name2); + if (!this) { + listen_free(head); + return -1; + } + + *last = this; + last = &(this->next); + } /* loop over "listen" directives in server */ + + goto add_sockets; + } /* * Walk through the "listen" sections, if they exist. @@ -1894,138 +2665,109 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) } /* 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 + event_new_fd(this); + } + /* * If we're proxying requests, open the proxy FD. * Otherwise, don't do anything. */ - do_proxy: - if (mainconfig.proxy_requests == TRUE) { - int port = -1; +#ifdef WITH_PROXY + if ((mainconfig.proxy_requests == TRUE) && + (*head != NULL) && !defined_proxy) { listen_socket_t *sock = NULL; + int port = 0; + home_server home; - /* - * No sockets to receive packets, therefore - * proxying is pointless. - */ - if (!*head) return -1; + memset(&home, 0, sizeof(home)); /* - * If we previously had proxy sockets, copy them - * to the new config. + * */ - if (mainconfig.listen != NULL) { - rad_listen_t *old, *next, **tail; - - tail = &mainconfig.listen; - for (old = mainconfig.listen; - old != NULL; - old = next) { - next = old->next; - - if (old->type != RAD_LISTEN_PROXY) { - tail = &((*tail)->next); - continue; - } - - *last = old; - *tail = next; - old->next = NULL; - last = &(old->next); - } - - goto do_snmp; - } + home.proto = IPPROTO_UDP; + home.src_ipaddr = server_ipaddr; /* * Find the first authentication port, * and use it */ for (this = *head; this != NULL; this = this->next) { + /* + * We shouldn't proxy on loopback. + */ + if ((sock->my_ipaddr.af == AF_INET) && + (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_LOOPBACK))) continue; + + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if ((sock->my_ipaddr.af == AF_INET6) && + (IN6_IS_ADDR_LINKLOCAL(&sock->my_ipaddr.ipaddr.ip6addr))) continue; +#endif + 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; } - 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 + (lrad_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; } } +#endif - do_snmp: -#ifdef WITH_SNMP - if (mainconfig.do_snmp) { - radius_snmp_init(); - - /* - * Forget about the old one. - */ - for (this = mainconfig.listen; - this != NULL; - this = this->next) { - if (this->type != RAD_LISTEN_SNMP) continue; - this->fd = -1; - } - - this = rad_malloc(sizeof(*this)); - memset(this, 0, sizeof(*this)); - - this->type = RAD_LISTEN_SNMP; - this->fd = rad_snmp.smux_fd; - - this->recv = radius_snmp_recv; - this->print = radius_snmp_print; + /* + * Haven't defined any sockets. Die. + */ + if (!*head) return -1; - *last = this; - last = &(this->next); - } -#endif return 0; } @@ -2051,6 +2793,21 @@ void listen_free(rad_listen_t **head) if (master_listen[this->type].free) { master_listen[this->type].free(this); } + +#ifdef WITH_TCP + if ((this->type == RAD_LISTEN_AUTH) || +#ifdef WITH_ACCT + (this->type == RAD_LISTEN_ACCT) || +#endif +#ifdef WITH_PROXY + (this->type == RAD_LISTEN_PROXY) +#endif + ) { + listen_socket_t *sock = this->data; + rad_free(&sock->packet); + } +#endif + free(this->data); free(this); @@ -2059,3 +2816,66 @@ void listen_free(rad_listen_t **head) *head = NULL; } + +#ifdef WITH_STATS +RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr, + int port) +{ + rad_listen_t *this; + + for (this = mainconfig.listen; this != NULL; this = this->next) { + listen_socket_t *sock; + + if ((this->type != RAD_LISTEN_AUTH) && + (this->type != RAD_LISTEN_ACCT)) continue; + + sock = this->data; + + if ((sock->my_port == port) && + (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) { + return sock->clients; + } + } + + return NULL; +} +#endif + +rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port) +{ + rad_listen_t *this; + + for (this = mainconfig.listen; this != NULL; this = this->next) { + listen_socket_t *sock; + + /* + * FIXME: For TCP, ignore the *secondary* + * listeners associated with the main socket. + */ + if ((this->type != RAD_LISTEN_AUTH) && + (this->type != RAD_LISTEN_ACCT)) continue; + + sock = this->data; + + if ((sock->my_port == port) && + (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) { + return this; + } + + 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->my_port == port) && + (sock->my_ipaddr.af == AF_INET6) && + (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr))) { + return this; + } +#endif + } + + return NULL; +}