Skip to content

Commit

Permalink
Patches from Alec Peterson, plus some work of my own, to let a multih…
Browse files Browse the repository at this point in the history
…omed

KDC respond to requests from the same IP address that the requests were sent
to.

**N.B. This will perform worse in the case of addresses dynamically added
and removed after the KDC has started, since it will be incapable of using
any new addresses.

I'm unclear on why the loopback interface address needs to be included in
the list of addresses.  Apparently, on NetBSD-current, if it's not, packets
sent to other local addresses but over the loopback interface are queued but
not received??  Needs further investigation; could just be a NetBSD bug.

	* configure.in: Invoke KRB5_SOCKADDR_SA_LEN.

	* network.c: Include <sys/ioctl.h>, <syslog.h>, <net/if.h>.
	(foreach_localaddr): New function, copied from
	lib/krb5/os/localaddr.c.  Tweaked to not exclude loopback
	interface.
	(NEED_SOCKETS): Define before including k5-int.h.
	(n_sockets): New variable.
	(setup_port): New function; creates listening udp ports given an
	address.
	(setup_network): Call foreach_localaddr to set up listening
	sockets on each local address, so we can always respond from the
	receiving address.
	(listen_and_process): Use n_sockets as upper bound of loop.


git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@12070 dc483132-0cff-0310-8789-dd5450dbe970
  • Loading branch information
raeburn committed Feb 25, 2000
1 parent 1e6959b commit 349d369
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 24 deletions.
18 changes: 18 additions & 0 deletions src/kdc/ChangeLog
@@ -1,3 +1,21 @@
2000-02-25 Ken Raeburn <raeburn@mit.edu>
Alec H. Peterson <ahp@hilander.com>

* configure.in: Invoke KRB5_SOCKADDR_SA_LEN.

* network.c: Include <sys/ioctl.h>, <syslog.h>, <net/if.h>.
(foreach_localaddr): New function, copied from
lib/krb5/os/localaddr.c. Tweaked to not exclude loopback
interface.
(NEED_SOCKETS): Define before including k5-int.h.
(n_sockets): New variable.
(setup_port): New function; creates listening udp ports given an
address.
(setup_network): Call foreach_localaddr to set up listening
sockets on each local address, so we can always respond from the
receiving address.
(listen_and_process): Use n_sockets as upper bound of loop.

2000-02-24 Ken Raeburn <raeburn@mit.edu>

* kerberos_v4.c (v4_klog): Don't treat the formatted text as a
Expand Down
1 change: 1 addition & 0 deletions src/kdc/configure.in
Expand Up @@ -5,6 +5,7 @@ AC_HEADER_CHECK(termios.h,AC_FUNC_CHECK([tcsetattr],AC_DEFINE(POSIX_TERMIOS)))
AC_CHECK_HEADERS(syslog.h stdarg.h sys/select.h)
AC_CHECK_FUNCS(openlog syslog closelog strftime vsprintf)
AC_PROG_AWK
KRB5_SOCKADDR_SA_LEN
CHECK_SIGNALS
HAS_ANSI_VOLATILE
dnl
Expand Down
223 changes: 199 additions & 24 deletions src/kdc/network.c
Expand Up @@ -26,11 +26,14 @@
* 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
Expand All @@ -43,11 +46,14 @@
#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;
Expand Down Expand Up @@ -87,19 +93,196 @@ static krb5_error_code add_port(port)
}
#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++) {
Expand All @@ -118,24 +301,16 @@ const char *prog;
}
}

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;
Expand Down Expand Up @@ -223,7 +398,7 @@ const char *prog;
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--;
Expand Down

0 comments on commit 349d369

Please sign in to comment.