+#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
+