X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Flisten.c;h=e82d5850fe07121ce557ec420255e7825b9b7460;hb=e9831edfd3d9f38e6996d6e0e655feb0e1547be8;hp=4b39dbbd5b98ab188cd0171a2a195a0ca41083f6;hpb=d096e94fb73fdc33dfe82ca6c87574b6ee1db2bd;p=freeradius.git diff --git a/src/main/listen.c b/src/main/listen.c index 4b39dbb..e82d585 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -25,7 +25,6 @@ RCSID("$Id$") #include -#include #include #include #include @@ -34,6 +33,10 @@ RCSID("$Id$") #include #include +#ifdef WITH_UDPFROMTO +#include +#endif + #ifdef HAVE_SYS_RESOURCE_H #include #endif @@ -47,6 +50,19 @@ RCSID("$Id$") #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. */ @@ -63,48 +79,75 @@ typedef struct rad_listen_master_t { rad_listen_decode_t decode; } rad_listen_master_t; -typedef struct listen_socket_t { - /* - * For normal sockets. - */ - fr_ipaddr_t ipaddr; - int port; - RADCLIENT_LIST *clients; -} listen_socket_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 fr_ipaddr_t *ipaddr) + const fr_ipaddr_t *ipaddr, int src_port) { #ifdef WITH_DYNAMIC_CLIENTS int rcode; - time_t now; - listen_socket_t *sock; REQUEST *request; - RADCLIENT *client, *created; + 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) || - (listener->type == RAD_LISTEN_DHCP)); - - clients = ((listen_socket_t *)listener->data)->clients; + sock = listener->data; + clients = sock->clients; /* * This HAS to have been initialized previously. */ rad_assert(clients != NULL); + client = client_find(clients, ipaddr,sock->proto); + if (!client) { + char name[256], buffer[128]; + #ifdef WITH_DYNAMIC_CLIENTS - client = client_find(clients, ipaddr); - if (!client) return NULL; + 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. @@ -116,7 +159,7 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener, /* * It's a dynamically generated client, check it. */ - if (client->dynamic) { + if (client->dynamic && (src_port != 0)) { /* * Lives forever. Return it. */ @@ -136,20 +179,21 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener, if ((client->created + client->lifetime) > now) return client; /* - * It's dead, Jim. Delete it. + * 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); + client = client_find(clients, ipaddr, sock->proto); /* * WTF? */ - if (!client) return NULL; - if (!client->client_server) return NULL; + if (!client) goto unknown; + if (!client->client_server) goto unknown; /* * At this point, 'client' is the enclosing @@ -157,32 +201,33 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener, * can be defined. */ rad_assert(client->dynamic == 0); - } else { + + } else if (!client->dynamic && client->rate_limit) { /* * The IP is unknown, so we've found an enclosing * network. Enable DoS protection. We only * allow one new client per second. Known * clients aren't subject to this restriction. */ - if (now == client->last_new_client) return NULL; + if (now == client->last_new_client) goto unknown; } client->last_new_client = now; request = request_alloc(); - if (!request) return NULL; + if (!request) goto unknown; request->listener = listener; request->client = client; - request->packet = rad_alloc(0); - if (!request->packet) { + request->packet = rad_recv(listener->fd, 0x02); /* MSG_PEEK */ + if (!request->packet) { /* badly formed, etc */ request_free(&request); - return NULL; + goto unknown; } - request->reply = rad_alloc(0); + request->reply = rad_alloc_reply(request->packet); if (!request->reply) { request_free(&request); - return NULL; + goto unknown; } request->packet->timestamp = request->timestamp; request->number = 0; @@ -198,23 +243,6 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener, * * and create the RADCLIENT structure from that. */ - - sock = listener->data; - request->packet->sockfd = listener->fd; - request->packet->src_ipaddr = *ipaddr; - request->packet->src_port = 0; /* who cares... */ - request->packet->dst_ipaddr = sock->ipaddr; - request->packet->dst_port = sock->port; - - request->reply->sockfd = request->packet->sockfd; - request->reply->dst_ipaddr = request->packet->src_ipaddr; - request->reply->src_ipaddr = request->packet->dst_ipaddr; - request->reply->dst_port = request->packet->src_port; - request->reply->src_port = request->packet->dst_port; - request->reply->id = request->packet->id; - request->reply->code = 0; /* UNKNOWN code */ - - DEBUG("server %s {", request->server); rcode = module_authorize(0, request); @@ -223,16 +251,28 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener, if (rcode != RLM_MODULE_OK) { request_free(&request); - return NULL; + goto unknown; } - created = client_create(clients, request); + /* + * 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); - return created; /* may be NULL */ -#else - return client_find(clients, ipaddr); + if (!created) goto unknown; + + return created; #endif } @@ -250,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 { @@ -278,7 +321,7 @@ static int rad_status_server(REQUEST *request) #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 { @@ -298,28 +341,298 @@ static int rad_status_server(REQUEST *request) 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; @@ -348,20 +661,129 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) 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 + + 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); } - return snprintf(buffer, bufsize, "%s address %s port %d as server %s", - name, ip_buf, sock->port, this->server); +#undef ADDSTRING +#undef FORWARD + + return 1; } +extern int check_config; /* radiusd.c */ + /* * Parse an authentication or accounting socket. @@ -403,40 +825,81 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) &listen_port, "0"); if (rcode < 0) return -1; - if ((listen_port < 0) || (listen_port > 65500)) { + if ((listen_port < 0) || (listen_port > 65535)) { cf_log_err(cf_sectiontoitem(cs), "Invalid value for \"port\""); return -1; } - sock->ipaddr = ipaddr; - sock->port = listen_port; + 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; - /* - * If we can bind to interfaces, do so, + + 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 */ + } + + /* + * If we can bind to interfaces, do so, * 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); @@ -445,17 +908,54 @@ 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 + } #endif + + /* + * And bind it to the port. + */ + if (listen_bind(this) < 0) { + char buffer[128]; + cf_log_err(cf_sectiontoitem(cs), + "Error binding to port for %s port %d", + ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)), + sock->my_port); + return -1; } #ifdef WITH_PROXY @@ -518,10 +1018,20 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) sock->clients = clients_parse_section(client_cs); if (!sock->clients) { cf_log_err(cf_sectiontoitem(cs), - "Failed to find any clients for this listen section"); + "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; } @@ -568,67 +1078,113 @@ 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 auth_socket_recv(rad_listen_t *listener, +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; - RAD_REQUEST_FUNP fun = NULL; - char buffer[128]; RADCLIENT *client; 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); + 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 (debug_flag > 0) { - char name[1024]; + 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; + } - listener->print(listener, name, sizeof(name)); + *pfun = rad_status_server; + return 1; +} +#endif - /* - * 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); - } +/* + * Check if an incoming request is "ok" + * + * It takes packets, not requests. It sees if the packet looks + * OK. If so, it does a number of sanity checks on it. + */ +static int auth_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + ssize_t rcode; + int code, src_port; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + RADCLIENT *client; + fr_ipaddr_t src_ipaddr; + + rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); + if (rcode < 0) return 0; + + RAD_STATS_TYPE_INC(listener, total_requests); + + if (rcode < 20) { /* AUTH_HDR_LEN */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + return 0; + } + + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); return 0; } @@ -637,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; } @@ -654,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); @@ -669,14 +1225,14 @@ static int auth_socket_recv(rad_listen_t *listener, */ packet = rad_recv(listener->fd, client->message_authenticator); if (!packet) { - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); - DEBUG("%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; } @@ -697,40 +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; 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. - */ - if (debug_flag > 0) { - char name[1024]; - - listener->print(listener, name, sizeof(name)); - - 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); - } - + RAD_STATS_TYPE_INC(listener, total_invalid_requests); return 0; } @@ -739,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; @@ -757,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); @@ -771,8 +1310,8 @@ static int acct_socket_recv(rad_listen_t *listener, */ 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; } @@ -780,8 +1319,249 @@ 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; + } + + *pfun = fun; + return 1; +} +#endif + + +#ifdef WITH_COA +/* + * For now, all CoA requests are *only* originated, and not + * proxied. So all of the necessary work is done in the + * post-proxy section, which is automatically handled by event.c. + * As a result, we don't have to do anything here. + */ +static int rad_coa_reply(REQUEST *request) +{ + VALUE_PAIR *s1, *s2; + + /* + * 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; + + /* + * Get the correct response + */ + switch (request->packet->code) { + case PW_COA_REQUEST: + ack = PW_COA_ACK; + nak = PW_COA_NAK; + break; + + case PW_DISCONNECT_REQUEST: + ack = PW_DISCONNECT_ACK; + nak = PW_DISCONNECT_NAK; + break; + + default: /* shouldn't happen */ + return RLM_MODULE_FAIL; + } + +#ifdef WITH_PROXY +#define WAS_PROXIED (request->proxy) +#else +#define WAS_PROXIED (0) +#endif + + if (!WAS_PROXIED) { + /* + * 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 { + /* + * Start the reply code with the proxy reply + * code. + */ + request->reply->code = request->proxy_reply->code; + } + + /* + * Copy State from the request to the reply. + * See RFC 5176 Section 3.3. + */ + vp = paircopy2(request->packet->vps, PW_STATE, 0); + if (vp) pairadd(&request->reply->vps, vp); + + /* + * We may want to over-ride the reply. + */ + rcode = module_send_coa(0, request); + switch (rcode) { + /* + * We need to send CoA-NAK back if Service-Type + * is Authorize-Only. Rely on the user's policy + * to do that. We're not a real NAS, so this + * restriction doesn't (ahem) apply to us. + */ + case RLM_MODULE_FAIL: + case RLM_MODULE_INVALID: + case RLM_MODULE_REJECT: + case RLM_MODULE_USERLOCK: + default: + /* + * Over-ride an ACK with a NAK + */ + 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: + /* + * Do NOT over-ride a previously set value. + * Otherwise an "ok" here will re-write a + * NAK to an ACK. + */ + if (request->reply->code == 0) { + request->reply->code = ack; + } + break; + + } + + return RLM_MODULE_OK; +} + + +/* + * 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; + + rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); + if (rcode < 0) return 0; + + RAD_STATS_TYPE_INC(listener, total_requests); + + if (rcode < 20) { /* AUTH_HDR_LEN */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + return 0; + } + + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); + return 0; + } + + /* + * Some sanity checks, based on the packet code. + */ + switch(code) { + case PW_COA_REQUEST: + case PW_DISCONNECT_REQUEST: + fun = rad_coa_recv; + break; + + 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 */ + + /* + * Now that we've sanity checked everything, receive the + * packet. + */ + packet = rad_recv(listener->fd, client->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_free(&packet); return 0; } @@ -805,7 +1585,7 @@ static int proxy_socket_recv(rad_listen_t *listener, packet = rad_recv(listener->fd, 0); if (!packet) { - radlog(L_ERR, "%s", librad_errstr); + radlog(L_ERR, "%s", fr_strerror()); return 0; } @@ -825,6 +1605,15 @@ static int proxy_socket_recv(rad_listen_t *listener, break; #endif +#ifdef WITH_COA + case PW_DISCONNECT_ACK: + case PW_DISCONNECT_NAK: + case PW_COA_ACK: + case PW_COA_NAK: + fun = rad_coa_reply; + break; +#endif + default: /* * FIXME: Update MIB for packet types? @@ -840,15 +1629,103 @@ static int proxy_socket_recv(rad_listen_t *listener, request = received_proxy_response(packet); if (!request) { + rad_free(&packet); return 0; } +#ifdef WITH_COA + /* + * Distinguish proxied CoA requests from ones we + * originate. + */ + if ((fun == rad_coa_reply) && + (request->packet->code == request->proxy->code)) { + fun = rad_coa_recv; + } +#endif + rad_assert(fun != NULL); *pfun = fun; *prequest = request; return 1; } + +#ifdef WITH_TCP +/* + * Recieve packets from a proxy socket. + */ +static int proxy_socket_tcp_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + REQUEST *request; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + listen_socket_t *sock = listener->data; + char buffer[128]; + + packet = fr_tcp_recv(listener->fd, 0); + if (!packet) { + listener->status = RAD_LISTEN_STATUS_REMOVE_FD; + event_new_fd(listener); + return 0; + } + + /* + * 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 + + default: + /* + * FIXME: Update MIB for packet types? + */ + radlog(L_ERR, "Invalid packet code %d sent to a proxy port " + "from home server %s port %d - ID %d : IGNORED", + packet->code, + ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), + packet->src_port, packet->id); + rad_free(&packet); + return 0; + } + + packet->src_ipaddr = sock->other_ipaddr; + packet->src_port = sock->other_port; + packet->dst_ipaddr = sock->my_ipaddr; + packet->dst_port = sock->my_port; + + /* + * 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; + + return 1; +} +#endif #endif @@ -888,63 +1765,36 @@ static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) 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; - } + /* + * rad_verify is run in event.c, received_proxy_response() + */ return rad_decode(request->proxy_reply, request->proxy, request->home_server->secret); } #endif -#ifdef WITH_SNMP -static int radius_snmp_recv(rad_listen_t *listener, - UNUSED RAD_REQUEST_FUNP *pfun, - UNUSED REQUEST **prequest) -{ - if ((rad_snmp.smux_fd >= 0) && - (rad_snmp.smux_event == SMUX_READ)) { - smux_read(); - } +#include "dhcpd.c" - /* - * If we've got to re-connect, then do so now, - * before calling select again. - */ - if (rad_snmp.smux_event == SMUX_CONNECT) { - smux_connect(); - } +#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 /* - * Reset this every time, as the smux connect may have - * opened a new socket. + * This always gets defined. */ - listener->fd = rad_snmp.smux_fd; - - return 0; -} - - -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 "dhcpd.c" - -static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */ +#endif #ifdef WITH_PROXY /* proxying */ { common_socket_parse, NULL, proxy_socket_recv, proxy_socket_send, socket_print, proxy_socket_encode, proxy_socket_decode }, -#else - { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, #endif /* authentication */ @@ -957,8 +1807,6 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { { common_socket_parse, NULL, acct_socket_recv, acct_socket_send, socket_print, client_socket_encode, client_socket_decode}, -#else - { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, #endif #ifdef WITH_DETAIL @@ -966,8 +1814,6 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { { detail_parse, detail_free, detail_recv, detail_send, detail_print, detail_encode, detail_decode }, -#else - { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, #endif #ifdef WITH_VMPS @@ -975,8 +1821,6 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { { 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 #ifdef WITH_DHCP @@ -984,11 +1828,22 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { { dhcp_socket_parse, NULL, dhcp_socket_recv, dhcp_socket_send, socket_print, dhcp_socket_encode, dhcp_socket_decode }, -#else - { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, #endif - { NULL, NULL, NULL, NULL, NULL, NULL, NULL} /* RAD_LISTEN_SNMP */ +#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 + }; @@ -998,104 +1853,324 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { */ static int listen_bind(rad_listen_t *this) { + 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: - sock->port = 0; - break; +#ifdef WITH_PROXY + case RAD_LISTEN_PROXY: + /* leave it at zero */ + break; +#endif + +#ifdef WITH_VMPS + case RAD_LISTEN_VQP: + sock->my_port = 1589; + break; +#endif + +#ifdef WITH_COA + case RAD_LISTEN_COA: + svp = getservbyname ("radius-dynauth", "udp"); + if (svp != NULL) { + sock->my_port = ntohs(svp->s_port); + } else { + sock->my_port = PW_COA_UDP_PORT; + } + break; +#endif + + 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 -#ifdef WITH_VMPS - case RAD_LISTEN_VQP: - sock->port = 1589; - break; +#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 + } - default: - radlog(L_ERR, "ERROR: Non-fatal internal sanity check failed in bind."); +#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; } } - - this->fd = fr_socket(&sock->ipaddr, sock->port); - if (this->fd < 0) { - radlog(L_ERR, "ERROR: Failed to open socket: %s", - librad_errstr); - return -1; - } +#endif +#endif /* - * 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. + * May be binding to priviledged ports. */ - { - struct sockaddr_storage src; - socklen_t sizeof_src = sizeof(src); + if (sock->my_port != 0) { +#ifdef SO_REUSEADDR + int on = 1; - memset(&src, 0, sizeof_src); - if (getsockname(this->fd, (struct sockaddr *) &src, - &sizeof_src) < 0) { - radlog(L_ERR, "Failed getting socket name: %s", + if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + radlog(L_ERR, "Can't set re-use address option: %s\n", strerror(errno)); return -1; } +#endif - if (!fr_sockaddr2ipaddr(&src, sizeof_src, - &sock->ipaddr, &sock->port)) { - radlog(L_ERR, "Socket has unsupported address family"); + fr_suid_up(); + rcode = bind(this->fd, (struct sockaddr *) &salocal, salen); + fr_suid_down(); + if (rcode < 0) { + char buffer[256]; + close(this->fd); + + this->print(this, buffer, sizeof(buffer)); + radlog(L_ERR, "Failed binding to %s: %s\n", + buffer, strerror(errno)); return -1; } + + /* + * FreeBSD jail issues. We bind to 0.0.0.0, but the + * kernel instead binds us to a 1.2.3.4. If this + * happens, notice, and remember our real IP. + */ + { + struct sockaddr_storage src; + socklen_t sizeof_src = sizeof(src); + + memset(&src, 0, sizeof_src); + if (getsockname(this->fd, (struct sockaddr *) &src, + &sizeof_src) < 0) { + radlog(L_ERR, "Failed getting socket name: %s", + strerror(errno)); + return -1; + } + + if (!fr_sockaddr2ipaddr(&src, sizeof_src, + &sock->my_ipaddr, &sock->my_port)) { + radlog(L_ERR, "Socket has unsupported address family"); + return -1; + } + } } -#ifdef O_NONBLOCK - { - int flags; - - if ((flags = fcntl(this->fd, F_GETFL, NULL)) < 0) { - radlog(L_ERR, "Failure getting socket flags: %s)\n", - strerror(errno)); - return -1; - } - - flags |= O_NONBLOCK; - if( fcntl(this->fd, F_SETFL, flags) < 0) { - radlog(L_ERR, "Failure setting socket flags: %s)\n", - strerror(errno)); +#ifdef WITH_TCP + if (sock->proto == IPPROTO_TCP) { + if (listen(this->fd, 8) < 0) { + close(this->fd); + radlog(L_ERR, "Failed in listen(): %s", strerror(errno)); return -1; } - } + } else #endif + if (fr_nonblock(this->fd) < 0) { + close(this->fd); + radlog(L_ERR, "Failed setting non-blocking on socket: %s", + strerror(errno)); + return -1; + } + + /* + * Mostly for proxy sockets. + */ + sock->other_ipaddr.af = sock->my_ipaddr.af; + +/* + * Don't screw up other people. + */ +#undef proto_for_port +#undef sock_type + return 0; } @@ -1118,21 +2193,46 @@ 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: - case RAD_LISTEN_DHCP: +#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 = NULL; break; #endif +#ifdef WITH_COMMAND_SOCKET + case RAD_LISTEN_COMMAND: + this->data = rad_malloc(sizeof(fr_command_socket_t)); + memset(this->data, 0, sizeof(fr_command_socket_t)); + break; +#endif + default: rad_assert("Unsupported option!" == NULL); break; @@ -1141,79 +2241,104 @@ 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; + rad_listen_t *this; + listen_socket_t *sock; + + if (!home) return 0; + + if ((home->max_connections > 0) && + (home->num_connections >= home->max_connections)) { + DEBUG("WARNING: Home server has too many open connections (%d)", + home->max_connections); + return 0; + } this = listen_alloc(RAD_LISTEN_PROXY); - /* - * 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; - } + sock = this->data; + sock->other_ipaddr = home->ipaddr; + sock->other_port = home->port; + sock->home = home; - last = &(tmp->next); - } + 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 (!old) { + if (this->fd < 0) { + DEBUG("Failed opening client socket: %s", fr_strerror()); listen_free(&this); - return NULL; /* This is a serious error. */ + return 0; } /* - * 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. + * Figure out which port we were bound to. */ - sock = this->data; - memcpy(&sock->ipaddr, &old->ipaddr, sizeof(sock->ipaddr)); + 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; + } + } /* - * Keep going until we find an unused port. + * Tell the event loop that we have a new FD */ - 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 (!event_new_fd(this)) { + listen_free(&this); + return 0; } - - listen_free(&this); - return NULL; + + return 1; } #endif + 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 }, @@ -1229,8 +2354,12 @@ static const FR_NAME_NUMBER listen_compare[] = { #endif #ifdef WITH_DHCP { "dhcp", RAD_LISTEN_DHCP }, -#else - { "dhcp", RAD_LISTEN_NONE }, +#endif +#ifdef WITH_COMMAND_SOCKET + { "control", RAD_LISTEN_COMMAND }, +#endif +#ifdef WITH_COA + { "coa", RAD_LISTEN_COA }, #endif { NULL, 0 }, }; @@ -1256,9 +2385,8 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return NULL; } - type = fr_str2int(listen_compare, listen_type, - RAD_LISTEN_NONE); - 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); @@ -1266,7 +2394,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return NULL; } free(listen_type); - + /* * Allow listen sections in the default config to * refer to a server. @@ -1281,6 +2409,18 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) if (rcode < 0) return NULL; } +#ifdef WITH_PROXY + /* + * We were passed a virtual server, so the caller is + * defining a proxy listener inside of a virtual server. + * This isn't allowed right now. + */ + else if (type == RAD_LISTEN_PROXY) { + radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section"); + return NULL; + } +#endif + /* * Set up cross-type data. */ @@ -1301,6 +2441,26 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return this; } +static int is_loopback(const fr_ipaddr_t *ipaddr) +{ + /* + * We shouldn't proxy on loopback. + */ + if ((ipaddr->af == AF_INET) && + (ipaddr->ipaddr.ip4addr.s_addr == htonl(INADDR_LOOPBACK))) { + return 1; + } + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if ((ipaddr->af == AF_INET6) && + (IN6_IS_ADDR_LINKLOCAL(&ipaddr->ipaddr.ip6addr))) { + return 1; + } +#endif + + return 0; +} + /* * Generate a list of listeners. Takes an input list of * listeners, too, so we don't close sockets with waiting packets. @@ -1309,7 +2469,7 @@ 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; fr_ipaddr_t server_ipaddr; @@ -1358,7 +2518,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 @@ -1371,23 +2531,24 @@ 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; 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(&this); 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); @@ -1401,7 +2562,7 @@ 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 @@ -1420,8 +2581,8 @@ 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) { @@ -1433,7 +2594,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) 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; } @@ -1462,7 +2623,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) 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... @@ -1476,15 +2637,11 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) 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 */ - goto do_proxy; + goto add_sockets; } /* @@ -1499,10 +2656,6 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) return -1; } -#ifdef WITH_PROXY - if (this->type == RAD_LISTEN_PROXY) defined_proxy = 1; -#endif - *last = this; last = &(this->next); } @@ -1527,36 +2680,53 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) return -1; } -#ifdef WITH_PROXY - if (this->type == RAD_LISTEN_PROXY) { - radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section"); - listen_free(head); - return -1; - } -#endif - *last = this; last = &(this->next); } /* loop over "listen" directives in virtual servers */ } /* loop over virtual servers */ +add_sockets: + /* + * No sockets to receive packets, this is an error. + * proxying is pointless. + */ + if (!*head) { + radlog(L_ERR, "The server is not configured to listen on any ports. Cannot start."); + return -1; + } + + /* + * Print out which sockets we're listening on, and + * add them to the event list. + */ + for (this = *head; this != NULL; this = this->next) { +#ifdef WITH_PROXY + if (this->type == RAD_LISTEN_PROXY) { + defined_proxy = 1; + } + +#endif + 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 do_snmp; + home.proto = IPPROTO_UDP; + home.src_ipaddr = server_ipaddr; /* * Find the first authentication port, @@ -1565,79 +2735,53 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) for (this = *head; this != NULL; this = this->next) { if (this->type == RAD_LISTEN_AUTH) { sock = this->data; - if (server_ipaddr.af == AF_UNSPEC) { - server_ipaddr = sock->ipaddr; + + if (is_loopback(&sock->my_ipaddr)) continue; + + if (home.src_ipaddr.af == AF_UNSPEC) { + home.src_ipaddr = sock->my_ipaddr; } - port = sock->port + 2; /* skip acct port */ + port = sock->my_port + 2; break; } - if (this->type == RAD_LISTEN_VQP) { +#ifdef WITH_ACCT + if (this->type == RAD_LISTEN_ACCT) { sock = this->data; - if (server_ipaddr.af == AF_UNSPEC) { - server_ipaddr = sock->ipaddr; + + if (is_loopback(&sock->my_ipaddr)) continue; + + if (home.src_ipaddr.af == AF_UNSPEC) { + home.src_ipaddr = sock->my_ipaddr; } - port = sock->port + 1; + port = sock->my_port + 1; break; } +#endif } - if (port < 0) port = 1024 + (fr_rand() & 0x1ff); - /* * Address is still unspecified, use IPv4. */ - if (server_ipaddr.af == AF_UNSPEC) { - server_ipaddr.af = AF_INET; - server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY); + if (home.src_ipaddr.af == AF_UNSPEC) { + home.src_ipaddr.af = AF_INET; + /* everything else is already set to zero */ } - this = listen_alloc(RAD_LISTEN_PROXY); - sock = this->data; - - /* - * Create the first proxy socket. - */ - sock->ipaddr = server_ipaddr; - - /* - * Try to find a proxy port (value doesn't matter) - */ - for (sock->port = port; - sock->port < 64000; - sock->port++) { - if (listen_bind(this) == 0) { - *last = this; - last = &(this->next); /* just in case */ - break; - } - } + home.ipaddr.af = home.src_ipaddr.af; + /* everything else is already set to zero */ - if (sock->port >= 64000) { + if (!proxy_new_listener(&home, port)) { listen_free(head); - listen_free(&this); - radlog(L_ERR, "Failed to open socket for proxying"); return -1; } } - - do_snmp: /* used only in proxy code. */ #endif -#ifdef WITH_SNMP - if (radius_snmp_init(config)) { - 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; } @@ -1663,6 +2807,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); @@ -1671,3 +2830,57 @@ 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) && + fr_inaddr_any(&sock->my_ipaddr)) { + return this; + } + } + + return NULL; +}