* Network code for Kerberos v5 KDC.
*/
+#define NEED_SOCKETS
#include "k5-int.h"
#include "com_err.h"
#include "kdc_util.h"
#include "extern.h"
#include "kdc5_err.h"
+#include <sys/ioctl.h>
+#include <syslog.h>
#include <ctype.h>
#ifdef HAVE_NETINET_IN_H
#endif
#include <arpa/inet.h>
+#include <net/if.h>
+
extern int errno;
static int *udp_port_fds = (int *) NULL;
static u_short *udp_port_nums = (u_short *) NULL;
static int n_udp_ports = 0;
+static int n_sockets = 0;
static int max_udp_ports = 0;
static fd_set select_fds;
static int select_nfds;
}
#undef safe_realloc
+/* Keep in sync with lib/krb5/os/localaddr.c version. */
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#define USE_AF AF_INET
+#define USE_TYPE SOCK_DGRAM
+#define USE_PROTO 0
+#define SOCKET_ERRNO errno
+
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+ sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+static int
+foreach_localaddr (data, pass1fn, betweenfn, pass2fn)
+ void *data;
+ int (*pass1fn) (void *, struct sockaddr *);
+ int (*betweenfn) (void *);
+ int (*pass2fn) (void *, struct sockaddr *);
+{
+ struct ifreq *ifr, ifreq;
+ struct ifconf ifc;
+ int s, code, n, i;
+ int est_if_count = 8, est_ifreq_size;
+ char *buf = 0;
+ size_t current_buf_size = 0;
+
+ s = socket (USE_AF, USE_TYPE, USE_PROTO);
+ if (s < 0)
+ return SOCKET_ERRNO;
+
+ /* At least on NetBSD, an ifreq can hold an IPv4 address, but
+ isn't big enough for an IPv6 or ethernet address. So add a
+ little more space. */
+ est_ifreq_size = sizeof (struct ifreq) + 8;
+ current_buf_size = est_ifreq_size * est_if_count;
+ buf = malloc (current_buf_size);
+
+ ask_again:
+ memset(buf, 0, current_buf_size);
+ ifc.ifc_len = current_buf_size;
+ ifc.ifc_buf = buf;
+
+ code = ioctl (s, SIOCGIFCONF, (char *)&ifc);
+ if (code < 0) {
+ int retval = errno;
+ closesocket (s);
+ return retval;
+ }
+ /* Test that the buffer was big enough that another ifreq could've
+ fit easily, if the OS wanted to provide one. That seems to be
+ the only indication we get, complicated by the fact that the
+ associated address may make the required storage a little
+ bigger than the size of an ifreq. */
+ if (current_buf_size - ifc.ifc_len < sizeof (struct ifreq) + 40) {
+ int new_size;
+ char *newbuf;
+
+ est_if_count *= 2;
+ new_size = est_ifreq_size * est_if_count;
+ newbuf = realloc (buf, new_size);
+ if (newbuf == 0) {
+ krb5_error_code e = errno;
+ free (buf);
+ return e;
+ }
+ current_buf_size = new_size;
+ buf = newbuf;
+ goto ask_again;
+ }
+
+ n = ifc.ifc_len;
+
+ for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
+ ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i);
+
+ strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+ if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ skip:
+ /* mark for next pass */
+ ifr->ifr_name[0] = 0;
+
+ continue;
+ }
+#if 0 /* Access from same host doesn't work if loopback is omitted? */
+#ifdef IFF_LOOPBACK
+ /* None of the current callers want loopback addresses. */
+ if (ifreq.ifr_flags & IFF_LOOPBACK)
+ goto skip;
+#endif
+#endif
+ /* Ignore interfaces that are down. */
+ if (!(ifreq.ifr_flags & IFF_UP))
+ goto skip;
+
+ if ((*pass1fn) (data, &ifr->ifr_addr)) {
+ abort ();
+ }
+ }
+
+ if (betweenfn && (*betweenfn)(data)) {
+ abort ();
+ }
+
+ if (pass2fn)
+ for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
+ ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i);
+
+ if (ifr->ifr_name[0] == 0)
+ /* Marked in first pass to be ignored. */
+ continue;
+
+ if ((*pass2fn) (data, &ifr->ifr_addr)) {
+ abort ();
+ }
+ }
+ closesocket(s);
+ free (buf);
+
+ return 0;
+}
+
+struct socksetup {
+ const char *prog;
+ krb5_error_code retval;
+};
+
+static int
+setup_port(void *P_data, struct sockaddr *addr)
+{
+ struct socksetup *data = P_data;
+ int sock, i;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *) addr, psin;
+ for (i = 0; i < n_udp_ports; i++) {
+ sock = socket (PF_INET, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ data->retval = errno;
+ com_err(data->prog, data->retval,
+ "Cannot create server socket for port %d address %s",
+ udp_port_nums[i], inet_ntoa (sin->sin_addr));
+ return 1;
+ }
+ psin = *sin;
+ psin.sin_port = htons (udp_port_nums[i]);
+ if (bind (sock, (struct sockaddr *)&psin, sizeof (psin)) == -1) {
+ data->retval = errno;
+ com_err(data->prog, data->retval,
+ "Cannot bind server socket to port %d address %s",
+ udp_port_nums[i], inet_ntoa (sin->sin_addr));
+ return 1;
+ }
+ FD_SET (sock, &select_fds);
+ if (sock > select_nfds)
+ select_nfds = sock;
+ udp_port_fds[n_sockets++] = sock;
+ krb5_klog_syslog (LOG_INFO, "listening on fd %d: %s port %d", sock,
+ inet_ntoa (sin->sin_addr), udp_port_nums[i]);
+ }
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
krb5_error_code
setup_network(prog)
-const char *prog;
+ const char *prog;
{
- struct sockaddr_in sin;
+ struct socksetup setup_data;
krb5_error_code retval;
- u_short port;
char *cp;
- int i;
+ int i, port;
FD_ZERO(&select_fds);
select_nfds = 0;
- memset((char *)&sin, 0, sizeof(sin));
/* Handle each realm's ports */
for (i=0; i<kdc_numrealms; i++) {
}
}
- for (i=0; i<n_udp_ports; i++) {
- if ((udp_port_fds[i] = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
- retval = errno;
- com_err(prog, 0, "Cannot create server socket on port %d",
- udp_port_nums[i]);
- return(retval);
- }
- sin.sin_port = htons(udp_port_nums[i]);
- if (bind(udp_port_fds[i], (struct sockaddr *) &sin,
- sizeof(sin)) == -1) {
- retval = errno;
- com_err(prog, 0, "Cannot bind server socket on port %d",
- udp_port_nums[i]);
- return(retval);
- }
- FD_SET(udp_port_fds[i], &select_fds);
- if (udp_port_fds[i]+1 > select_nfds)
- select_nfds = udp_port_fds[i]+1;
+ setup_data.prog = prog;
+ setup_data.retval = 0;
+ krb5_klog_syslog (LOG_INFO, "setting up network...");
+ if (foreach_localaddr (&setup_data, setup_port, 0, 0)) {
+ return setup_data.retval;
+ }
+ krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
+ if (n_sockets == 0) {
+ com_err(prog, 0, "no sockets set up?");
+ return -1;
}
return 0;
com_err(prog, errno, "while selecting for network input");
continue;
}
- for (i=0; i<n_udp_ports; i++) {
+ for (i=0; i<n_sockets; i++) {
if (FD_ISSET(udp_port_fds[i], &readfds)) {
process_packet(udp_port_fds[i], prog, udp_port_nums[i]);
nfound--;