#endif
+void print_packet(RADIUS_PACKET *packet)
+{
+ char src[256], dst[256];
+
+ ip_ntoh(&packet->src_ipaddr, src, sizeof(src));
+ ip_ntoh(&packet->dst_ipaddr, dst, sizeof(dst));
+
+ fprintf(stderr, "ID %d: %s %d -> %s %d\n", packet->id,
+ src, packet->src_port, dst, packet->dst_port);
+
+}
+
+
/*
* We'll use this below.
*/
rad_listen_decode_t decode;
} rad_listen_master_t;
-typedef struct listen_socket_t {
- /*
- * For normal sockets.
- */
- fr_ipaddr_t ipaddr;
- int port;
-#ifdef SO_BINDTODEVICE
- const char *interface;
-#endif
- RADCLIENT_LIST *clients;
-} listen_socket_t;
-
static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
/*
{
#ifdef WITH_DYNAMIC_CLIENTS
int rcode;
- listen_socket_t *sock;
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);
- 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);
+ client = client_find(clients, ipaddr,sock->proto);
if (!client) {
- static time_t last_printed = 0;
char name[256], buffer[128];
#ifdef WITH_DYNAMIC_CLIENTS
#endif
/*
- * DoS attack quenching, but only in debug mode.
+ * DoS attack quenching, but only in daemon mode.
* If they're running in debug mode, show them
* every packet.
*/
if (debug_flag == 0) {
+ static time_t last_printed = 0;
+
now = time(NULL);
if (last_printed == now) return NULL;
listener->print(listener, name, sizeof(name));
- radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d",
- name, inet_ntop(ipaddr->af, &ipaddr->ipaddr,
- buffer, sizeof(buffer)),
- src_port);
+ radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d"
+#ifdef WITH_TCP
+ " proto %s"
+#endif
+ , name, inet_ntop(ipaddr->af, &ipaddr->ipaddr,
+ buffer, sizeof(buffer)), src_port
+#ifdef WITH_TCP
+ , (sock->proto == IPPROTO_UDP) ? "udp" : "tcp"
+#endif
+ );
return NULL;
}
/*
* Go find the enclosing network again.
*/
- client = client_find(clients, ipaddr);
+ client = client_find(clients, ipaddr, sock->proto);
/*
* WTF?
* can be defined.
*/
rad_assert(client->dynamic == 0);
- } else {
+
+ } else if (!client->dynamic && client->rate_limit) {
/*
* The IP is unknown, so we've found an enclosing
* network. Enable DoS protection. We only
request->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);
goto unknown;
}
- request->reply = rad_alloc(0);
+ request->reply = rad_alloc_reply(request->packet);
if (!request->reply) {
request_free(&request);
goto unknown;
*
* 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);
case RAD_LISTEN_NONE:
#endif
case RAD_LISTEN_AUTH:
- dval = dict_valbyname(PW_AUTZ_TYPE, "Status-Server");
+ dval = dict_valbyname(PW_AUTZ_TYPE, 0, "Status-Server");
if (dval) {
rcode = module_authorize(dval->value, request);
} else {
#ifdef WITH_ACCOUNTING
case RAD_LISTEN_ACCT:
- dval = dict_valbyname(PW_ACCT_TYPE, "Status-Server");
+ dval = dict_valbyname(PW_ACCT_TYPE, 0, "Status-Server");
if (dval) {
rcode = module_accounting(dval->value, request);
} else {
* the WG. We like it, so it goes in here.
*/
case RAD_LISTEN_COA:
- dval = dict_valbyname(PW_RECV_COA_TYPE, "Status-Server");
+ dval = dict_valbyname(PW_RECV_COA_TYPE, 0, "Status-Server");
if (dval) {
rcode = module_recv_coa(dval->value, request);
} else {
return 0;
}
+#ifdef WITH_TCP
+static int 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
+
-static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
+/*
+ * This function is stupid and complicated.
+ */
+static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
{
size_t len;
listen_socket_t *sock = this->data;
ADDSTRING(name);
-#ifdef SO_BINDTODEVICE
if (sock->interface) {
ADDSTRING(" interface ");
ADDSTRING(sock->interface);
}
+
+#ifdef WITH_TCP
+ if (this->recv == 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;
+ }
+
+#ifdef WITH_PROXY
+ /*
+ * Maybe it's a socket that we opened to a home server.
+ */
+ if ((sock->proto == IPPROTO_TCP) &&
+ (this->type == RAD_LISTEN_PROXY)) {
+ ADDSTRING(" (");
+ ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
+ FORWARD;
+
+ ADDSTRING(", ");
+ snprintf(buffer, bufsize, "%d", sock->my_port);
+ FORWARD;
+ ADDSTRING(") -> home_server (");
+
+ if ((sock->other_ipaddr.af == AF_INET) &&
+ (sock->other_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+ strlcpy(buffer, "*", bufsize);
+ } else {
+ ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
+ }
+ FORWARD;
+
+ ADDSTRING(", ");
+ snprintf(buffer, bufsize, "%d", sock->other_port);
+ FORWARD;
+
+ ADDSTRING(")");
+
+ return 1;
+ }
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
+
ADDSTRING(" address ");
- if ((sock->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+ if ((sock->my_ipaddr.af == AF_INET) &&
+ (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
strlcpy(buffer, "*", bufsize);
} else {
- ip_ntoh(&sock->ipaddr, buffer, bufsize);
+ ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
}
FORWARD;
ADDSTRING(" port ");
- snprintf(buffer, bufsize, "%d", sock->port);
+ snprintf(buffer, bufsize, "%d", sock->my_port);
FORWARD;
if (this->server) {
return 1;
}
+extern int check_config; /* radiusd.c */
+
/*
* Parse an authentication or accounting socket.
return -1;
}
- sock->ipaddr = ipaddr;
- sock->port = listen_port;
+ sock->proto = IPPROTO_UDP;
+
+ if (cf_pair_find(cs, "proto")) {
+#ifndef WITH_TCP
+ cf_log_err(cf_sectiontoitem(cs),
+ "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.
+ */
+#ifdef WITH_PROXY
+ if ((this->type == RAD_LISTEN_PROXY) &&
+ (sock->proto != IPPROTO_UDP)) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "Proxy listeners can only listen on proto = udp");
+ return -1;
+ }
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
+ }
+
+ 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");
return -1;
}
sock->interface = value;
+ }
+
+#ifdef WITH_DHCP
+ /*
+ * If we can do broadcasts..
+ */
+ if (cf_pair_find(cs, "broadcast")) {
+#ifndef SO_BROADCAST
+ cf_log_err(cf_sectiontoitem(cs),
+ "System does not support broadcast sockets. Delete this line from the configuration file.");
+ return -1;
+#else
+ const char *value;
+ CONF_PAIR *cp = cf_pair_find(cs, "broadcast");
+
+ if (this->type != RAD_LISTEN_DHCP) {
+ cf_log_err(cf_pairtoitem(cp),
+ "Broadcast can only be set for DHCP listeners. Delete this line from the configuration file.");
+ return -1;
+ }
+
+ rad_assert(cp != NULL);
+ value = cf_pair_value(cp);
+ if (!value) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "No broadcast value given");
+ return -1;
+ }
+
+ /*
+ * Hack... whatever happened to cf_section_parse?
+ */
+ sock->broadcast = (strcmp(value, "yes") == 0);
#endif
}
+#endif
/*
* And bind it to the port.
char buffer[128];
cf_log_err(cf_sectiontoitem(cs),
"Error binding to port for %s port %d",
- ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)),
- sock->port);
+ ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)),
+ sock->my_port);
return -1;
}
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;
}
*/
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);
}
/*
* Inform the user about RFC requirements.
*/
- s1 = pairfind(request->proxy->vps, PW_STATE);
+ s1 = pairfind(request->proxy->vps, PW_STATE, 0);
if (s1) {
- s2 = pairfind(request->proxy_reply->vps, PW_STATE);
+ 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.");
* with Service-Type = Authorize-Only, it MUST
* have a State attribute in it.
*/
- vp = pairfind(request->packet->vps, PW_SERVICE_TYPE);
+ vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0);
if (request->packet->code == PW_COA_REQUEST) {
if (vp && (vp->vp_integer == 17)) {
- vp = pairfind(request->packet->vps, PW_STATE);
+ vp = pairfind(request->packet->vps, PW_STATE, 0);
if (!vp || (vp->length == 0)) {
RDEBUG("ERROR: CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
request->reply->code = PW_COA_NAK;
* Copy State from the request to the reply.
* See RFC 5176 Section 3.3.
*/
- vp = paircopy2(request->packet->vps, PW_STATE);
+ vp = paircopy2(request->packet->vps, PW_STATE, 0);
if (vp) pairadd(&request->reply->vps, vp);
/*
case RLM_MODULE_REJECT:
case RLM_MODULE_USERLOCK:
default:
+ /*
+ * Over-ride an ACK with a NAK
+ */
request->reply->code = nak;
break;
case RLM_MODULE_NOTFOUND:
case RLM_MODULE_OK:
case RLM_MODULE_UPDATED:
- request->reply->code = ack;
- break;
-
+ /*
+ * 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;
int code, src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
RADCLIENT *client;
fr_ipaddr_t src_ipaddr;
&src_ipaddr, src_port)) == NULL) {
rad_recv_discard(listener->fd);
RAD_STATS_TYPE_INC(listener, total_invalid_requests);
-
- if (debug_flag > 0) {
- char name[1024];
-
- listener->print(listener, name, sizeof(name));
-
- /*
- * This is debugging rather than logging, so that
- * DoS attacks don't affect us.
- */
- DEBUG("Ignoring request to %s from unknown client %s port %d",
- name,
- inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr,
- buffer, sizeof(buffer)), src_port);
- }
-
return 0;
}
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
#ifdef WITH_COMMAND_SOCKET
/* TCP command socket */
- { command_socket_parse, NULL,
+ { command_socket_parse, command_socket_free,
command_domain_accept, command_domain_send,
command_socket_print, command_socket_encode, command_socket_decode },
#endif
struct sockaddr_storage salocal;
socklen_t salen;
listen_socket_t *sock = this->data;
+#ifndef WITH_TCP
+#define proto_for_port "udp"
+#define sock_type SOCK_DGRAM
+#else
+ const char *proto_for_port = "udp";
+ int sock_type = SOCK_DGRAM;
+
+ if (sock->proto == IPPROTO_TCP) {
+#ifdef WITH_VMPS
+ if (this->type == RAD_LISTEN_VQP) {
+ radlog(L_ERR, "VQP does not support TCP transport");
+ return -1;
+ }
+#endif
+
+ proto_for_port = "tcp";
+ sock_type = SOCK_STREAM;
+ }
+#endif
/*
* If the port is zero, then it means the appropriate
* thing from /etc/services.
*/
- if (sock->port == 0) {
+ if (sock->my_port == 0) {
struct servent *svp;
switch (this->type) {
case RAD_LISTEN_AUTH:
- svp = getservbyname ("radius", "udp");
+ svp = getservbyname ("radius", proto_for_port);
if (svp != NULL) {
- sock->port = ntohs(svp->s_port);
+ sock->my_port = ntohs(svp->s_port);
} else {
- sock->port = PW_AUTH_UDP_PORT;
+ sock->my_port = PW_AUTH_UDP_PORT;
}
break;
#ifdef WITH_ACCOUNTING
case RAD_LISTEN_ACCT:
- svp = getservbyname ("radacct", "udp");
+ svp = getservbyname ("radacct", proto_for_port);
if (svp != NULL) {
- sock->port = ntohs(svp->s_port);
+ sock->my_port = ntohs(svp->s_port);
} else {
- sock->port = PW_ACCT_UDP_PORT;
+ sock->my_port = PW_ACCT_UDP_PORT;
}
break;
#endif
#ifdef WITH_PROXY
case RAD_LISTEN_PROXY:
- sock->port = 0;
+ /* leave it at zero */
break;
#endif
#ifdef WITH_VMPS
case RAD_LISTEN_VQP:
- sock->port = 1589;
+ sock->my_port = 1589;
break;
#endif
#ifdef WITH_COA
case RAD_LISTEN_COA:
- sock->port = PW_COA_UDP_PORT;
+ svp = getservbyname ("radius-dynauth", "udp");
+ if (svp != NULL) {
+ sock->my_port = ntohs(svp->s_port);
+ } else {
+ sock->my_port = PW_COA_UDP_PORT;
+ }
break;
#endif
default:
- radlog(L_ERR, "ERROR: Non-fatal internal sanity check failed in bind.");
+ DEBUG("WARNING: Internal sanity check failed in binding to socket. Ignoring problem.");
return -1;
}
}
/*
* Copy fr_socket() here, as we may need to bind to a device.
*/
- this->fd = socket(sock->ipaddr.af, SOCK_DGRAM, 0);
+ this->fd = socket(sock->my_ipaddr.af, sock_type, 0);
if (this->fd < 0) {
radlog(L_ERR, "Failed opening socket: %s", strerror(errno));
return -1;
}
-#ifdef SO_BINDTODEVICE
/*
* Bind to a device BEFORE touching IP addresses.
*/
if (sock->interface) {
+#ifdef SO_BINDTODEVICE
struct ifreq ifreq;
strcpy(ifreq.ifr_name, sock->interface);
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
return -1;
}
#endif
-
+
/*
* Set up sockaddr stuff.
*/
- if (!fr_ipaddr2sockaddr(&sock->ipaddr, sock->port, &salocal, &salen)) {
+ if (!fr_ipaddr2sockaddr(&sock->my_ipaddr, sock->my_port, &salocal, &salen)) {
close(this->fd);
return -1;
}
#ifdef HAVE_STRUCT_SOCKADDR_IN6
- if (sock->ipaddr.af == AF_INET6) {
+ if (sock->my_ipaddr.af == AF_INET6) {
/*
* Listening on '::' does NOT get you IPv4 to
* IPv6 mapping. You've got to listen on an IPv4
*/
#ifdef IPV6_V6ONLY
- if (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr)) {
int on = 1;
setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY,
}
#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
- /*
- * May be binding to priviledged ports.
- */
- fr_suid_up();
- rcode = bind(this->fd, (struct sockaddr *) &salocal, salen);
- fr_suid_down();
- if (rcode < 0) {
- char buffer[256];
- close(this->fd);
+ if (sock->my_ipaddr.af == AF_INET) {
+ UNUSED int flag;
- this->print(this, buffer, sizeof(buffer));
- radlog(L_ERR, "Failed binding to %s: %s\n",
- buffer, strerror(errno));
- return -1;
+#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
+
/*
- * 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;
}
#ifdef WITH_VMPS
case RAD_LISTEN_VQP:
#endif
-#ifdef WITH_DHCP
- case RAD_LISTEN_DHCP:
-#endif
#ifdef WITH_COA
case RAD_LISTEN_COA:
#endif
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;
return this;
}
-
#ifdef WITH_PROXY
/*
* Externally visible function for creating a new proxy LISTENER.
* Not thread-safe, but all calls to it are protected by the
* proxy mutex in event.c
*/
-rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
+int proxy_new_listener(home_server *home, int src_port)
{
- int last_proxy_port, port;
- rad_listen_t *this, *tmp, **last;
- listen_socket_t *sock, *old;
+ rad_listen_t *this;
+ listen_socket_t *sock;
- /*
- * Find an existing proxy socket to copy.
- */
- last_proxy_port = 0;
- old = NULL;
- last = &mainconfig.listen;
- for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) {
- /*
- * Not proxy, ignore it.
- */
- if (tmp->type != RAD_LISTEN_PROXY) continue;
+ if (!home) return 0;
- sock = tmp->data;
+ if ((home->max_connections > 0) &&
+ (home->num_connections >= home->max_connections)) {
+ DEBUG("WARNING: Home server has too many open connections (%d)",
+ home->max_connections);
+ return 0;
+ }
- /*
- * If we were asked to copy a specific one, do
- * so. If we're just finding one that already
- * exists, return a pointer to it. Otherwise,
- * create ANOTHER one with the same IP address.
- */
- if ((ipaddr->af != AF_UNSPEC) &&
- (fr_ipaddr_cmp(&sock->ipaddr, ipaddr) != 0)) {
- if (exists) return tmp;
- continue;
- }
-
- if (sock->port > last_proxy_port) {
- last_proxy_port = sock->port + 1;
- }
- if (!old) old = sock;
+ this = listen_alloc(RAD_LISTEN_PROXY);
- last = &(tmp->next);
- }
+ sock = this->data;
+ sock->other_ipaddr = home->ipaddr;
+ sock->other_port = home->port;
+ sock->home = home;
+
+ sock->my_ipaddr = home->src_ipaddr;
+ sock->my_port = src_port;
+ sock->proto = home->proto;
+
+#ifdef WITH_TCP
+ sock->last_packet = time(NULL);
+
+ if (home->proto == IPPROTO_TCP) {
+ this->recv = proxy_socket_tcp_recv;
- if (!old) {
/*
- * The socket MUST already exist if we're binding
- * to an address while proxying.
+ * FIXME: connect() is blocking!
+ * We do this with the proxy mutex locked, which may
+ * cause large delays!
*
- * If we're initializing the server, it's OK for the
- * socket to NOT exist.
+ * http://www.developerweb.net/forum/showthread.php?p=13486
*/
- if (!exists) return NULL;
-
- this = listen_alloc(RAD_LISTEN_PROXY);
-
- sock = this->data;
- sock->ipaddr = *ipaddr;
+ this->fd = fr_tcp_client_socket(&home->src_ipaddr,
+ &home->ipaddr, home->port);
+ } else
+#endif
+ this->fd = fr_socket(&home->src_ipaddr, src_port);
- } else {
- this = listen_alloc(RAD_LISTEN_PROXY);
-
- sock = this->data;
- sock->ipaddr = old->ipaddr;
+ if (this->fd < 0) {
+ DEBUG("Failed opening client socket: %s", fr_strerror());
+ listen_free(&this);
+ return 0;
}
/*
- * Keep going until we find an unused port.
+ * Figure out which port we were bound to.
*/
- for (port = last_proxy_port; port < 64000; port++) {
- int rcode;
-
- sock->port = port;
-
- rcode = listen_bind(this);
- if (rcode < 0) {
+ if (sock->my_port == 0) {
+ struct sockaddr_storage src;
+ socklen_t sizeof_src = sizeof(src);
+
+ memset(&src, 0, sizeof_src);
+ if (getsockname(this->fd, (struct sockaddr *) &src,
+ &sizeof_src) < 0) {
+ radlog(L_ERR, "Failed getting socket name: %s",
+ strerror(errno));
listen_free(&this);
- return NULL;
+ return 0;
}
- /*
- * Add the new listener to the list of
- * listeners.
- */
- *last = this;
- return this;
+ if (!fr_sockaddr2ipaddr(&src, sizeof_src,
+ &sock->my_ipaddr, &sock->my_port)) {
+ radlog(L_ERR, "Socket has unsupported address family");
+ listen_free(&this);
+ return 0;
+ }
}
- listen_free(&this);
- return NULL;
+ /*
+ * Tell the event loop that we have a new FD
+ */
+ if (!event_new_fd(this)) {
+ listen_free(&this);
+ return 0;
+ }
+
+ return 1;
}
#endif
+
static const FR_NAME_NUMBER listen_compare[] = {
#ifdef WITH_STATS
{ "status", RAD_LISTEN_NONE },
return NULL;
}
free(listen_type);
-
+
/*
* Allow listen sections in the default config to
* refer to a server.
if (rcode < 0) return NULL;
}
+#ifdef WITH_PROXY
+ /*
+ * We were passed a virtual server, so the caller is
+ * defining a proxy listener inside of a virtual server.
+ * This isn't allowed right now.
+ */
+ else if (type == RAD_LISTEN_PROXY) {
+ radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section");
+ return NULL;
+ }
+#endif
+
/*
* Set up cross-type data.
*/
return this;
}
+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.
listen_socket_t *sock;
server_ipaddr.af = AF_INET;
- radlog(L_INFO, "WARNING: The directive 'bind_adress' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
+ radlog(L_INFO, "WARNING: The directive 'bind_address' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
bind_it:
#ifdef WITH_VMPS
sock = this->data;
- sock->ipaddr = server_ipaddr;
- sock->port = auth_port;
+ sock->my_ipaddr = server_ipaddr;
+ sock->my_port = auth_port;
sock->clients = clients_parse_section(config);
if (!sock->clients) {
if (listen_bind(this) < 0) {
listen_free(head);
- radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->port);
+ radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->my_port);
listen_free(&this);
return -1;
}
- auth_port = sock->port; /* may have been updated in listen_bind */
+ auth_port = sock->my_port; /* may have been updated in listen_bind */
if (override) {
cs = cf_section_sub_find_name2(config, "server",
mainconfig.name);
/*
* No acct for vmpsd
*/
- if (strcmp(progname, "vmpsd") == 0) goto do_proxy;
+ if (strcmp(progname, "vmpsd") == 0) goto add_sockets;
#endif
#ifdef WITH_ACCOUNTING
* The accounting port is always the
* authentication port + 1
*/
- sock->ipaddr = server_ipaddr;
- sock->port = auth_port + 1;
+ sock->my_ipaddr = server_ipaddr;
+ sock->my_port = auth_port + 1;
sock->clients = clients_parse_section(config);
if (!sock->clients) {
if (listen_bind(this) < 0) {
listen_free(&this);
listen_free(head);
- radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->port);
+ radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->my_port);
return -1;
}
cs = cf_section_sub_find_name2(config, "server",
mainconfig.name);
- if (!cs) goto do_proxy;
+ if (!cs) goto add_sockets;
/*
* Should really abstract this code...
return -1;
}
-#ifdef WITH_PROXY
- if (this->type == RAD_LISTEN_PROXY) defined_proxy = 1;
-#endif
-
*last = this;
last = &(this->next);
} /* loop over "listen" directives in server <foo> */
- goto do_proxy;
+ goto add_sockets;
}
/*
return -1;
}
-#ifdef WITH_PROXY
- if (this->type == RAD_LISTEN_PROXY) defined_proxy = 1;
-#endif
-
*last = this;
last = &(this->next);
}
return -1;
}
-#ifdef WITH_PROXY
- if (this->type == RAD_LISTEN_PROXY) {
- radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section");
- listen_free(head);
- return -1;
- }
-#endif
-
*last = this;
last = &(this->next);
} /* loop over "listen" directives in virtual servers */
} /* loop over virtual servers */
+add_sockets:
+ /*
+ * No sockets to receive packets, this is an error.
+ * proxying is pointless.
+ */
+ if (!*head) {
+ radlog(L_ERR, "The server is not configured to listen on any ports. Cannot start.");
+ return -1;
+ }
+
+ /*
+ * Print out which sockets we're listening on, and
+ * add them to the event list.
+ */
+ for (this = *head; this != NULL; this = this->next) {
+#ifdef WITH_PROXY
+ if (this->type == RAD_LISTEN_PROXY) {
+ defined_proxy = 1;
+ }
+
+#endif
+ 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;
- /*
- * No sockets to receive packets, therefore
- * proxying is pointless.
- */
- if (!*head) return -1;
+ memset(&home, 0, sizeof(home));
/*
- * Create *additional* proxy listeners, based
- * on their src_ipaddr.
- */
- if (home_server_create_listeners(*head) != 0) return -1;
-
- /*
- *
+ *
*/
- while (*last) last = &((*last)->next);
-
- if (defined_proxy) goto done;
+ home.proto = IPPROTO_UDP;
+ home.src_ipaddr = server_ipaddr;
/*
* Find the first authentication port,
for (this = *head; this != NULL; this = this->next) {
if (this->type == RAD_LISTEN_AUTH) {
sock = this->data;
- if (server_ipaddr.af == AF_UNSPEC) {
- server_ipaddr = sock->ipaddr;
+
+ if (is_loopback(&sock->my_ipaddr)) continue;
+
+ if (home.src_ipaddr.af == AF_UNSPEC) {
+ home.src_ipaddr = sock->my_ipaddr;
}
- port = sock->port + 2; /* skip acct port */
+ port = sock->my_port + 2;
break;
}
-#ifdef WITH_VMPS
- if (this->type == RAD_LISTEN_VQP) {
+#ifdef WITH_ACCT
+ if (this->type == RAD_LISTEN_ACCT) {
sock = this->data;
- if (server_ipaddr.af == AF_UNSPEC) {
- server_ipaddr = sock->ipaddr;
+
+ if (is_loopback(&sock->my_ipaddr)) continue;
+
+ if (home.src_ipaddr.af == AF_UNSPEC) {
+ home.src_ipaddr = sock->my_ipaddr;
}
- port = sock->port + 1;
+ port = sock->my_port + 1;
break;
}
#endif
}
- if (port < 0) port = 1024 + (fr_rand() & 0x1ff);
-
/*
* Address is still unspecified, use IPv4.
*/
- if (server_ipaddr.af == AF_UNSPEC) {
- server_ipaddr.af = AF_INET;
- server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
+ if (home.src_ipaddr.af == AF_UNSPEC) {
+ home.src_ipaddr.af = AF_INET;
+ /* everything else is already set to zero */
}
- this = listen_alloc(RAD_LISTEN_PROXY);
- sock = this->data;
-
- /*
- * Create the first proxy socket.
- */
- sock->ipaddr = server_ipaddr;
-
- /*
- * Try to find a proxy port (value doesn't matter)
- */
- for (sock->port = port;
- sock->port < 64000;
- sock->port++) {
- if (listen_bind(this) == 0) {
- *last = this;
- last = &(this->next); /* just in case */
- break;
- }
- }
+ home.ipaddr.af = home.src_ipaddr.af;
+ /* everything else is already set to zero */
- if (sock->port >= 64000) {
+ if (!proxy_new_listener(&home, port)) {
listen_free(head);
- listen_free(&this);
- radlog(L_ERR, "Failed to open socket for proxying");
return -1;
}
}
-
- done: /* used only in proxy code. */
#endif
+ /*
+ * Haven't defined any sockets. Die.
+ */
+ if (!*head) return -1;
+
+
return 0;
}
if (master_listen[this->type].free) {
master_listen[this->type].free(this);
}
+
+#ifdef WITH_TCP
+ if ((this->type == RAD_LISTEN_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);
sock = this->data;
- if ((sock->port == port) &&
- (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
+ if ((sock->my_port == port) &&
+ (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
return sock->clients;
}
}
sock = this->data;
- if ((sock->port == port) &&
- (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
+ if ((sock->my_port == port) &&
+ (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
return this;
}
- if ((sock->port == port) &&
- ((sock->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) {
+ if ((sock->my_port == port) &&
+ fr_inaddr_any(&sock->my_ipaddr)) {
return this;
}
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- if ((sock->port == port) &&
- (sock->ipaddr.af == AF_INET6) &&
- (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr))) {
- return this;
- }
-#endif
}
return NULL;