📄 listen.c
字号:
/* * listen.c Handle socket stuff * * Version: $Id: listen.c,v 1.122 2008/04/20 15:00:06 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2005,2006 The FreeRADIUS server project * Copyright 2005 Alan DeKok <aland@ox.org> */#include <freeradius-devel/ident.h>RCSID("$Id: listen.c,v 1.122 2008/04/20 15:00:06 aland Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/radius_snmp.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/rad_assert.h>#include <freeradius-devel/vqp.h>#include <freeradius-devel/dhcp.h>#include <freeradius-devel/vmps.h>#include <freeradius-devel/detail.h>#ifdef HAVE_SYS_RESOURCE_H#include <sys/resource.h>#endif#ifdef HAVE_NET_IF_H#include <net/if.h>#endif#ifdef HAVE_FCNTL_H#include <fcntl.h>#endif/* * We'll use this below. */typedef int (*rad_listen_parse_t)(CONF_SECTION *, rad_listen_t *);typedef void (*rad_listen_free_t)(rad_listen_t *);typedef struct rad_listen_master_t { rad_listen_parse_t parse; rad_listen_free_t free; rad_listen_recv_t recv; rad_listen_send_t send; rad_listen_print_t print; rad_listen_encode_t encode; rad_listen_decode_t decode;} rad_listen_master_t;typedef struct listen_socket_t { /* * For normal sockets. */ fr_ipaddr_t ipaddr; int port; RADCLIENT_LIST *clients;} listen_socket_t;/* * Find a per-socket client. */RADCLIENT *client_listener_find(const rad_listen_t *listener, const fr_ipaddr_t *ipaddr){ const RADCLIENT_LIST *clients; 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; /* * This HAS to have been initialized previously. */ rad_assert(clients != NULL); return client_find(clients, ipaddr);}static int listen_bind(rad_listen_t *this);/* * Process and reply to a server-status request. * Like rad_authenticate and rad_accounting this should * live in it's own file but it's so small we don't bother. */static int rad_status_server(REQUEST *request){ int rcode = RLM_MODULE_OK; DICT_VALUE *dval; switch (request->listener->type) { case RAD_LISTEN_AUTH: dval = dict_valbyname(PW_AUTZ_TYPE, "Status-Server"); if (dval) { rcode = module_authorize(dval->value, request); } else { rcode = RLM_MODULE_OK; } switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = PW_AUTHENTICATION_ACK; break; case RLM_MODULE_FAIL: case RLM_MODULE_HANDLED: request->reply->code = 0; /* don't reply */ break; default: case RLM_MODULE_REJECT: request->reply->code = PW_AUTHENTICATION_REJECT; break; } break; case RAD_LISTEN_ACCT: dval = dict_valbyname(PW_ACCT_TYPE, "Status-Server"); if (dval) { rcode = module_accounting(dval->value, request); } else { rcode = RLM_MODULE_OK; } switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = PW_ACCOUNTING_RESPONSE; break; default: request->reply->code = 0; /* don't reply */ break; } break; default: return 0; } return 0;}static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize){ 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) { case RAD_LISTEN_AUTH: name = "authentication"; break; case RAD_LISTEN_ACCT: name = "accounting"; break; case RAD_LISTEN_PROXY: name = "proxy"; break;#ifdef WITH_VMPS case RAD_LISTEN_VQP: name = "vmps"; break;#endif#ifdef WITH_DHCP case RAD_LISTEN_DHCP: name = "dhcp"; break;#endif default: name = "??"; break; } if (!this->server) { return snprintf(buffer, bufsize, "%s address %s port %d", name, ip_buf, sock->port); } return snprintf(buffer, bufsize, "%s address %s port %d as server %s", name, ip_buf, sock->port, this->server);}/* * Parse an authentication or accounting socket. */static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this){ int rcode; int listen_port; fr_ipaddr_t ipaddr; listen_socket_t *sock = this->data; char *section_name = NULL; CONF_SECTION *client_cs, *parentcs; /* * Try IPv4 first */ ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE); rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR, &ipaddr.ipaddr.ip4addr, NULL); if (rcode < 0) return -1; if (rcode == 0) { /* successfully parsed IPv4 */ ipaddr.af = AF_INET; } else { /* maybe IPv6? */ rcode = cf_item_parse(cs, "ipv6addr", PW_TYPE_IPV6ADDR, &ipaddr.ipaddr.ip6addr, NULL); if (rcode < 0) return -1; if (rcode == 1) { cf_log_err(cf_sectiontoitem(cs), "No address specified in listen section"); return -1; } ipaddr.af = AF_INET6; } rcode = cf_item_parse(cs, "port", PW_TYPE_INTEGER, &listen_port, "0"); if (rcode < 0) return -1; if ((listen_port < 0) || (listen_port > 65500)) { cf_log_err(cf_sectiontoitem(cs), "Invalid value for \"port\""); return -1; } sock->ipaddr = ipaddr; sock->port = listen_port; /* * And bind it to the port. */ if (listen_bind(this) < 0) { char buffer[128]; cf_log_err(cf_sectiontoitem(cs), "Error binding to port for %s port %d", ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)), sock->port); return -1; } /* * 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); if (!value) { cf_log_err(cf_sectiontoitem(cs), "No interface name given"); return -1; } strcpy(ifreq.ifr_name, value); if (setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifreq, sizeof(ifreq)) < 0) { cf_log_err(cf_sectiontoitem(cs), "Failed binding to interface %s: %s", value, strerror(errno)); return -1; } /* else it worked. */#endif } /* * Proxy sockets don't have clients. */ if (this->type == RAD_LISTEN_PROXY) return 0; /* * The more specific configurations are preferred to more * generic ones. */ client_cs = NULL; parentcs = cf_top_section(cs); rcode = cf_item_parse(cs, "clients", PW_TYPE_STRING_PTR, §ion_name, NULL); if (rcode < 0) return -1; /* bad string */ if (rcode == 0) { /* * Explicit list given: use it. */ client_cs = cf_section_sub_find_name2(parentcs, "clients", section_name); if (!client_cs) { client_cs = cf_section_find(section_name); } if (!client_cs) { cf_log_err(cf_sectiontoitem(cs), "Failed to find clients %s {...}", section_name); free(section_name); return -1; } free(section_name); } /* else there was no "clients = " entry. */ if (!client_cs) { CONF_SECTION *server_cs; server_cs = cf_section_sub_find_name2(parentcs, "server", this->server); /* * Found a "server foo" section. If there are clients * in it, use them. */ if (server_cs && (cf_section_sub_find(server_cs, "client") != NULL)) { client_cs = server_cs; } } /* * Still nothing. Look for global clients. */ if (!client_cs) client_cs = parentcs; sock->clients = clients_parse_section(client_cs); if (!sock->clients) { cf_log_err(cf_sectiontoitem(cs), "Failed to find any clients for this listen section"); return -1; } return 0;}/* * Send an authentication response packet */static int auth_socket_send(rad_listen_t *listener, REQUEST *request){ rad_assert(request->listener == listener); rad_assert(listener->send == auth_socket_send); return rad_send(request->reply, request->packet, request->client->secret);}/* * Send an accounting response packet (or not) */static int acct_socket_send(rad_listen_t *listener, REQUEST *request){ rad_assert(request->listener == listener); rad_assert(listener->send == acct_socket_send); /* * Accounting reject's are silently dropped. * * We do it here to avoid polluting the rest of the * code with this knowledge */ if (request->reply->code == 0) return 0; return rad_send(request->reply, request->packet, request->client->secret);}/* * Send a packet to a home server. * * FIXME: have different code for proxy auth & acct! */static int proxy_socket_send(rad_listen_t *listener, REQUEST *request){ listen_socket_t *sock = listener->data; rad_assert(request->proxy_listener == listener); rad_assert(listener->send == proxy_socket_send); request->proxy->src_ipaddr = sock->ipaddr; request->proxy->src_port = sock->port; return rad_send(request->proxy, request->packet, request->home_server->secret);}/* * 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; 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); if (rcode < 20) { /* AUTH_HDR_LEN */ RAD_SNMP_TYPE_INC(listener, total_malformed_requests); return 0; } if ((client = client_listener_find(listener, &src_ipaddr)) == NULL) { rad_recv_discard(listener->fd); RAD_SNMP_TYPE_INC(listener, total_invalid_requests); if (debug_flag > 0) { char name[1024]; listener->print(listener, name, sizeof(name)); /* * This is debugging rather than logging, so that * DoS attacks don't affect us. */ DEBUG("Ignoring request to %s from unknown client %s port %d", name, inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr, buffer, sizeof(buffer)), src_port); } return 0; } /* * Some sanity checks, based on the packet code. */ switch(code) { case PW_AUTHENTICATION_REQUEST: RAD_SNMP_CLIENT_INC(listener, client, 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -