adding zephyr-im master branch
This commit is contained in:
89
server/Makefile.in
Normal file
89
server/Makefile.in
Normal file
@ -0,0 +1,89 @@
|
||||
SHELL=@SHELL@
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
datadir=@datadir@
|
||||
sysconfdir=@sysconfdir@
|
||||
sbindir=@sbindir@
|
||||
lsbindir=@lsbindir@
|
||||
datarootdir=@datarootdir@
|
||||
|
||||
includedir=@includedir@
|
||||
mandir=@mandir@
|
||||
libdir=@libdir@
|
||||
top_builddir=..
|
||||
|
||||
srcdir=@srcdir@
|
||||
top_srcdir=@top_srcdir@
|
||||
BUILDTOP=..
|
||||
VPATH=@srcdir@
|
||||
LIBTOOL=@LIBTOOL@
|
||||
CC=@CC@
|
||||
INSTALL=@INSTALL@
|
||||
|
||||
editman = sed \
|
||||
-e 's|@datadir[@]|${datadir}|g' \
|
||||
-e 's|@sysconfdir[@]|${sysconfdir}|g' \
|
||||
-e 's|@sbindir[@]|${sbindir}|g' \
|
||||
-e 's|@lsbindir[@]|${lsbindir}|g'
|
||||
|
||||
LIBZEPHYR=${BUILDTOP}/lib/libzephyr.la
|
||||
CPPFLAGS=@CPPFLAGS@
|
||||
CFLAGS=@CFLAGS@
|
||||
ALL_CFLAGS=${CFLAGS} -DSYSCONFDIR=\"${sysconfdir}\" -I${top_srcdir}/h \
|
||||
-I${BUILDTOP}/h -I. ${CPPFLAGS}
|
||||
LDFLAGS=@LDFLAGS@
|
||||
LIBS=${LIBZEPHYR} @LIBS@ -lcom_err @ARES_LIBS@
|
||||
HESIOD_LIBS=@HESIOD_LIBS@
|
||||
|
||||
NMOBJS= zsrv_err.o access.o acl_files.o bdump.o class.o client.o common.o \
|
||||
dispatch.o kstuff.o global.o server.o subscr.o timer.o uloc.o \
|
||||
zstring.o realm.o version.o utf8proc.o
|
||||
|
||||
OBJS= main.o $(NMOBJS)
|
||||
|
||||
TESTOBJS = test_server.o $(NMOBJS)
|
||||
|
||||
all: zephyrd zephyrd.8 test_server
|
||||
|
||||
zephyrd: ${OBJS} ${LIBZEPHYR}
|
||||
${LIBTOOL} --mode=link ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS} ${HESIOD_LIBS}
|
||||
|
||||
test_server: $(TESTOBJS)
|
||||
${LIBTOOL} --mode=link ${CC} ${LDFLAGS} -o $@ ${TESTOBJS} ${LIBS} ${HESIOD_LIBS}
|
||||
|
||||
zsrv_err.h: zsrv_err.c
|
||||
zsrv_err.c: zsrv_err.et
|
||||
compile_et ${srcdir}/zsrv_err.et
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${ALL_CFLAGS} $<
|
||||
|
||||
zephyrd.8: ${srcdir}/zephyrd.8.in Makefile
|
||||
${editman} ${srcdir}/$@.in > $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
check: test_server
|
||||
./test_server
|
||||
|
||||
# No dependency on zephyrd, to avoid rebuilding version.o.
|
||||
install: zephyrd.8 zephyrd
|
||||
${LIBTOOL} --mode=install ${INSTALL} -m 755 zephyrd \
|
||||
${DESTDIR}${sbindir}
|
||||
${INSTALL} -m 644 zephyrd.8 ${DESTDIR}${mandir}/man8
|
||||
${INSTALL} -m 644 ${srcdir}/default.subscriptions \
|
||||
${DESTDIR}${sysconfdir}/zephyr
|
||||
|
||||
clean:
|
||||
${LIBTOOL} --mode=clean rm -f zephyrd test_server
|
||||
rm -f ${OBJS} zsrv_err.[ch]
|
||||
rm -f zephyrd.8
|
||||
|
||||
${OBJS} ${TESTOBJS}: zserver.h zsrv_err.h timer.h zsrv_conf.h zstring.h access.h acl.h
|
||||
${OBJS} ${TESTOBJS}: ${top_srcdir}/h/internal.h ${top_srcdir}/h/sysdep.h
|
||||
${OBJS} ${TESTOBJS}: ${BUILDTOP}/h/config.h ${BUILDTOP}/h/zephyr/zephyr.h
|
||||
${OBJS} ${TESTOBJS}: ${BUILDTOP}/h/zephyr/zephyr_err.h
|
||||
version.o: ${BUILDTOP}/h/zephyr_version.h
|
||||
|
||||
.PHONY: all check install clean
|
||||
|
251
server/access.c
Normal file
251
server/access.c
Normal file
@ -0,0 +1,251 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for dealing with acl's.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1987 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
|
||||
#if !defined (lint) && !defined (SABER)
|
||||
static const char rcsid_access_c[] =
|
||||
"$Id$";
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
* External routines:
|
||||
*
|
||||
* int access_check(sender, who, acl, accesstype)
|
||||
* char *sender;
|
||||
* struct sockaddr_in *who;
|
||||
* Acl *acl;
|
||||
* Access accesstype;
|
||||
*
|
||||
* int opstaff_check(sender)
|
||||
* char *sender;
|
||||
*
|
||||
* void access_init();
|
||||
*
|
||||
* void access_reinit();
|
||||
*/
|
||||
|
||||
/*
|
||||
* Each restricted class has four ACL's associated with it,
|
||||
* governing subscriptions, transmission, and instance restrictions.
|
||||
* This module provides the 'glue' between the standard Athena ACL
|
||||
* routines and the support needed by the Zephyr server.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Our private types for the acl_types field in the Acl structure.
|
||||
* -TYT 8/14/90
|
||||
*/
|
||||
#define ACL_XMT 1
|
||||
#define ACL_SUB 2
|
||||
#define ACL_IWS 4
|
||||
#define ACL_IUI 8
|
||||
|
||||
static void check_acl(Acl *acl);
|
||||
static void check_acl_type(Acl *acl, Access accesstype, int typeflag);
|
||||
static void access_setup(int first);
|
||||
|
||||
/*
|
||||
* check access. return 1 if ok, 0 if not ok.
|
||||
*/
|
||||
|
||||
int
|
||||
access_check(char *sender,
|
||||
struct sockaddr_in *who,
|
||||
Acl *acl,
|
||||
Access accesstype)
|
||||
{
|
||||
char buf[1024]; /* holds the real acl name */
|
||||
char *prefix;
|
||||
int flag;
|
||||
int retval;
|
||||
|
||||
switch (accesstype) {
|
||||
case TRANSMIT:
|
||||
prefix = "xmt";
|
||||
flag = ACL_XMT;
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
prefix = "sub";
|
||||
flag = ACL_SUB;
|
||||
break;
|
||||
case INSTWILD:
|
||||
prefix = "iws";
|
||||
flag = ACL_IWS;
|
||||
break;
|
||||
case INSTUID:
|
||||
prefix = "iui";
|
||||
flag = ACL_IUI;
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR, "unknown access type %d", (int) accesstype);
|
||||
return 0;
|
||||
}
|
||||
if (!(acl->acl_types & flag)) /* no acl ==> no restriction */
|
||||
return 1;
|
||||
snprintf(buf, sizeof buf, "%s/%s-%s.acl", acl_dir, prefix, acl->acl_filename);
|
||||
/*
|
||||
* If we can't load it (because it probably doesn't exist),
|
||||
* we deny access.
|
||||
*/
|
||||
retval = acl_load(buf);
|
||||
if (retval < 0) {
|
||||
syslog(LOG_DEBUG, "Error in acl_load of %s for %s",
|
||||
buf, sender ? sender : "unauth client");
|
||||
return 0;
|
||||
}
|
||||
return acl_check(buf, sender, who);
|
||||
}
|
||||
|
||||
int
|
||||
opstaff_check(char *sender)
|
||||
{
|
||||
char buf[1024]; /* holds the real acl name */
|
||||
int retval;
|
||||
|
||||
snprintf(buf, sizeof buf, "%s/opstaff.acl", acl_dir);
|
||||
/*
|
||||
* If we can't load it (because it probably doesn't exist),
|
||||
* we deny access.
|
||||
*/
|
||||
retval = acl_load(buf);
|
||||
if (retval < 0) {
|
||||
syslog(LOG_DEBUG, "Error in acl_load of %s for %s",
|
||||
buf, sender ? sender : "unauth client");
|
||||
return 0;
|
||||
}
|
||||
return acl_check(buf, sender, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
check_acl(Acl *acl)
|
||||
{
|
||||
acl->acl_types = 0;
|
||||
check_acl_type(acl, TRANSMIT, ACL_XMT);
|
||||
check_acl_type(acl, SUBSCRIBE, ACL_SUB);
|
||||
check_acl_type(acl, INSTWILD, ACL_IWS);
|
||||
check_acl_type(acl, INSTUID, ACL_IUI);
|
||||
}
|
||||
|
||||
static void
|
||||
check_acl_type(Acl *acl,
|
||||
Access accesstype,
|
||||
int typeflag)
|
||||
{
|
||||
char buf[1024]; /* holds the real acl name */
|
||||
char *prefix;
|
||||
|
||||
switch (accesstype) {
|
||||
case TRANSMIT:
|
||||
prefix = "xmt";
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
prefix = "sub";
|
||||
break;
|
||||
case INSTWILD:
|
||||
prefix = "iws";
|
||||
break;
|
||||
case INSTUID:
|
||||
prefix = "iui";
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR, "unknown access type %d", (int) accesstype);
|
||||
return;
|
||||
}
|
||||
snprintf(buf, sizeof buf, "%s/%s-%s.acl", acl_dir, prefix, acl->acl_filename);
|
||||
if (!access(buf, F_OK))
|
||||
acl->acl_types |= typeflag;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Re-init code written by TYT, 8/14/90.
|
||||
*
|
||||
* General plan of action; we reread the registry list, and add any
|
||||
* new restricted classes. If any restricted classes disappear (this
|
||||
* should be rarely) the Acl structure is not deallocated; rather,
|
||||
* the acl_types field will be left at zero, since there will be no
|
||||
* acl files for the (non-)restricted class.
|
||||
*/
|
||||
static void
|
||||
access_setup(int first)
|
||||
{
|
||||
char buf[1024];
|
||||
char class_name[512]; /* assume class names <= 511 bytes */
|
||||
FILE *registry;
|
||||
Acl *acl;
|
||||
int len;
|
||||
char *colon_idx;
|
||||
Code_t retval = 0;
|
||||
|
||||
snprintf(buf, sizeof buf, "%s/%s", acl_dir, ZEPHYR_CLASS_REGISTRY);
|
||||
registry = fopen(buf, "r");
|
||||
if (!registry) {
|
||||
syslog(LOG_ERR, "no registry available, all classes are free");
|
||||
return;
|
||||
}
|
||||
while (fgets(class_name, 512, registry)) {
|
||||
colon_idx = strchr(class_name, ':');
|
||||
if (colon_idx != NULL)
|
||||
*colon_idx = '\0';
|
||||
else if ((len = strlen(class_name)) != 0)
|
||||
class_name[len - 1] = '\0';
|
||||
acl = 0;
|
||||
if (!first) {
|
||||
String *z;
|
||||
|
||||
z = make_string(class_name,1);
|
||||
acl = class_get_acl(z);
|
||||
free_string(z);
|
||||
}
|
||||
if (!acl) {
|
||||
acl = (Acl *) malloc(sizeof(Acl));
|
||||
if (!acl) {
|
||||
syslog(LOG_ERR, "no mem acl alloc");
|
||||
abort();
|
||||
}
|
||||
acl->acl_filename = strsave(class_name);
|
||||
check_acl(acl);
|
||||
|
||||
if (!first) {
|
||||
/* Try to restrict already existing class */
|
||||
retval = class_restrict(class_name, acl);
|
||||
if (retval == ZSRV_NOCLASS)
|
||||
retval = class_setup_restricted(class_name, acl);
|
||||
} else {
|
||||
retval = class_setup_restricted(class_name, acl);
|
||||
}
|
||||
}
|
||||
if (retval) {
|
||||
syslog(LOG_ERR, "can't restrict %s: %s",
|
||||
class_name, error_message(retval));
|
||||
continue;
|
||||
}
|
||||
zdbug((LOG_DEBUG, "restricted %s", class_name));
|
||||
}
|
||||
fclose(registry);
|
||||
}
|
||||
|
||||
void
|
||||
access_init(void)
|
||||
{
|
||||
access_setup(1);
|
||||
}
|
||||
|
||||
void
|
||||
access_reinit(void)
|
||||
{
|
||||
acl_cache_reset();
|
||||
access_setup(0);
|
||||
}
|
38
server/access.h
Normal file
38
server/access.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the Project Athena Zephyr Notification System.
|
||||
*
|
||||
* It contains declarations for use in the server, relating to access
|
||||
* control.
|
||||
*
|
||||
* Created by Ken Raeburn.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1990 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include "acl.h"
|
||||
#include "zstring.h"
|
||||
|
||||
typedef enum _Access {
|
||||
TRANSMIT, /* use transmission acl */
|
||||
SUBSCRIBE, /* use subscription acl */
|
||||
INSTWILD, /* use instance wildcard acl */
|
||||
INSTUID /* use instance UID identity acl */
|
||||
} Access;
|
||||
|
||||
typedef struct _Acl {
|
||||
char *acl_filename;
|
||||
int acl_types; /* Internal; access fields present. */
|
||||
} Acl;
|
||||
|
||||
/* found in access.c */
|
||||
void access_init(void);
|
||||
void access_reinit(void);
|
||||
|
||||
/* found in acl_files.c */
|
||||
int acl_load(char *);
|
25
server/acl.h
Normal file
25
server/acl.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains definitions for the ACL library
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1987 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef __ACL__
|
||||
#define __ACL__
|
||||
|
||||
int acl_add(char *, char *);
|
||||
int acl_check(char *, char *, struct sockaddr_in *);
|
||||
int acl_delete(char *, char *);
|
||||
int acl_initialize(char *, int);
|
||||
void acl_cache_reset(void);
|
||||
|
||||
#endif /* __ACL__ */
|
||||
|
375
server/acl_files.c
Normal file
375
server/acl_files.c
Normal file
@ -0,0 +1,375 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for maintaining Access Control Lists.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
/* Define this if you really want the ACL-writing code included. */
|
||||
|
||||
/*
|
||||
* Stolen from lib/acl_files.c because acl_load needs to be externally
|
||||
* declared and not statically declared.
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
#include <fnmatch.h>
|
||||
|
||||
#ifndef SABER
|
||||
#ifndef lint
|
||||
static const char rcsid_acl_files_c[] = "$Id$";
|
||||
#endif /* lint */
|
||||
#endif /* SABER */
|
||||
|
||||
/*** Routines for manipulating access control list files ***/
|
||||
|
||||
#define REALM_SEP '@'
|
||||
#define ESCAPE '\\'
|
||||
|
||||
#define CACHED_ACLS 256 /* How many acls to cache */
|
||||
#define ACL_LEN 256 /* Twice a reasonable acl length */
|
||||
|
||||
/* Eliminate all whitespace character in buf */
|
||||
/* Modifies its argument */
|
||||
static void
|
||||
nuke_whitespace(char *buf)
|
||||
{
|
||||
char *pin, *pout;
|
||||
|
||||
for (pin = pout = buf; *pin != '\0'; pin++)
|
||||
if (!isspace(*pin)) *pout++ = *pin;
|
||||
*pout = '\0'; /* Terminate the string */
|
||||
}
|
||||
|
||||
struct host_ace {
|
||||
struct host_ace *next;
|
||||
unsigned long addr, mask;
|
||||
};
|
||||
|
||||
static int
|
||||
add_host(struct host_ace **list,
|
||||
char *buf)
|
||||
{
|
||||
struct host_ace *e;
|
||||
struct in_addr addr;
|
||||
unsigned long mask = 0;
|
||||
long i;
|
||||
char *m, *x;
|
||||
|
||||
m = strchr(buf, '/');
|
||||
if (m) {
|
||||
*(m++) = 0;
|
||||
if (!*m)
|
||||
return EINVAL;
|
||||
i = strtol(m, &x, 10);
|
||||
if (*x || i < 0 || i > 32)
|
||||
return EINVAL;
|
||||
while (i--)
|
||||
mask = (mask >> 1) | 0x80000000;
|
||||
} else {
|
||||
mask = 0xffffffff;
|
||||
}
|
||||
|
||||
if (!inet_aton(buf, &addr))
|
||||
return EINVAL;
|
||||
|
||||
e = (struct host_ace *)malloc(sizeof(struct host_ace));
|
||||
if (e == NULL)
|
||||
return errno;
|
||||
memset(e, 0, sizeof(struct host_ace));
|
||||
e->addr = addr.s_addr;
|
||||
e->mask = htonl(mask);
|
||||
e->next = *list;
|
||||
*list = e;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_hosts(struct host_ace **list)
|
||||
{
|
||||
struct host_ace *e;
|
||||
|
||||
while ((e = *list)) {
|
||||
*list = e->next;
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
split_name(char *princ) {
|
||||
int i;
|
||||
int len = strlen(princ);
|
||||
|
||||
for (i = 0; i < len && princ[i] != REALM_SEP; i++)
|
||||
if (princ[i] == ESCAPE && (i + 1) < len)
|
||||
i++;
|
||||
if (i != len) { /* yay found an @ */
|
||||
princ[i] = 0;
|
||||
return &princ[i + 1];
|
||||
}
|
||||
return &princ[i]; /* failure, just return a pointer empty string */
|
||||
}
|
||||
|
||||
struct user_ace {
|
||||
struct user_ace *next;
|
||||
char *princ;
|
||||
char *realm;
|
||||
};
|
||||
|
||||
static int
|
||||
add_user(struct user_ace **list, char *princ) {
|
||||
struct user_ace *e;
|
||||
char *realm = split_name(princ);
|
||||
|
||||
e = (struct user_ace *)malloc(sizeof(struct user_ace));
|
||||
if (e == NULL)
|
||||
return errno;
|
||||
memset(e, 0, sizeof(struct user_ace));
|
||||
|
||||
if (!strcmp(princ, "*.*")) /* #ifdef KRB4_COMPAT */
|
||||
e->princ = strdup("*");
|
||||
else /* #endif */
|
||||
e->princ = strdup(princ);
|
||||
if (e->princ == NULL) {
|
||||
free(e);
|
||||
return errno;
|
||||
}
|
||||
|
||||
e->realm = strdup(realm);
|
||||
if (e->realm == NULL) {
|
||||
free(e->princ);
|
||||
free(e);
|
||||
return errno;
|
||||
}
|
||||
e->next = *list;
|
||||
*list = e;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_user(struct user_ace **list) {
|
||||
struct user_ace *e;
|
||||
while ((e = *list)) {
|
||||
*list = e->next;
|
||||
free(e->princ);
|
||||
free(e->realm);
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
check_user(struct user_ace *list, char *princ, char *realm) {
|
||||
struct user_ace *e;
|
||||
|
||||
e = list;
|
||||
while (e) {
|
||||
if (fnmatch(e->princ, princ, 0) == 0
|
||||
&& fnmatch(e->realm, realm, 0) == 0)
|
||||
return 1;
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct acl {
|
||||
String *filename; /* Name of acl file */
|
||||
int loaded;
|
||||
struct user_ace *acl; /* Positive acl entries */
|
||||
struct user_ace *negacl; /* Negative acl entries */
|
||||
struct host_ace *hosts; /* Positive host entries */
|
||||
struct host_ace *neghosts; /* Negative host entries */
|
||||
};
|
||||
|
||||
static struct acl acl_cache[CACHED_ACLS];
|
||||
|
||||
static int acl_cache_count = 0;
|
||||
static int acl_cache_next = 0;
|
||||
|
||||
/* wipe an entry in the acl cache */
|
||||
static void
|
||||
destroy_acl(int i) {
|
||||
if (acl_cache[i].filename)
|
||||
free_string(acl_cache[i].filename);
|
||||
if (acl_cache[i].acl)
|
||||
destroy_user(&acl_cache[i].acl);
|
||||
if (acl_cache[i].negacl)
|
||||
destroy_user(&acl_cache[i].negacl);
|
||||
if (acl_cache[i].hosts)
|
||||
destroy_hosts(&acl_cache[i].hosts);
|
||||
if (acl_cache[i].neghosts)
|
||||
destroy_hosts(&acl_cache[i].neghosts);
|
||||
acl_cache[i].filename = NULL;
|
||||
acl_cache[i].acl = (struct user_ace *) 0;
|
||||
acl_cache[i].negacl = (struct user_ace *) 0;
|
||||
acl_cache[i].hosts = (struct host_ace *) 0;
|
||||
acl_cache[i].neghosts = (struct host_ace *) 0;
|
||||
acl_cache[i].loaded = 0;
|
||||
}
|
||||
|
||||
/* Returns < 0 if unsuccessful in loading acl */
|
||||
/* Returns index into acl_cache otherwise */
|
||||
/* Note that if acl is already loaded, this is just a lookup */
|
||||
int acl_load(char *name)
|
||||
{
|
||||
int i;
|
||||
FILE *f;
|
||||
char buf[BUFSIZ];
|
||||
int ret = 0;
|
||||
String *interned_name;
|
||||
|
||||
syslog(LOG_DEBUG, "acl_load(%s)", name);
|
||||
/* See if it's there already */
|
||||
interned_name = make_string(name, 0);
|
||||
for (i = 0; i < acl_cache_count; i++) {
|
||||
if (acl_cache[i].filename == interned_name)
|
||||
goto got_it;
|
||||
}
|
||||
|
||||
/* It isn't, load it in */
|
||||
/* maybe there's still room */
|
||||
if (acl_cache_count < CACHED_ACLS) {
|
||||
i = acl_cache_count++;
|
||||
} else {
|
||||
/* No room, clean one out */
|
||||
i = acl_cache_next;
|
||||
acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
|
||||
destroy_acl(i);
|
||||
}
|
||||
|
||||
/* Set up the acl */
|
||||
acl_cache[i].filename = interned_name;
|
||||
/* Force reload */
|
||||
acl_cache[i].loaded = 0;
|
||||
|
||||
got_it:
|
||||
/*
|
||||
* See if we need to reload the ACL
|
||||
*/
|
||||
if (!acl_cache[i].loaded) {
|
||||
syslog(LOG_DEBUG, "acl_load(%s) actually loading", name);
|
||||
/* Gotta reload */
|
||||
if ((f = fopen(name, "r")) == NULL) {
|
||||
syslog(LOG_ERR, "Error loading acl file %s: %m", name);
|
||||
return -errno;
|
||||
}
|
||||
if (acl_cache[i].acl)
|
||||
destroy_user(&acl_cache[i].acl);
|
||||
if (acl_cache[i].negacl)
|
||||
destroy_user(&acl_cache[i].negacl);
|
||||
acl_cache[i].acl = (struct user_ace *)0;
|
||||
acl_cache[i].negacl = (struct user_ace *)0;
|
||||
while(fgets(buf, sizeof(buf), f) != NULL && ret == 0) {
|
||||
nuke_whitespace(buf);
|
||||
if (!buf[0])
|
||||
continue;
|
||||
if (buf[0] == '!' && buf[1] == '@')
|
||||
ret = add_host(&acl_cache[i].neghosts, buf + 2);
|
||||
else if (buf[0] == '@')
|
||||
ret = add_host(&acl_cache[i].hosts, buf + 1);
|
||||
else if (buf[0] == '!')
|
||||
ret = add_user(&acl_cache[i].negacl, buf + 1);
|
||||
else
|
||||
ret = add_user(&acl_cache[i].acl, buf);
|
||||
}
|
||||
fclose(f);
|
||||
if (ret) {
|
||||
destroy_acl(i);
|
||||
return -ret;
|
||||
}
|
||||
acl_cache[i].loaded = 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* This destroys all cached ACL's so that new ones will be loaded in
|
||||
* the next time they are requested.
|
||||
*/
|
||||
void
|
||||
acl_cache_reset(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
syslog(LOG_DEBUG, "acl_cache_reset()");
|
||||
for (i = 0; i < acl_cache_count; i++)
|
||||
destroy_acl(i);
|
||||
acl_cache_count = 0;
|
||||
acl_cache_next = 0;
|
||||
}
|
||||
|
||||
/* Returns nonzero if it can be determined that acl contains principal */
|
||||
/* Principal is not canonicalized, and no wildcarding is done */
|
||||
/* If neg is nonzero, we look for negative entries */
|
||||
static int
|
||||
acl_match(char *acl, char *princ, char *realm, int neg)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if ((idx = acl_load(acl)) < 0)
|
||||
return 0;
|
||||
if (neg)
|
||||
return check_user(acl_cache[idx].negacl, princ, realm);
|
||||
else
|
||||
return check_user(acl_cache[idx].acl, princ, realm);
|
||||
}
|
||||
|
||||
/* Returns nonzero if it can be determined that acl contains who */
|
||||
/* If neg is nonzero, we look for negative entries */
|
||||
static int
|
||||
acl_host_match(char *acl,
|
||||
unsigned long who,
|
||||
int neg)
|
||||
{
|
||||
int idx;
|
||||
struct host_ace *e;
|
||||
|
||||
if ((idx = acl_load(acl)) < 0)
|
||||
return 0;
|
||||
e = neg ? acl_cache[idx].neghosts : acl_cache[idx].hosts;
|
||||
while (e) {
|
||||
if ((e->addr & e->mask) == (who & e->mask))
|
||||
return 1;
|
||||
e = e->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns nonzero if it can be determined that acl contains principal */
|
||||
/* Recognizes wildcards in acl. */
|
||||
/* Also checks for IP address entries and applies negative ACL's */
|
||||
int
|
||||
acl_check(char *acl, char *princ, struct sockaddr_in *who)
|
||||
{
|
||||
char *realm;
|
||||
char *name;
|
||||
int result = 0;
|
||||
|
||||
if (princ) {
|
||||
name = strdup(princ);
|
||||
realm = split_name(name);
|
||||
|
||||
if (acl_match(acl, name, realm, 1))
|
||||
return 0;
|
||||
if (acl_match(acl, name, realm, 0))
|
||||
result = 1;
|
||||
free(name);
|
||||
}
|
||||
|
||||
if (who) {
|
||||
if (acl_host_match(acl, who->sin_addr.s_addr, 1))
|
||||
return 0;
|
||||
if (acl_host_match(acl, who->sin_addr.s_addr, 0))
|
||||
result = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
1702
server/bdump.c
Normal file
1702
server/bdump.c
Normal file
File diff suppressed because it is too large
Load Diff
392
server/class.c
Normal file
392
server/class.c
Normal file
@ -0,0 +1,392 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for the Zephyr server class manager subsystem.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Source$
|
||||
* $Author$
|
||||
*
|
||||
* Copyright (c) 1987 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h" /* includes zephyr/zephyr.h */
|
||||
#include <assert.h>
|
||||
|
||||
#if !defined (lint) && !defined (SABER)
|
||||
static const char rcsid_class_c[] =
|
||||
"$Id$";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Class manager subsystem.
|
||||
*
|
||||
*
|
||||
* External functions are:
|
||||
*
|
||||
* Code_t triplet_register(client, subs, realm)
|
||||
*
|
||||
* Code_t triplet_deregister(client, subs, realm)
|
||||
*
|
||||
* Client *triplet_lookup(subs)
|
||||
* Client *client;
|
||||
* Destlist *subs;
|
||||
*
|
||||
* Acl *class_get_acl(class_name)
|
||||
* String *class_name;
|
||||
*
|
||||
* Code_t class_restrict(class_name, acl)
|
||||
* char *class_name;
|
||||
* Acl *acl;
|
||||
*
|
||||
* Code_t class_setup_restricted(class_name, acl)
|
||||
* char *class_name;
|
||||
* Acl *acl;
|
||||
*
|
||||
* and several Destination methods.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The data structure used for the class manager is an array of hash buckets
|
||||
* each containing a pointer to a doubly linked circular list (in the style
|
||||
* of insque/remque). Each element of this list contains a class.instance
|
||||
* name (which hashes into the bucket associated with this list) and a
|
||||
* doubly linked list of clients which are interested in this class.
|
||||
* The data pointed to by these clients is owned by other modules. Care
|
||||
* must be taken by the caller not to a free()'d client
|
||||
* structure.
|
||||
*
|
||||
* If any hash bucket is empty, the pointer is null.
|
||||
*
|
||||
* The first element in the hash bucket is a special header unused for
|
||||
* storing classes, and is used for finding the end of the list.
|
||||
*
|
||||
* If any list of interested clients is empty, the class name is garbage
|
||||
* collected, unless the class has been registered as restricted.
|
||||
*/
|
||||
|
||||
/* Private variables */
|
||||
#define EMPTY_CLASS 2000
|
||||
|
||||
#define ALLOC_OFFSET 8 /* Allocate 32 bytes less than a power of 2. */
|
||||
#define ALLOC_INIT 8 /* Initial number of subscriptions. */
|
||||
|
||||
#define HASHSIZE 1023
|
||||
#define HASHVAL(c, i, r) (((c)->hash_val ^ (i)->hash_val ^ (r)->hash_val) \
|
||||
% HASHSIZE)
|
||||
#define DEST_HASHVAL(dest) HASHVAL((dest).classname, (dest).inst, (dest).recip)
|
||||
|
||||
static Triplet *triplet_bucket[HASHSIZE]; /* the hash table of pointers */
|
||||
|
||||
static Code_t remove_client(Triplet *triplet, Client *client, ZRealm *realm);
|
||||
static Code_t insert_client(Triplet *triplet, Client *client, ZRealm *realm);
|
||||
static Triplet *triplet_alloc(String *classname, String *inst,
|
||||
String *recipient);
|
||||
static void free_triplet(Triplet *);
|
||||
|
||||
/* public routines */
|
||||
|
||||
/*
|
||||
* Determine if two destination triplets are equal. Note the backup
|
||||
* case-insensitive recipient check in the third term. Recipients are
|
||||
* not downcased at subscription time (in order to preserve case for,
|
||||
* say, "zctl ret"), but traditional zephyr server behavior has not
|
||||
* been case-sensitive in the recipient string. In most cases, a
|
||||
* failed match will fail on the classname or instance, and a successful
|
||||
* match will succeed on the (d1->recip == d2->recip) check, so this
|
||||
* shouldn't affect performance.
|
||||
*/
|
||||
|
||||
int
|
||||
ZDest_eq(Destination *d1,
|
||||
Destination *d2)
|
||||
{
|
||||
return((d1->classname == d2->classname) &&
|
||||
(d1->inst == d2->inst) &&
|
||||
(d1->recip == d2->recip ||
|
||||
strcasecmp(d1->recip->string, d2->recip->string) == 0));
|
||||
}
|
||||
|
||||
|
||||
/* the client as interested in a triplet */
|
||||
|
||||
Code_t
|
||||
triplet_register(Client *client,
|
||||
Destination *dest,
|
||||
ZRealm *realm)
|
||||
{
|
||||
Triplet *triplet;
|
||||
unsigned long hashval;
|
||||
|
||||
hashval = DEST_HASHVAL(*dest);
|
||||
for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
|
||||
if (ZDest_eq(&triplet->dest, dest))
|
||||
return insert_client(triplet, client, realm);
|
||||
}
|
||||
|
||||
/* Triplet not present in hash table, insert it. */
|
||||
triplet = triplet_alloc(dest->classname, dest->inst, dest->recip);
|
||||
Triplet_insert(&triplet_bucket[hashval], triplet);
|
||||
return insert_client(triplet, client, realm);
|
||||
}
|
||||
|
||||
/* dissociate client from the class, garbage collecting if appropriate */
|
||||
|
||||
Code_t
|
||||
triplet_deregister(Client *client,
|
||||
Destination *dest,
|
||||
ZRealm *realm)
|
||||
{
|
||||
Triplet *triplet;
|
||||
int retval;
|
||||
unsigned long hashval;
|
||||
|
||||
hashval = DEST_HASHVAL(*dest);
|
||||
for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
|
||||
if (ZDest_eq(&triplet->dest, dest)) {
|
||||
retval = remove_client(triplet, client, realm);
|
||||
if (retval != ZERR_NONE)
|
||||
return retval;
|
||||
if (*triplet->clients == NULL && !triplet->acl) {
|
||||
Triplet_delete(triplet);
|
||||
free_triplet(triplet);
|
||||
return ZSRV_EMPTYCLASS;
|
||||
}
|
||||
return ZERR_NONE;
|
||||
}
|
||||
}
|
||||
return(ZSRV_BADASSOC);
|
||||
}
|
||||
|
||||
/* return a linked list of what clients are interested in this triplet */
|
||||
|
||||
Client **
|
||||
triplet_lookup(Destination *dest)
|
||||
{
|
||||
Triplet *triplet;
|
||||
unsigned long hashval;
|
||||
|
||||
hashval = DEST_HASHVAL(*dest);
|
||||
for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
|
||||
if (ZDest_eq(&triplet->dest, dest))
|
||||
return triplet->clients;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the acl structure associated with class, or NULL if there is
|
||||
* no such acl struct
|
||||
*/
|
||||
|
||||
Acl *
|
||||
class_get_acl(String *class_name)
|
||||
{
|
||||
Triplet *triplet;
|
||||
unsigned long hashval;
|
||||
|
||||
hashval = HASHVAL(class_name, empty, empty);
|
||||
for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
|
||||
if (triplet->dest.classname == class_name &&
|
||||
triplet->dest.inst == empty && triplet->dest.recip == empty)
|
||||
return triplet->acl;
|
||||
}
|
||||
|
||||
/* No acl found, not restricted. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* restrict class by associating it with the acl structure acl.
|
||||
* return ZERR_NONE if no error, or ZSRV_NOCLASS if there is no such
|
||||
* class, or ZSRV_CLASSRESTRICTED if it is already restricted.
|
||||
*/
|
||||
|
||||
Code_t
|
||||
class_restrict(char *class_name,
|
||||
Acl *acl)
|
||||
{
|
||||
Triplet *triplet;
|
||||
String *d;
|
||||
unsigned long hashval;
|
||||
|
||||
d = make_string(class_name,1);
|
||||
hashval = HASHVAL(d, empty, empty);
|
||||
for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
|
||||
if (triplet->dest.classname == d && triplet->dest.inst == empty &&
|
||||
triplet->dest.recip == empty) {
|
||||
if (triplet->acl)
|
||||
return ZSRV_CLASSRESTRICTED;
|
||||
triplet->acl = acl;
|
||||
free_string(d);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
free_string(d);
|
||||
return ZSRV_NOCLASS;
|
||||
}
|
||||
|
||||
/*
|
||||
* restrict class by registering it and associating it with the acl
|
||||
* structure acl. return ZERR_NONE if no error, or ZSRV_CLASSXISTS
|
||||
* if the class is already registered, or ENOMEM in case of malloc failure.
|
||||
*/
|
||||
|
||||
Code_t
|
||||
class_setup_restricted(char *class_name,
|
||||
Acl *acl)
|
||||
{
|
||||
Triplet *triplet;
|
||||
String *d;
|
||||
unsigned long hashval;
|
||||
|
||||
d = make_string(class_name,1);
|
||||
hashval = HASHVAL(d, empty, empty);
|
||||
for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
|
||||
if (triplet->dest.classname == d && triplet->dest.inst == empty &&
|
||||
triplet->dest.recip == d) {
|
||||
free_string(d);
|
||||
return ZSRV_CLASSXISTS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Triplet not present in hash table, insert it. */
|
||||
triplet = triplet_alloc(d, empty, empty);
|
||||
free_string(d);
|
||||
if (!triplet)
|
||||
return ENOMEM;
|
||||
triplet->acl = acl;
|
||||
Triplet_insert(&triplet_bucket[hashval], triplet);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
|
||||
/* private routines */
|
||||
|
||||
/* allocate space for a class structure */
|
||||
|
||||
static Triplet *
|
||||
triplet_alloc(String *classname,
|
||||
String *inst,
|
||||
String *recipient)
|
||||
{
|
||||
Triplet *triplet;
|
||||
|
||||
triplet = (Triplet *) malloc(sizeof(Triplet));
|
||||
if (!triplet)
|
||||
return NULL;
|
||||
|
||||
triplet->dest.classname = dup_string(classname);
|
||||
triplet->dest.inst = dup_string(inst);
|
||||
triplet->dest.recip = dup_string(recipient);
|
||||
triplet->clients = NULL;
|
||||
triplet->acl = NULL;
|
||||
|
||||
return triplet;
|
||||
}
|
||||
|
||||
/* insert a client into the list associated with the class *ptr */
|
||||
|
||||
static Code_t
|
||||
insert_client(Triplet *triplet,
|
||||
Client *client,
|
||||
ZRealm *realm)
|
||||
{
|
||||
Client **clientp, **newclients;
|
||||
int new_size;
|
||||
|
||||
if (triplet->clients) {
|
||||
/* Avoid duplication. */
|
||||
for (clientp = triplet->clients; *clientp; clientp++) {
|
||||
if (*clientp == client || (realm && (*clientp)->realm == realm))
|
||||
return ZSRV_CLASSXISTS;
|
||||
}
|
||||
|
||||
if (clientp + 1 - triplet->clients >= triplet->clients_size) {
|
||||
new_size = triplet->clients_size * 2 + ALLOC_OFFSET;
|
||||
newclients = (Client **) realloc(triplet->clients,
|
||||
new_size * sizeof(Client *));
|
||||
if (newclients == NULL)
|
||||
return ENOMEM;
|
||||
clientp = newclients + (clientp - triplet->clients);
|
||||
triplet->clients = newclients;
|
||||
triplet->clients_size = new_size;
|
||||
}
|
||||
} else {
|
||||
/* Allocate an initial list of client pointers. */
|
||||
triplet->clients = (Client **) malloc(ALLOC_INIT * sizeof(Client *));
|
||||
if (triplet->clients == NULL)
|
||||
return ENOMEM;
|
||||
triplet->clients_size = ALLOC_INIT;
|
||||
clientp = triplet->clients;
|
||||
}
|
||||
|
||||
*clientp = client;
|
||||
clientp[1] = NULL;
|
||||
return ZERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove the client from the list associated with class *ptr, garbage
|
||||
* collecting if appropriate
|
||||
*/
|
||||
|
||||
static Code_t
|
||||
remove_client(Triplet *triplet,
|
||||
Client *client,
|
||||
ZRealm *realm)
|
||||
{
|
||||
Client **clientp;
|
||||
|
||||
for (clientp = triplet->clients; *clientp; clientp++) {
|
||||
if (*clientp == client || (realm && (*clientp)->realm == realm)) {
|
||||
for (; *clientp; clientp++)
|
||||
*clientp = clientp[1];
|
||||
return ZERR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return ZSRV_BADASSOC;
|
||||
}
|
||||
|
||||
static void
|
||||
free_triplet(Triplet *triplet)
|
||||
{
|
||||
if (triplet->clients)
|
||||
free(triplet->clients);
|
||||
free_string(triplet->dest.classname);
|
||||
free_string(triplet->dest.inst);
|
||||
free_string(triplet->dest.recip);
|
||||
free(triplet);
|
||||
}
|
||||
|
||||
void
|
||||
triplet_dump_subs(FILE *fp)
|
||||
{
|
||||
int i;
|
||||
Triplet *triplet;
|
||||
Client **clientp;
|
||||
|
||||
for (i = 0; i < HASHSIZE; i++) {
|
||||
for (triplet = triplet_bucket[i]; triplet; triplet = triplet->next) {
|
||||
fputs("Triplet '", fp);
|
||||
dump_quote(triplet->dest.classname->string, fp);
|
||||
fputs("' '", fp);
|
||||
dump_quote(triplet->dest.inst->string, fp);
|
||||
fputs("' '", fp);
|
||||
dump_quote(triplet->dest.recip->string, fp);
|
||||
fputs("':\n", fp);
|
||||
if (triplet->clients) {
|
||||
for (clientp = triplet->clients; *clientp; clientp++) {
|
||||
fprintf(fp, "\t%s %d (%s)\n",
|
||||
inet_ntoa((*clientp)->addr.sin_addr),
|
||||
ntohs((*clientp)->addr.sin_port),
|
||||
(*clientp)->principal->string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
234
server/client.c
Normal file
234
server/client.c
Normal file
@ -0,0 +1,234 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for the Client Manager subsystem of the Zephyr server.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#if !defined (lint) && !defined (SABER)
|
||||
static const char rcsid_client_c[] =
|
||||
"$Id$";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* External functions:
|
||||
*
|
||||
* Code_t client_register(notice, who, client, server, wantdefaults)
|
||||
* ZNotice_t *notice;
|
||||
* struct sockaddr_in *who;
|
||||
* Client **client; (RETURN)
|
||||
* Server *server;
|
||||
* int wantdefaults;
|
||||
*
|
||||
* Code_t client_deregister(client, host, flush)
|
||||
* Client *client;
|
||||
* Host *host;
|
||||
* int flush;
|
||||
*
|
||||
* Client *client_find(who, unsigned int port)
|
||||
* struct in_addr *host;
|
||||
* unsigned int port;
|
||||
*
|
||||
* void client_dump_clients(fp, clist)
|
||||
* FILE *fp;
|
||||
* Client *clist;
|
||||
*/
|
||||
|
||||
/*
|
||||
* a client: allocate space, find or insert the address in the
|
||||
* server's list of hosts, initialize and insert the client into
|
||||
* the host's list of clients.
|
||||
*
|
||||
* This routine assumes that the client has not been registered yet.
|
||||
* The caller should check by calling client_find.
|
||||
*/
|
||||
|
||||
#define HASHSIZE 1024
|
||||
static Client *client_bucket[HASHSIZE];
|
||||
|
||||
#define INET_HASH(host, port) ((htonl((host)->s_addr) + \
|
||||
htons((unsigned short) (port))) % HASHSIZE)
|
||||
|
||||
Code_t
|
||||
client_register(ZNotice_t *notice,
|
||||
struct in_addr *host,
|
||||
Client **client_p,
|
||||
int wantdefaults)
|
||||
{
|
||||
Client *client;
|
||||
|
||||
/* chain the client's host onto this server's host list */
|
||||
|
||||
zdbug((LOG_DEBUG, "client_register: adding %s at %s/%d",
|
||||
notice->z_sender, inet_ntoa(*host), ntohs(notice->z_port)));
|
||||
|
||||
if (!notice->z_port)
|
||||
return ZSRV_BADSUBPORT;
|
||||
|
||||
*client_p = client = client_find(host, notice->z_port);
|
||||
if (!client) {
|
||||
*client_p = client = (Client *) malloc(sizeof(Client));
|
||||
if (!client)
|
||||
return ENOMEM;
|
||||
memset(&client->addr, 0, sizeof(struct sockaddr_in));
|
||||
#ifdef HAVE_KRB5
|
||||
client->session_keyblock = NULL;
|
||||
#else
|
||||
#ifdef HAVE_KRB4
|
||||
memset(&client->session_key, 0, sizeof(client->session_key));
|
||||
#endif
|
||||
#endif
|
||||
client->last_send = 0;
|
||||
client->last_ack = NOW;
|
||||
client->addr.sin_family = AF_INET;
|
||||
client->addr.sin_addr.s_addr = host->s_addr;
|
||||
client->addr.sin_port = notice->z_port;
|
||||
client->subs = NULL;
|
||||
client->realm = NULL;
|
||||
client->principal = make_string(notice->z_sender, 0);
|
||||
Client_insert(&client_bucket[INET_HASH(&client->addr.sin_addr,
|
||||
notice->z_port)], client);
|
||||
}
|
||||
|
||||
/* Add default subscriptions only if this is not resulting from a brain
|
||||
* dump, AND this request wants defaults. */
|
||||
if (!bdumping && wantdefaults)
|
||||
return subscr_def_subs(client);
|
||||
else
|
||||
return ZERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deregister the client, freeing resources.
|
||||
* Remove any packets in the nack queue, release subscriptions, release
|
||||
* locations, and dequeue him from the host.
|
||||
*/
|
||||
|
||||
void
|
||||
client_deregister(Client *client,
|
||||
int flush)
|
||||
{
|
||||
Client_delete(client);
|
||||
nack_release(client);
|
||||
subscr_cancel_client(client);
|
||||
free_string(client->principal);
|
||||
#ifdef HAVE_KRB5
|
||||
if (client->session_keyblock)
|
||||
krb5_free_keyblock(Z_krb5_ctx, client->session_keyblock);
|
||||
#endif
|
||||
if (flush)
|
||||
uloc_flush_client(&client->addr);
|
||||
free(client);
|
||||
}
|
||||
|
||||
void
|
||||
client_flush_host(struct in_addr *host)
|
||||
{
|
||||
int i;
|
||||
Client *client, *next;
|
||||
|
||||
for (i = 0; i < HASHSIZE; i++) {
|
||||
for (client = client_bucket[i]; client; client = next) {
|
||||
next = client->next;
|
||||
if (client->addr.sin_addr.s_addr == host->s_addr)
|
||||
client_deregister(client, 1);
|
||||
}
|
||||
}
|
||||
uloc_hflush(host);
|
||||
}
|
||||
|
||||
/* Unlike client_flush_host, this flushes only subs, not locations */
|
||||
void
|
||||
client_flush_princ(char *target)
|
||||
{
|
||||
int i;
|
||||
Client *client, *next;
|
||||
String *principal = make_string(target, 0);
|
||||
|
||||
for (i = 0; i < HASHSIZE; i++) {
|
||||
for (client = client_bucket[i]; client; client = next) {
|
||||
next = client->next;
|
||||
if (client->principal == principal)
|
||||
client_deregister(client, 0);
|
||||
}
|
||||
}
|
||||
free_string(principal);
|
||||
}
|
||||
|
||||
Code_t
|
||||
client_send_clients(void)
|
||||
{
|
||||
int i;
|
||||
Client *client;
|
||||
Code_t retval;
|
||||
|
||||
for (i = 0; i < HASHSIZE; i++) {
|
||||
/* Allow packets to be processed between rows of the hash table. */
|
||||
if (packets_waiting()) {
|
||||
bdumping = 0;
|
||||
bdump_concurrent = 1;
|
||||
handle_packet();
|
||||
bdump_concurrent = 0;
|
||||
bdumping = 1;
|
||||
}
|
||||
for (client = client_bucket[i]; client; client = client->next) {
|
||||
if (client->subs) {
|
||||
retval = subscr_send_subs(client);
|
||||
if (retval != ZERR_NONE)
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ZERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* dump info about clients in this clist onto the fp.
|
||||
* assumed to be called with SIGFPE blocked
|
||||
* (true if called from signal handler)
|
||||
*/
|
||||
|
||||
void
|
||||
client_dump_clients(FILE *fp)
|
||||
{
|
||||
Client *client;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HASHSIZE; i++) {
|
||||
for (client = client_bucket[i]; client; client = client->next) {
|
||||
fprintf(fp, "%s/%d (%s):\n", inet_ntoa(client->addr.sin_addr),
|
||||
ntohs(client->addr.sin_port), client->principal->string);
|
||||
subscr_dump_subs(fp, client->subs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* find a client by host and port
|
||||
*/
|
||||
|
||||
Client *
|
||||
client_find(struct in_addr *host,
|
||||
unsigned int port)
|
||||
{
|
||||
Client *client;
|
||||
long hashval;
|
||||
|
||||
hashval = INET_HASH(host, port);
|
||||
for (client = client_bucket[hashval]; client; client = client->next) {
|
||||
if (client->addr.sin_addr.s_addr == host->s_addr
|
||||
&& client->addr.sin_port == port)
|
||||
return client;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
130
server/common.c
Normal file
130
server/common.c
Normal file
@ -0,0 +1,130 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for general use within the Zephyr server.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1987 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
|
||||
#ifndef lint
|
||||
#ifndef SABER
|
||||
static const char rcsid_common_c[] =
|
||||
"$Id$";
|
||||
#endif /* SABER */
|
||||
#endif /* lint */
|
||||
|
||||
/* common routines for the server */
|
||||
|
||||
/* copy a string into a newly allocated area */
|
||||
|
||||
char *
|
||||
strsave (const char *sp)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
ret = strdup(sp);
|
||||
if (!ret) {
|
||||
syslog(LOG_CRIT, "no mem strdup'ing");
|
||||
abort();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The "& 0x5f" provides case-insensitivity for ASCII. */
|
||||
|
||||
unsigned long
|
||||
hash(const char *string)
|
||||
{
|
||||
unsigned long hval = 0;
|
||||
char cp;
|
||||
|
||||
while (1) {
|
||||
cp = *string++;
|
||||
if (!cp)
|
||||
break;
|
||||
hval += cp & 0x5f;
|
||||
|
||||
cp = *string++;
|
||||
if (!cp)
|
||||
break;
|
||||
hval += (cp & 0x5f) * (3 + (1 << 16));
|
||||
|
||||
cp = *string++;
|
||||
if (!cp)
|
||||
break;
|
||||
hval += (cp & 0x5f) * (1 + (1 << 8));
|
||||
|
||||
cp = *string++;
|
||||
if (!cp)
|
||||
break;
|
||||
hval += (cp & 0x5f) * (1 + (1 << 12));
|
||||
|
||||
cp = *string++;
|
||||
if (!cp)
|
||||
break;
|
||||
hval += (cp & 0x5f) * (1 + (1 << 4));
|
||||
|
||||
hval += ((long) hval) >> 18;
|
||||
}
|
||||
|
||||
hval &= 0x7fffffff;
|
||||
return hval;
|
||||
}
|
||||
|
||||
/* Output a name, replacing newlines with \n and single quotes with \q. */
|
||||
void
|
||||
dump_quote(char *p, FILE *fp)
|
||||
{
|
||||
for (; *p; p++) {
|
||||
if (*p == '\'') {
|
||||
putc('\\', fp);
|
||||
putc('q', fp);
|
||||
} else if (*p == '\n') {
|
||||
putc('\\', fp);
|
||||
putc('n', fp);
|
||||
} else {
|
||||
putc(*p, fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pull the address out of the packet for dispatching. Doesn't do anything
|
||||
* special, and will need to change signatures when ipv6 support happens. But
|
||||
* it'll be in one place....
|
||||
*/
|
||||
void
|
||||
notice_extract_address(ZNotice_t *notice, struct sockaddr_in *addr)
|
||||
{
|
||||
/*
|
||||
* We get the address out of the uid rather than the
|
||||
* Hopefully by the time a server will actually be speaking ipv6, it won't have
|
||||
* to worry about talking to other <3.0 realms
|
||||
*/
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
addr->sin_addr.s_addr = notice->z_uid.zuid_addr.s_addr;
|
||||
addr->sin_port = notice->z_port;
|
||||
addr->sin_family = AF_INET;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
packets_waiting(void)
|
||||
{
|
||||
fd_set readable, initial;
|
||||
struct timeval tv;
|
||||
|
||||
if (msgs_queued())
|
||||
return 1;
|
||||
FD_ZERO(&initial);
|
||||
FD_SET(srv_socket, &initial);
|
||||
readable = initial;
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
return (select(srv_socket + 1, &readable, NULL, NULL, &tv) > 0);
|
||||
}
|
3
server/default.subscriptions
Normal file
3
server/default.subscriptions
Normal file
@ -0,0 +1,3 @@
|
||||
operations,message,*
|
||||
message,personal,%me%
|
||||
message,urgent,%me%
|
1239
server/dispatch.c
Normal file
1239
server/dispatch.c
Normal file
File diff suppressed because it is too large
Load Diff
75
server/global.c
Normal file
75
server/global.c
Normal file
@ -0,0 +1,75 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains the global variables used by the server. (moved from main.c)
|
||||
*
|
||||
* Created by: Karl Ramm
|
||||
*
|
||||
* Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
int nfds; /* max file descriptor for select() */
|
||||
int srv_socket; /* dgram socket for clients
|
||||
and other servers */
|
||||
int bdump_socket = -1; /* brain dump socket fd
|
||||
(closed most of the time) */
|
||||
#ifdef HAVE_ARES
|
||||
ares_channel achannel; /* C-ARES resolver channel */
|
||||
#endif
|
||||
fd_set interesting; /* the file descrips we are listening
|
||||
to right now */
|
||||
struct sockaddr_in srv_addr; /* address of the socket */
|
||||
|
||||
Unacked *nacklist = NULL; /* list of packets waiting for ack's */
|
||||
|
||||
unsigned short hm_port; /* host manager receiver port */
|
||||
unsigned short hm_srv_port; /* host manager server sending port */
|
||||
|
||||
char myname[NS_MAXDNAME]; /* my host name */
|
||||
|
||||
char list_file[128];
|
||||
#ifdef HAVE_KRB5
|
||||
char keytab_file[128];
|
||||
#endif
|
||||
#ifdef HAVE_KRB4
|
||||
char srvtab_file[128];
|
||||
#endif
|
||||
char acl_dir[128];
|
||||
char subs_file[128];
|
||||
|
||||
int zdebug;
|
||||
#ifdef DEBUG
|
||||
int zalone;
|
||||
#endif
|
||||
|
||||
struct timeval t_local; /* store current time for other uses */
|
||||
|
||||
|
||||
u_long npackets; /* number of packets processed */
|
||||
time_t uptime; /* when we started operations */
|
||||
struct in_addr my_addr;
|
||||
char *bdump_version = "1.2";
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
int bdump_auth_proto = 5;
|
||||
#else /* HAVE_KRB5 */
|
||||
#ifdef HAVE_KRB4
|
||||
int bdump_auth_proto = 4;
|
||||
#else /* HAVE_KRB4 */
|
||||
int bdump_auth_proto = 0;
|
||||
#endif /* HAVE_KRB4 */
|
||||
#endif /* HAVE_KRB5 */
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
krb5_ccache Z_krb5_ccache;
|
||||
krb5_keyblock *__Zephyr_keyblock;
|
||||
#else
|
||||
#ifdef HAVE_KRB4
|
||||
C_Block __Zephyr_session;
|
||||
#endif
|
||||
#endif
|
755
server/kstuff.c
Normal file
755
server/kstuff.c
Normal file
@ -0,0 +1,755 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for dealing with Kerberos functions in the server.
|
||||
*
|
||||
* Created by: John T Kohl
|
||||
*
|
||||
* Copyright (c) 1988 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
/*
|
||||
* $Source$
|
||||
* $Header$
|
||||
*/
|
||||
|
||||
#include "zserver.h"
|
||||
|
||||
#ifndef lint
|
||||
#ifndef SABER
|
||||
static const char rcsid_kstuff_c[] = "$Id$";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB4) && defined(HAVE_KRB5)
|
||||
static Code_t ZCheckAuthentication4(ZNotice_t *notice, struct sockaddr_in *from);
|
||||
#endif
|
||||
#ifdef HAVE_KRB5
|
||||
static ZChecksum_t compute_checksum(ZNotice_t *, unsigned char *);
|
||||
static ZChecksum_t compute_rlm_checksum(ZNotice_t *, unsigned char *);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KRB4
|
||||
/*
|
||||
* GetKerberosData
|
||||
*
|
||||
* get ticket from file descriptor and decode it.
|
||||
* Return KFAILURE if we barf on reading the ticket, else return
|
||||
* the value of rd_ap_req() applied to the ticket.
|
||||
*/
|
||||
int
|
||||
GetKerberosData(int fd, /* file descr. to read from */
|
||||
struct in_addr haddr, /* address of foreign host on fd */
|
||||
AUTH_DAT *kdata, /* kerberos data (returned) */
|
||||
char *service, /* service principal desired */
|
||||
char *srvtab) /* file to get keys from */
|
||||
{
|
||||
char p[20];
|
||||
KTEXT_ST ticket; /* will get Kerberos ticket from client */
|
||||
unsigned int i;
|
||||
char instance[INST_SZ];
|
||||
|
||||
/*
|
||||
* Get the Kerberos ticket. The first few characters, terminated
|
||||
* by a blank, should give us a length; then get than many chars
|
||||
* which will be the ticket proper.
|
||||
*/
|
||||
for (i=0; i<20; i++) {
|
||||
if (read(fd, &p[i], 1) != 1) {
|
||||
syslog(LOG_WARNING,"bad read tkt len");
|
||||
return(KFAILURE);
|
||||
}
|
||||
if (p[i] == ' ') {
|
||||
p[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
ticket.length = atoi(p);
|
||||
if ((i==20) || (ticket.length<=0) || (ticket.length>MAX_KTXT_LEN)) {
|
||||
syslog(LOG_WARNING,"bad tkt len %d",ticket.length);
|
||||
return(KFAILURE);
|
||||
}
|
||||
for (i=0; i<ticket.length; i++) {
|
||||
if (read(fd, &ticket.dat[i], 1) != 1) {
|
||||
syslog(LOG_WARNING,"bad tkt read");
|
||||
return(KFAILURE);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* now have the ticket. use it to get the authenticated
|
||||
* data from Kerberos.
|
||||
*/
|
||||
(void) strcpy(instance,"*"); /* let Kerberos fill it in */
|
||||
|
||||
return(krb_rd_req(&ticket, service, instance, haddr.s_addr,
|
||||
kdata, srvtab ? srvtab : ""));
|
||||
}
|
||||
|
||||
/*
|
||||
* SendKerberosData
|
||||
*
|
||||
* create and transmit a ticket over the file descriptor for service.host
|
||||
* return failure codes if appropriate, or 0 if we
|
||||
* get the ticket and write it to the file descriptor
|
||||
*/
|
||||
|
||||
#if !defined(krb_err_base) && defined(ERROR_TABLE_BASE_krb)
|
||||
#define krb_err_base ERROR_TABLE_BASE_krb
|
||||
#endif
|
||||
|
||||
Code_t
|
||||
SendKerberosData(int fd, /* file descriptor to write onto */
|
||||
KTEXT ticket, /* where to put ticket (return) */
|
||||
char *service, /* service name, foreign host */
|
||||
char *host)
|
||||
|
||||
{
|
||||
int rem;
|
||||
char p[32];
|
||||
size_t written;
|
||||
size_t size_to_write;
|
||||
|
||||
rem = krb_mk_req(ticket, service, host, (char *)ZGetRealm(), (u_long) 0);
|
||||
if (rem != KSUCCESS)
|
||||
return rem + krb_err_base;
|
||||
|
||||
(void) sprintf(p,"%d ",ticket->length);
|
||||
size_to_write = strlen (p);
|
||||
if ((written = write(fd, p, size_to_write)) != size_to_write)
|
||||
return ((ssize_t)written < 0) ? errno : ZSRV_PKSHORT;
|
||||
if ((written = write(fd, ticket->dat, ticket->length))
|
||||
!= ticket->length)
|
||||
return ((ssize_t)written < 0) ? errno : ZSRV_PKSHORT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_KRB4 */
|
||||
|
||||
#if defined(HAVE_KRB5) || defined(HAVE_KRB4)
|
||||
Code_t
|
||||
ReadKerberosData(int fd, int *size, char **data, int *proto) {
|
||||
char p[20];
|
||||
int i;
|
||||
char *dst;
|
||||
int len = 0;
|
||||
|
||||
for (i=0; i<20; i++) {
|
||||
if (read(fd, &p[i], 1) != 1) {
|
||||
p[i] = 0;
|
||||
syslog(LOG_WARNING,"ReadKerberosData: bad read reply len @%d (got \"%s\"", i, p);
|
||||
return ZSRV_LEN;
|
||||
}
|
||||
if (p[i] == ' ') {
|
||||
p[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 20) {
|
||||
syslog(LOG_WARNING, "ReadKerberosData: read reply len exceeds buffer");
|
||||
return ZSRV_BUFSHORT;
|
||||
}
|
||||
|
||||
if (!strncmp(p, "V5-", 3) && (len = atoi(p+3)) > 0)
|
||||
*proto = 5;
|
||||
else if ((len = atoi(p)) > 0)
|
||||
*proto = 4;
|
||||
|
||||
if ((*proto < 4) | (*proto > 5)) {
|
||||
syslog(LOG_WARNING, "ReadKerberosData: error parsing authenticator length (\"%s\")", p);
|
||||
return ZSRV_LEN;
|
||||
}
|
||||
|
||||
if (len <= 0) {
|
||||
syslog(LOG_WARNING, "ReadKerberosData: read reply len = %d", len);
|
||||
return ZSRV_LEN;
|
||||
}
|
||||
|
||||
*data = malloc(len);
|
||||
if (! *data) {
|
||||
syslog(LOG_WARNING, "ReadKerberosData: failure allocating %d bytes: %m", len);
|
||||
return errno;
|
||||
}
|
||||
|
||||
dst=*data;
|
||||
for (i=0; i < len; i++) {
|
||||
if (read(fd, dst++, 1) != 1) {
|
||||
free(*data);
|
||||
*data = NULL;
|
||||
*size = 0;
|
||||
syslog(LOG_WARNING,"ReadKerberosData: bad read reply string");
|
||||
return ZSRV_PKSHORT;
|
||||
}
|
||||
}
|
||||
*size = len;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
Code_t
|
||||
GetKrb5Data(int fd, krb5_data *data) {
|
||||
char p[20];
|
||||
unsigned int i;
|
||||
char *dst;
|
||||
|
||||
for (i=0; i<20; i++) {
|
||||
if (read(fd, &p[i], 1) != 1) {
|
||||
p[i] = 0;
|
||||
syslog(LOG_WARNING,"bad read reply len @%d (got \"%s\")", i, p);
|
||||
return ZSRV_LEN;
|
||||
}
|
||||
if (p[i] == ' ') {
|
||||
p[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 20 || strncmp(p, "V5-", 3) || !atoi(p+3)) {
|
||||
syslog(LOG_WARNING,"bad reply len");
|
||||
return ZSRV_LEN;
|
||||
}
|
||||
data->length = atoi(p+3);
|
||||
data->data = malloc(data->length);
|
||||
if (! data->data) {
|
||||
data->length = 0;
|
||||
return errno;
|
||||
}
|
||||
dst=data->data;
|
||||
for (i=0; i < data->length; i++) {
|
||||
if (read(fd, dst++, 1) != 1) {
|
||||
free(data->data);
|
||||
memset((char *)data, 0, sizeof(krb5_data));
|
||||
syslog(LOG_WARNING,"bad read reply string");
|
||||
return ZSRV_PKSHORT;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Code_t
|
||||
SendKrb5Data(int fd, krb5_data *data) {
|
||||
char p[32];
|
||||
size_t written, size_to_write;
|
||||
sprintf(p, "V5-%lu ", (unsigned long)data->length);
|
||||
size_to_write = strlen (p);
|
||||
if (size_to_write != (written = write(fd, p, size_to_write)) ||
|
||||
data->length != (written = write(fd, data->data, data->length))) {
|
||||
return ((ssize_t)written < 0) ? errno : ZSRV_PKSHORT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Code_t
|
||||
ZCheckSrvAuthentication(ZNotice_t *notice,
|
||||
struct sockaddr_in *from,
|
||||
char *realm)
|
||||
{
|
||||
#ifdef HAVE_KRB5
|
||||
unsigned char *authbuf;
|
||||
krb5_principal princ;
|
||||
krb5_data packet;
|
||||
krb5_ticket *tkt;
|
||||
char *name;
|
||||
krb5_error_code result;
|
||||
krb5_principal server;
|
||||
krb5_keytab keytabid = 0;
|
||||
krb5_auth_context authctx;
|
||||
krb5_keyblock *keyblock;
|
||||
krb5_enctype enctype;
|
||||
krb5_cksumtype cksumtype;
|
||||
krb5_data cksumbuf;
|
||||
int valid;
|
||||
char *cksum0_base, *cksum1_base = NULL, *cksum2_base;
|
||||
char *x;
|
||||
unsigned char *asn1_data, *key_data, *cksum_data;
|
||||
int asn1_len, key_len, cksum0_len = 0, cksum1_len = 0, cksum2_len = 0;
|
||||
KRB5_AUTH_CON_FLAGS_TYPE acflags;
|
||||
#ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
|
||||
krb5_authenticator *authenticator;
|
||||
#define KRB5AUTHENT authenticator
|
||||
#else
|
||||
krb5_authenticator authenticator;
|
||||
#define KRB5AUTHENT &authenticator
|
||||
#endif
|
||||
int len;
|
||||
char *sender;
|
||||
char rlmprincipal[MAX_PRINCIPAL_SIZE];
|
||||
|
||||
if (!notice->z_auth)
|
||||
return ZAUTH_NO;
|
||||
|
||||
/* Check for bogus authentication data length. */
|
||||
if (notice->z_authent_len <= 0) {
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: bogus authenticator length");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
#ifdef HAVE_KRB4
|
||||
if (notice->z_ascii_authent[0] != 'Z' && realm == NULL)
|
||||
return ZCheckAuthentication4(notice, from);
|
||||
#endif
|
||||
|
||||
len = strlen(notice->z_ascii_authent)+1;
|
||||
authbuf = malloc(len);
|
||||
|
||||
/* Read in the authentication data. */
|
||||
if (ZReadZcode((unsigned char *)notice->z_ascii_authent,
|
||||
authbuf,
|
||||
len, &len) == ZERR_BADFIELD) {
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: ZReadZcode: Improperly formatted field");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
if (realm == NULL) {
|
||||
sender = notice->z_sender;
|
||||
} else {
|
||||
(void) snprintf(rlmprincipal, MAX_PRINCIPAL_SIZE, "%s/%s@%s", SERVER_SERVICE,
|
||||
SERVER_INSTANCE, realm);
|
||||
sender = rlmprincipal;
|
||||
}
|
||||
|
||||
packet.length = len;
|
||||
packet.data = (char *)authbuf;
|
||||
|
||||
result = krb5_kt_resolve(Z_krb5_ctx,
|
||||
keytab_file, &keytabid);
|
||||
if (result) {
|
||||
free(authbuf);
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_kt_resolve: %s", error_message(result));
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
/* HOLDING: authbuf, keytabid */
|
||||
/* Create the auth context */
|
||||
result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
|
||||
if (result) {
|
||||
krb5_kt_close(Z_krb5_ctx, keytabid);
|
||||
free(authbuf);
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_init: %s", error_message(result));
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
/* HOLDING: authbuf, keytabid, authctx */
|
||||
result = krb5_auth_con_getflags(Z_krb5_ctx, authctx, &acflags);
|
||||
if (result) {
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
krb5_kt_close(Z_krb5_ctx, keytabid);
|
||||
free(authbuf);
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_getflags: %s", error_message(result));
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
acflags &= ~KRB5_AUTH_CONTEXT_DO_TIME;
|
||||
|
||||
result = krb5_auth_con_setflags(Z_krb5_ctx, authctx, acflags);
|
||||
if (result) {
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
krb5_kt_close(Z_krb5_ctx, keytabid);
|
||||
free(authbuf);
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_setflags: %s", error_message(result));
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm),
|
||||
__Zephyr_realm, SERVER_SERVICE,
|
||||
SERVER_INSTANCE, NULL);
|
||||
if (!result) {
|
||||
result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server,
|
||||
keytabid, NULL, &tkt);
|
||||
krb5_free_principal(Z_krb5_ctx, server);
|
||||
}
|
||||
krb5_kt_close(Z_krb5_ctx, keytabid);
|
||||
|
||||
/* HOLDING: authbuf, authctx */
|
||||
if (result) {
|
||||
if (result == KRB5KRB_AP_ERR_REPEAT)
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: k5 auth failed: %s",
|
||||
error_message(result));
|
||||
else
|
||||
syslog(LOG_WARNING,"ZCheckSrvAuthentication: k5 auth failed: %s",
|
||||
error_message(result));
|
||||
free(authbuf);
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
/* HOLDING: authbuf, authctx, tkt */
|
||||
|
||||
if (tkt == 0 || !Z_tktprincp(tkt)) {
|
||||
if (tkt)
|
||||
krb5_free_ticket(Z_krb5_ctx, tkt);
|
||||
free(authbuf);
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: No Ticket");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
princ = Z_tktprinc(tkt);
|
||||
|
||||
if (princ == 0) {
|
||||
krb5_free_ticket(Z_krb5_ctx, tkt);
|
||||
free(authbuf);
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: No... Ticket?");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
/* HOLDING: authbuf, authctx, tkt */
|
||||
result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
|
||||
if (result) {
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_unparse_name failed: %s",
|
||||
error_message(result));
|
||||
free(authbuf);
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
krb5_free_ticket(Z_krb5_ctx, tkt);
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
krb5_free_ticket(Z_krb5_ctx, tkt);
|
||||
|
||||
/* HOLDING: authbuf, authctx, name */
|
||||
if (strcmp(name, sender)) {
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: name mismatch: '%s' vs '%s'",
|
||||
name, sender);
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
|
||||
krb5_free_unparsed_name(Z_krb5_ctx, name);
|
||||
#else
|
||||
free(name);
|
||||
#endif
|
||||
free(authbuf);
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
|
||||
krb5_free_unparsed_name(Z_krb5_ctx, name);
|
||||
#else
|
||||
free(name);
|
||||
#endif
|
||||
free(authbuf);
|
||||
|
||||
/* HOLDING: authctx */
|
||||
/* Get an authenticator so we can get the keyblock */
|
||||
result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
|
||||
&authenticator);
|
||||
if (result) {
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getauthenticator failed: %s",
|
||||
error_message(result));
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
|
||||
/* HOLDING: authctx, authenticator */
|
||||
result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
|
||||
krb5_auth_con_free(Z_krb5_ctx, authctx);
|
||||
krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
|
||||
if (result) {
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getkey failed: %s",
|
||||
error_message(result));
|
||||
return (ZAUTH_FAILED);
|
||||
}
|
||||
|
||||
/* HOLDING: keyblock */
|
||||
/* Figure out what checksum type to use */
|
||||
key_data = Z_keydata(keyblock);
|
||||
key_len = Z_keylen(keyblock);
|
||||
result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype);
|
||||
if (result) {
|
||||
krb5_free_keyblock(Z_krb5_ctx, keyblock);
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: Z_ExtractEncCksum failed: %s",
|
||||
error_message(result));
|
||||
return (ZAUTH_FAILED);
|
||||
}
|
||||
/* HOLDING: keyblock */
|
||||
|
||||
if (realm == NULL)
|
||||
ZSetSession(keyblock);
|
||||
|
||||
/* Assemble the things to be checksummed */
|
||||
/* first part is from start of packet through z_default_format:
|
||||
* - z_version
|
||||
* - z_num_other_fields
|
||||
* - z_kind
|
||||
* - z_uid
|
||||
* - z_port
|
||||
* - z_auth
|
||||
* - z_authent_len
|
||||
* - z_ascii_authent
|
||||
* - z_class
|
||||
* - z_class_inst
|
||||
* - z_opcode
|
||||
* - z_sender
|
||||
* - z_recipient
|
||||
* - z_default_format
|
||||
*/
|
||||
cksum0_base = notice->z_packet;
|
||||
x = notice->z_default_format;
|
||||
cksum0_len = x + strlen(x) + 1 - cksum0_base;
|
||||
/* second part is from z_multinotice through other fields:
|
||||
* - z_multinotice
|
||||
* - z_multiuid
|
||||
* - z_sender_(sock)addr
|
||||
* - z_charset
|
||||
* - z_other_fields[]
|
||||
*/
|
||||
if (notice->z_num_hdr_fields > 15 ) {
|
||||
cksum1_base = notice->z_multinotice;
|
||||
if (notice->z_num_other_fields)
|
||||
x = notice->z_other_fields[notice->z_num_other_fields - 1];
|
||||
else {
|
||||
/* see lib/ZCkZaut.c:ZCheckZcodeAuthentication */
|
||||
/* XXXXXXXXXXXXXXXXXXXXXXX */
|
||||
if (notice->z_num_hdr_fields > 16)
|
||||
x = cksum1_base + strlen(cksum1_base) + 1; /* multinotice */
|
||||
if (notice->z_num_hdr_fields > 17)
|
||||
x = x + strlen(x) + 1; /* multiuid */
|
||||
if (notice->z_num_hdr_fields > 18)
|
||||
x = x + strlen(x) + 1; /* sender */
|
||||
}
|
||||
cksum1_len = x + strlen(x) + 1 - cksum1_base; /* charset / extra field */
|
||||
}
|
||||
|
||||
/* last part is the message body */
|
||||
cksum2_base = notice->z_message;
|
||||
cksum2_len = notice->z_message_len;
|
||||
|
||||
/*XXX we may wish to ditch this code someday?*/
|
||||
if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') &&
|
||||
key_len == 8 &&
|
||||
(enctype == (krb5_enctype)ENCTYPE_DES_CBC_CRC ||
|
||||
enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD4 ||
|
||||
enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD5)) {
|
||||
/* try old-format checksum (covers cksum0 only) */
|
||||
|
||||
ZChecksum_t our_checksum;
|
||||
|
||||
if (realm == NULL)
|
||||
our_checksum = compute_checksum(notice, key_data);
|
||||
else
|
||||
our_checksum = compute_rlm_checksum(notice, key_data);
|
||||
|
||||
krb5_free_keyblock(Z_krb5_ctx, keyblock);
|
||||
|
||||
if (our_checksum == notice->z_checksum) {
|
||||
return ZAUTH_YES;
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: des quad checksum mismatch");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* HOLDING: keyblock */
|
||||
|
||||
cksumbuf.length = cksum0_len + cksum1_len + cksum2_len;
|
||||
cksumbuf.data = malloc(cksumbuf.length);
|
||||
if (!cksumbuf.data) {
|
||||
krb5_free_keyblock(Z_krb5_ctx, keyblock);
|
||||
syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(cksumbuf.data): %m");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
/* HOLDING: keyblock, cksumbuf.data */
|
||||
|
||||
cksum_data = (unsigned char *)cksumbuf.data;
|
||||
memcpy(cksum_data, cksum0_base, cksum0_len);
|
||||
if (cksum1_len)
|
||||
memcpy(cksum_data + cksum0_len, cksum1_base, cksum1_len);
|
||||
memcpy(cksum_data + cksum0_len + cksum1_len,
|
||||
cksum2_base, cksum2_len);
|
||||
|
||||
/* decode zcoded checksum */
|
||||
/* The encoded form is always longer than the original */
|
||||
asn1_len = strlen(notice->z_ascii_checksum) + 1;
|
||||
asn1_data = malloc(asn1_len);
|
||||
if (!asn1_data) {
|
||||
krb5_free_keyblock(Z_krb5_ctx, keyblock);
|
||||
free(cksumbuf.data);
|
||||
syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(asn1_data): %m");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
/* HOLDING: keyblock, cksumbuf.data, asn1_data */
|
||||
result = ZReadZcode((unsigned char *)notice->z_ascii_checksum,
|
||||
asn1_data, asn1_len, &asn1_len);
|
||||
if (result != ZERR_NONE) {
|
||||
krb5_free_keyblock(Z_krb5_ctx, keyblock);
|
||||
free(asn1_data);
|
||||
free(cksumbuf.data);
|
||||
syslog(LOG_WARNING, "ZCheckSrvAuthentication: ZReadZcode: %s",
|
||||
error_message(result));
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
/* HOLDING: asn1_data, cksumbuf.data */
|
||||
|
||||
valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype,
|
||||
Z_KEYUSAGE_CLT_CKSUM,
|
||||
asn1_data, asn1_len);
|
||||
|
||||
/* XXX compatibility with unreleased interrealm krb5; drop in 3.1 */
|
||||
if (!valid && realm)
|
||||
valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype,
|
||||
Z_KEYUSAGE_SRV_CKSUM,
|
||||
asn1_data, asn1_len);
|
||||
|
||||
free(asn1_data);
|
||||
krb5_free_keyblock(Z_krb5_ctx, keyblock);
|
||||
free(cksumbuf.data);
|
||||
|
||||
if (valid) {
|
||||
return ZAUTH_YES;
|
||||
} else {
|
||||
syslog(LOG_DEBUG, "ZCheckSrvAuthentication: Z_krb5_verify_cksum: failed");
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
#else
|
||||
return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef KRB5AUTHENT
|
||||
|
||||
#if defined(HAVE_KRB4) && defined(HAVE_KRB5)
|
||||
static Code_t
|
||||
ZCheckAuthentication4(ZNotice_t *notice,
|
||||
struct sockaddr_in *from)
|
||||
{
|
||||
int result;
|
||||
char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
|
||||
KTEXT_ST authent;
|
||||
AUTH_DAT dat;
|
||||
ZChecksum_t checksum;
|
||||
char instance[INST_SZ+1];
|
||||
|
||||
if (!notice->z_auth)
|
||||
return ZAUTH_NO;
|
||||
|
||||
/* Check for bogus authentication data length. */
|
||||
if (notice->z_authent_len <= 0)
|
||||
return ZAUTH_FAILED;
|
||||
|
||||
/* Read in the authentication data. */
|
||||
if (ZReadAscii(notice->z_ascii_authent,
|
||||
strlen(notice->z_ascii_authent)+1,
|
||||
(unsigned char *)authent.dat,
|
||||
notice->z_authent_len) == ZERR_BADFIELD) {
|
||||
return ZAUTH_FAILED;
|
||||
}
|
||||
authent.length = notice->z_authent_len;
|
||||
|
||||
strcpy(instance, SERVER_INSTANCE);
|
||||
|
||||
/* We don't have the session key cached; do it the long way. */
|
||||
result = krb_rd_req(&authent, SERVER_SERVICE, instance,
|
||||
from->sin_addr.s_addr, &dat, srvtab_file);
|
||||
if (result == RD_AP_OK) {
|
||||
ZSetSessionDES(&dat.session);
|
||||
sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
|
||||
dat.pinst, dat.prealm);
|
||||
if (strcmp(srcprincipal, notice->z_sender))
|
||||
return ZAUTH_FAILED;
|
||||
} else {
|
||||
return ZAUTH_FAILED; /* didn't decode correctly */
|
||||
}
|
||||
|
||||
/* Check the cryptographic checksum. */
|
||||
checksum = compute_checksum(notice, dat.session);
|
||||
|
||||
if (checksum != notice->z_checksum)
|
||||
return ZAUTH_FAILED;
|
||||
|
||||
return ZAUTH_YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
static ZChecksum_t
|
||||
compute_checksum(ZNotice_t *notice,
|
||||
unsigned char *session_key)
|
||||
{
|
||||
ZChecksum_t checksum;
|
||||
char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
|
||||
|
||||
cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
|
||||
cend = cstart + strlen(cstart) + 1;
|
||||
checksum = z_quad_cksum((unsigned char *)hstart, NULL, cstart - hstart, 0, session_key);
|
||||
checksum ^= z_quad_cksum((unsigned char *)cend, NULL, hend - cend, 0, session_key);
|
||||
checksum ^= z_quad_cksum((unsigned char *)notice->z_message, NULL, notice->z_message_len,
|
||||
0, session_key);
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static ZChecksum_t compute_rlm_checksum(ZNotice_t *notice,
|
||||
unsigned char *session_key)
|
||||
{
|
||||
ZChecksum_t checksum;
|
||||
char *cstart, *hstart = notice->z_packet;
|
||||
|
||||
cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
|
||||
checksum = z_quad_cksum((unsigned char *)hstart, NULL,
|
||||
cstart - hstart, 0, session_key);
|
||||
|
||||
return checksum;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
krb5_error_code
|
||||
Z_krb5_init_keyblock(krb5_context context,
|
||||
krb5_enctype type,
|
||||
size_t size,
|
||||
krb5_keyblock **key)
|
||||
{
|
||||
#ifdef HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
|
||||
return krb5_init_keyblock(context, type, size, key);
|
||||
#else
|
||||
krb5_error_code ret;
|
||||
krb5_keyblock *tmp, tmp_ss;
|
||||
tmp = &tmp_ss;
|
||||
|
||||
*key = NULL;
|
||||
Z_enctype(tmp) = type;
|
||||
Z_keylen(tmp) = size;
|
||||
Z_keydata(tmp) = malloc(size);
|
||||
if (!Z_keydata(tmp))
|
||||
return ENOMEM;
|
||||
ret = krb5_copy_keyblock(context, tmp, key);
|
||||
free(Z_keydata(tmp));
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ZSetSession(krb5_keyblock *keyblock) {
|
||||
krb5_error_code result;
|
||||
|
||||
if (__Zephyr_keyblock) {
|
||||
krb5_free_keyblock_contents(Z_krb5_ctx, __Zephyr_keyblock);
|
||||
result = krb5_copy_keyblock_contents(Z_krb5_ctx, keyblock, __Zephyr_keyblock);
|
||||
} else {
|
||||
result = krb5_copy_keyblock(Z_krb5_ctx, keyblock, &__Zephyr_keyblock);
|
||||
}
|
||||
|
||||
if (result) /*XXX we're out of memory? */
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_KRB4
|
||||
void
|
||||
ZSetSessionDES(C_Block *key) {
|
||||
#ifdef HAVE_KRB5
|
||||
Code_t result;
|
||||
if (__Zephyr_keyblock) {
|
||||
krb5_free_keyblock(Z_krb5_ctx, __Zephyr_keyblock);
|
||||
__Zephyr_keyblock=NULL;
|
||||
}
|
||||
result = Z_krb5_init_keyblock(Z_krb5_ctx, ENCTYPE_DES_CBC_CRC,
|
||||
sizeof(C_Block),
|
||||
&__Zephyr_keyblock);
|
||||
if (result) /*XXX we're out of memory? */
|
||||
return;
|
||||
|
||||
memcpy(Z_keydata(__Zephyr_keyblock), key, sizeof(C_Block));
|
||||
#else
|
||||
memcpy(__Zephyr_session, key, sizeof(C_Block));
|
||||
#endif
|
||||
}
|
||||
#endif
|
705
server/main.c
Normal file
705
server/main.c
Normal file
@ -0,0 +1,705 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains the main loop of the Zephyr server
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Source$
|
||||
* $Author$
|
||||
*
|
||||
* Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#ifndef lint
|
||||
#ifndef SABER
|
||||
static const char rcsid_main_c[] =
|
||||
"$Id$";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Server loop for Zephyr.
|
||||
*/
|
||||
|
||||
/*
|
||||
The Zephyr server maintains several linked lists of information.
|
||||
|
||||
There is an array of servers (otherservers) initialized and maintained
|
||||
by server_s.c.
|
||||
|
||||
Each server descriptor contains a pointer to a linked list of hosts
|
||||
which are ``owned'' by that server. The first server is the ``limbo''
|
||||
server which owns any host which was formerly owned by a dead server.
|
||||
|
||||
Each of these host list entries has an IP address and a pointer to a
|
||||
linked list of clients on that host.
|
||||
|
||||
Each client has a sockaddr_in, a list of subscriptions, and possibly
|
||||
a session key.
|
||||
|
||||
In addition, the class manager has copies of the pointers to the
|
||||
clients which are registered with a particular class, the
|
||||
not-yet-acknowledged list has copies of pointers to some clients,
|
||||
and the hostm manager may have copies of pointers to some clients
|
||||
(if the client has not acknowledged a packet after a given timeout).
|
||||
*/
|
||||
|
||||
static int do_net_setup(void);
|
||||
static int initialize(void);
|
||||
static void usage(void);
|
||||
static void do_reset(void);
|
||||
static RETSIGTYPE bye(int);
|
||||
static RETSIGTYPE dbug_on(int);
|
||||
static RETSIGTYPE dbug_off(int);
|
||||
static RETSIGTYPE sig_dump_db(int);
|
||||
static RETSIGTYPE reset(int);
|
||||
static RETSIGTYPE reap(int);
|
||||
static void read_from_dump(char *dumpfile);
|
||||
static void dump_db(void);
|
||||
static void dump_strings(void);
|
||||
|
||||
#ifndef DEBUG
|
||||
static void detach(void);
|
||||
#endif
|
||||
|
||||
static short doreset = 0; /* if it becomes 1, perform
|
||||
reset functions */
|
||||
|
||||
static char *programname; /* set to the basename of argv[0] */
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
static char tkt5_file[256];
|
||||
#endif
|
||||
#ifdef HAVE_KRB4
|
||||
static char tkt_file[128];
|
||||
#endif
|
||||
|
||||
static int dump_db_flag = 0;
|
||||
static int dump_strings_flag = 0;
|
||||
|
||||
static int nofork;
|
||||
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_KRB5)
|
||||
static char my_realm[REALM_SZ];
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc,
|
||||
char **argv)
|
||||
{
|
||||
int nselect; /* #fildes to select on */
|
||||
int nfound; /* #fildes ready on select */
|
||||
fd_set readable, writable;
|
||||
struct timeval tv, *tvp;
|
||||
int init_from_dump = 0;
|
||||
char *dumpfile;
|
||||
#ifdef _POSIX_VERSION
|
||||
struct sigaction action;
|
||||
#endif
|
||||
int optchar; /* option processing */
|
||||
|
||||
sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, SERVER_LIST_FILE);
|
||||
#ifdef HAVE_KRB4
|
||||
sprintf(srvtab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_SRVTAB);
|
||||
strcpy(tkt_file, ZEPHYR_TKFILE);
|
||||
#endif
|
||||
#ifdef HAVE_KRB5
|
||||
sprintf(keytab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_KEYTAB);
|
||||
strcpy(tkt5_file, ZEPHYR_TK5FILE);
|
||||
#endif
|
||||
sprintf(acl_dir, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_ACL_DIR);
|
||||
sprintf(subs_file, "%s/zephyr/%s", SYSCONFDIR, DEFAULT_SUBS_FILE);
|
||||
|
||||
/* set name */
|
||||
programname = strrchr(argv[0],'/');
|
||||
programname = (programname) ? programname + 1 : argv[0];
|
||||
|
||||
/* process arguments */
|
||||
while ((optchar = getopt(argc, argv, "dsnv4f:k:")) != EOF) {
|
||||
switch(optchar) {
|
||||
case 'd':
|
||||
zdebug = 1;
|
||||
break;
|
||||
#ifdef DEBUG
|
||||
case 's':
|
||||
zalone = 1;
|
||||
break;
|
||||
#endif
|
||||
case 'n':
|
||||
nofork = 1;
|
||||
break;
|
||||
case 'k':
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_KRB5)
|
||||
strncpy(my_realm, optarg, REALM_SZ);
|
||||
#endif
|
||||
break;
|
||||
case 'v':
|
||||
bdump_version = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
init_from_dump = 0;
|
||||
dumpfile = optarg;
|
||||
break;
|
||||
case '4':
|
||||
bdump_auth_proto = 4;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_KRB4
|
||||
/* if there is no readable srvtab and we are not standalone, there
|
||||
is no possible way we can succeed, so we exit */
|
||||
|
||||
if (access(srvtab_file, R_OK)
|
||||
#ifdef DEBUG
|
||||
&& !zalone
|
||||
#endif /* DEBUG */
|
||||
) {
|
||||
fprintf(stderr, "NO ZEPHYR SRVTAB (%s) available; exiting\n",
|
||||
srvtab_file);
|
||||
exit(1);
|
||||
}
|
||||
/* Use local realm if not specified on command line. */
|
||||
if (!*my_realm) {
|
||||
if (krb_get_lrealm(my_realm, 1) != KSUCCESS) {
|
||||
fputs("Couldn't get local Kerberos realm; exiting.\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_KRB4 */
|
||||
|
||||
#ifndef DEBUG
|
||||
if (!nofork)
|
||||
detach();
|
||||
#endif /* DEBUG */
|
||||
|
||||
/* open log */
|
||||
OPENLOG(programname, LOG_PID, LOG_LOCAL6);
|
||||
|
||||
#if defined (DEBUG) && 0
|
||||
if (zalone)
|
||||
syslog(LOG_DEBUG, "standalone operation");
|
||||
#endif
|
||||
if (zdebug)
|
||||
syslog(LOG_DEBUG, "debugging on");
|
||||
|
||||
/* set up sockets & my_addr and myname,
|
||||
find other servers and set up server table, initialize queues
|
||||
for retransmits, initialize error tables,
|
||||
set up restricted classes */
|
||||
|
||||
/* Initialize t_local for other uses */
|
||||
gettimeofday(&t_local, NULL);
|
||||
|
||||
if (initialize())
|
||||
exit(1);
|
||||
|
||||
if (init_from_dump)
|
||||
read_from_dump(dumpfile);
|
||||
|
||||
/* Seed random number set. */
|
||||
srandom(getpid() ^ time(0));
|
||||
|
||||
/* chdir to somewhere where a core dump will survive */
|
||||
if (chdir(TEMP_DIRECTORY) != 0)
|
||||
syslog(LOG_ERR, "chdir failed (%m) (execution continuing)");
|
||||
|
||||
FD_ZERO(&interesting);
|
||||
FD_SET(srv_socket, &interesting);
|
||||
|
||||
nfds = srv_socket + 1;
|
||||
|
||||
|
||||
#ifdef _POSIX_VERSION
|
||||
action.sa_flags = 0;
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
||||
action.sa_handler = bye;
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
|
||||
action.sa_handler = dbug_on;
|
||||
sigaction(SIGUSR1, &action, NULL);
|
||||
|
||||
action.sa_handler = dbug_off;
|
||||
sigaction(SIGUSR2, &action, NULL);
|
||||
|
||||
action.sa_handler = reap;
|
||||
sigaction(SIGCHLD, &action, NULL);
|
||||
|
||||
action.sa_handler = sig_dump_db;
|
||||
sigaction(SIGFPE, &action, NULL);
|
||||
|
||||
action.sa_handler = reset;
|
||||
sigaction(SIGHUP, &action, NULL);
|
||||
#else /* !posix */
|
||||
signal(SIGINT, bye);
|
||||
signal(SIGTERM, bye);
|
||||
signal(SIGUSR1, dbug_on);
|
||||
signal(SIGUSR2, dbug_off);
|
||||
signal(SIGCHLD, reap);
|
||||
signal(SIGFPE, sig_dump_db);
|
||||
signal(SIGHUP, reset);
|
||||
#endif /* _POSIX_VERSION */
|
||||
|
||||
syslog(LOG_NOTICE, "Ready for action");
|
||||
|
||||
/* Reinitialize t_local now that initialization is done. */
|
||||
gettimeofday(&t_local, NULL);
|
||||
uptime = NOW;
|
||||
|
||||
for (;;) {
|
||||
if (doreset)
|
||||
do_reset();
|
||||
|
||||
if (dump_db_flag)
|
||||
dump_db();
|
||||
if (dump_strings_flag)
|
||||
dump_strings();
|
||||
|
||||
timer_process();
|
||||
|
||||
readable = interesting;
|
||||
FD_ZERO(&writable);
|
||||
tvp = timer_timeout(&tv);
|
||||
#ifdef HAVE_ARES
|
||||
nselect = ares_fds(achannel, &readable, &writable);
|
||||
if (nselect < nfds)
|
||||
nselect = nfds;
|
||||
tvp = ares_timeout(achannel, tvp, &tv);
|
||||
#else
|
||||
nselect = nfds;
|
||||
#endif
|
||||
if (msgs_queued()) {
|
||||
/* when there is input in the queue, we
|
||||
artificially set up to pick up the input */
|
||||
nfound = 1;
|
||||
FD_ZERO(&readable);
|
||||
FD_ZERO(&writable);
|
||||
} else {
|
||||
nfound = select(nselect, &readable, &writable, NULL, tvp);
|
||||
}
|
||||
|
||||
/* Initialize t_local for other uses */
|
||||
gettimeofday(&t_local, (struct timezone *)0);
|
||||
|
||||
/* don't flame about EINTR, since a SIGUSR1 or SIGUSR2
|
||||
can generate it by interrupting the select */
|
||||
if (nfound < 0) {
|
||||
if (errno != EINTR)
|
||||
syslog(LOG_WARNING, "select error: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
ares_process(achannel, &readable, &writable);
|
||||
#endif
|
||||
|
||||
if (nfound == 0) {
|
||||
/* either we timed out or we were just
|
||||
polling for input. Either way we want to continue
|
||||
the loop, and process the next timeout */
|
||||
continue;
|
||||
} else {
|
||||
if (bdump_socket >= 0 && FD_ISSET(bdump_socket,&readable))
|
||||
bdump_send();
|
||||
else if (msgs_queued() || FD_ISSET(srv_socket, &readable))
|
||||
handle_packet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize net stuff.
|
||||
Set up the server array.
|
||||
Initialize the packet ack queues to be empty.
|
||||
Initialize the error tables.
|
||||
Restrict certain classes.
|
||||
*/
|
||||
|
||||
static int
|
||||
initialize(void)
|
||||
{
|
||||
if (do_net_setup())
|
||||
return(1);
|
||||
|
||||
server_init();
|
||||
|
||||
#ifdef HAVE_KRB4
|
||||
krb_set_tkt_string(tkt_file);
|
||||
#endif
|
||||
realm_init();
|
||||
|
||||
ZSetServerState(1);
|
||||
ZInitialize(); /* set up the library */
|
||||
#ifdef HAVE_KRB5
|
||||
krb5_cc_resolve(Z_krb5_ctx, tkt5_file, &Z_krb5_ccache);
|
||||
#ifdef HAVE_KRB5_CC_SET_DEFAULT_NAME
|
||||
krb5_cc_set_default_name(Z_krb5_ctx, tkt5_file);
|
||||
#else
|
||||
{
|
||||
/* Hack to make krb5_cc_default do something reasonable */
|
||||
char *env=(char *)malloc(strlen(tkt5_file)+12);
|
||||
if (!env) return(1);
|
||||
sprintf(env, "KRB5CCNAME=%s", tkt5_file);
|
||||
putenv(env);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_KRB5)
|
||||
/* Override what Zinitialize set for ZGetRealm() */
|
||||
if (*my_realm)
|
||||
strcpy(__Zephyr_realm, my_realm);
|
||||
#endif
|
||||
|
||||
/* set up err table */
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
add_error_table(&et_zsrv_error_table);
|
||||
#else
|
||||
init_zsrv_err_tbl();
|
||||
#endif
|
||||
|
||||
ZSetFD(srv_socket); /* set up the socket as the input fildes */
|
||||
|
||||
/* set up default strings */
|
||||
|
||||
class_control = make_string(ZEPHYR_CTL_CLASS, 1);
|
||||
class_admin = make_string(ZEPHYR_ADMIN_CLASS, 1);
|
||||
class_hm = make_string(HM_CTL_CLASS, 1);
|
||||
class_ulogin = make_string(LOGIN_CLASS, 1);
|
||||
class_ulocate = make_string(LOCATE_CLASS, 1);
|
||||
wildcard_instance = make_string(WILDCARD_INSTANCE, 1);
|
||||
empty = make_string("", 0);
|
||||
|
||||
/* restrict certain classes */
|
||||
access_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the server and client sockets, and initialize my_addr and myname
|
||||
*/
|
||||
|
||||
static int
|
||||
do_net_setup(void)
|
||||
{
|
||||
struct servent *sp;
|
||||
struct hostent *hp;
|
||||
char hostname[NS_MAXDNAME];
|
||||
int flags;
|
||||
#ifdef HAVE_ARES
|
||||
int status;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
status = ares_init(&achannel);
|
||||
if (status != ARES_SUCCESS) {
|
||||
syslog(LOG_ERR, "resolver init failed: %s", ares_strerror(status));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gethostname(hostname, sizeof(hostname))) {
|
||||
syslog(LOG_ERR, "no hostname: %m");
|
||||
return 1;
|
||||
}
|
||||
hp = gethostbyname(hostname);
|
||||
if (!hp || hp->h_addrtype != AF_INET) {
|
||||
syslog(LOG_ERR, "no gethostbyname repsonse");
|
||||
strncpy(myname, hostname, NS_MAXDNAME);
|
||||
return 1;
|
||||
}
|
||||
strncpy(myname, hp->h_name, NS_MAXDNAME);
|
||||
memcpy(&my_addr, hp->h_addr_list[0], hp->h_length);
|
||||
|
||||
setservent(1); /* keep file/connection open */
|
||||
|
||||
memset(&srv_addr, 0, sizeof(srv_addr));
|
||||
srv_addr.sin_family = AF_INET;
|
||||
sp = getservbyname(SERVER_SVCNAME, "udp");
|
||||
srv_addr.sin_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
|
||||
|
||||
sp = getservbyname(HM_SVCNAME, "udp");
|
||||
hm_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
|
||||
|
||||
sp = getservbyname(HM_SRV_SVCNAME, "udp");
|
||||
hm_srv_port = (sp) ? sp->s_port : HM_SRV_SVC_FALLBACK;
|
||||
|
||||
srv_socket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (srv_socket < 0) {
|
||||
syslog(LOG_ERR, "client_sock failed: %m");
|
||||
return 1;
|
||||
}
|
||||
if (bind(srv_socket, (struct sockaddr *) &srv_addr,
|
||||
sizeof(srv_addr)) < 0) {
|
||||
syslog(LOG_ERR, "client bind failed: %m");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* set not-blocking */
|
||||
#ifdef _POSIX_VERSION
|
||||
flags = fcntl(srv_socket, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(srv_socket, F_SETFL, flags);
|
||||
#else
|
||||
flags = 1;
|
||||
ioctl(srv_socket, FIONBIO, &flags);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* print out a usage message.
|
||||
*/
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Usage: %s [-d] [-s] [-n] [-k realm] [-f dumpfile]\n",
|
||||
programname);
|
||||
#else
|
||||
fprintf(stderr, "Usage: %s [-d] [-n] [-k realm] [-f dumpfile]\n",
|
||||
programname);
|
||||
#endif /* DEBUG */
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
bye(int sig)
|
||||
{
|
||||
server_shutdown(); /* tell other servers */
|
||||
#ifdef REALM_MGMT
|
||||
realm_shutdown(); /* tell other realms */
|
||||
#endif
|
||||
hostm_shutdown(); /* tell our hosts */
|
||||
kill_realm_pids();
|
||||
#ifdef HAVE_KRB4
|
||||
dest_tkt();
|
||||
#endif
|
||||
syslog(LOG_NOTICE, "goodbye (sig %d)", sig);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
dbug_on(int sig)
|
||||
{
|
||||
syslog(LOG_DEBUG, "debugging turned on");
|
||||
zdebug = 1;
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
dbug_off(int sig)
|
||||
{
|
||||
syslog(LOG_DEBUG, "debugging turned off");
|
||||
zdebug = 0;
|
||||
}
|
||||
|
||||
int fork_for_dump = 0;
|
||||
|
||||
static void dump_strings(void)
|
||||
{
|
||||
char filename[128];
|
||||
|
||||
FILE *fp;
|
||||
int oerrno = errno;
|
||||
|
||||
sprintf(filename, "%szephyr.strings", TEMP_DIRECTORY);
|
||||
fp = fopen (filename, "w");
|
||||
if (!fp) {
|
||||
syslog(LOG_ERR, "can't open strings dump file: %m");
|
||||
errno = oerrno;
|
||||
dump_strings_flag = 0;
|
||||
return;
|
||||
}
|
||||
syslog(LOG_INFO, "dumping strings to disk");
|
||||
print_string_table(fp);
|
||||
if (fclose(fp) == EOF)
|
||||
syslog(LOG_ERR, "error writing strings dump file");
|
||||
else
|
||||
syslog(LOG_INFO, "dump done");
|
||||
oerrno = errno;
|
||||
dump_strings_flag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
sig_dump_db(int sig)
|
||||
{
|
||||
dump_db_flag = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
dump_db(void)
|
||||
{
|
||||
/* dump the in-core database to human-readable form on disk */
|
||||
FILE *fp;
|
||||
int oerrno = errno;
|
||||
int pid;
|
||||
char filename[128];
|
||||
|
||||
pid = (fork_for_dump) ? fork() : -1;
|
||||
if (pid > 0) {
|
||||
dump_db_flag = 0;
|
||||
return;
|
||||
}
|
||||
sprintf(filename, "%szephyr.db", TEMP_DIRECTORY);
|
||||
fp = fopen(filename, "w");
|
||||
if (!fp) {
|
||||
syslog(LOG_ERR, "can't open dump database");
|
||||
errno = oerrno;
|
||||
dump_db_flag = 0;
|
||||
return;
|
||||
}
|
||||
syslog(LOG_INFO, "dumping to disk");
|
||||
server_dump_servers(fp);
|
||||
uloc_dump_locs(fp);
|
||||
client_dump_clients(fp);
|
||||
triplet_dump_subs(fp);
|
||||
realm_dump_realms(fp);
|
||||
syslog(LOG_INFO, "dump done");
|
||||
if (fclose(fp) == EOF)
|
||||
syslog(LOG_ERR, "can't close dump db");
|
||||
if (pid == 0)
|
||||
exit(0);
|
||||
errno = oerrno;
|
||||
dump_db_flag = 0;
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
reset(int sig)
|
||||
{
|
||||
zdbug((LOG_DEBUG,"reset()"));
|
||||
doreset = 1;
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
reap(int sig)
|
||||
{
|
||||
int pid, i = 0;
|
||||
int oerrno = errno;
|
||||
ZRealm *rlm;
|
||||
#ifdef _POSIX_VERSION
|
||||
int waitb;
|
||||
#else
|
||||
union wait waitb;
|
||||
#endif
|
||||
|
||||
zdbug((LOG_DEBUG,"reap()"));
|
||||
#ifdef _POSIX_VERSION
|
||||
while ((pid = waitpid(-1, &waitb, WNOHANG)) == 0)
|
||||
{ i++; if (i > 10) break; }
|
||||
#else
|
||||
while ((pid = wait3 (&waitb, WNOHANG, (struct rusage*) 0)) == 0)
|
||||
{ i++; if (i > 10) break; }
|
||||
#endif
|
||||
|
||||
errno = oerrno;
|
||||
|
||||
if (pid) {
|
||||
if (WIFSIGNALED(waitb) == 0) {
|
||||
if (WIFEXITED(waitb) != 0) {
|
||||
rlm = realm_get_realm_by_pid(pid);
|
||||
if (rlm) {
|
||||
rlm->child_pid = 0;
|
||||
rlm->have_tkt = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rlm = realm_get_realm_by_pid(pid);
|
||||
if (rlm) {
|
||||
rlm->child_pid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_reset(void)
|
||||
{
|
||||
int oerrno = errno;
|
||||
#ifdef _POSIX_VERSION
|
||||
sigset_t mask, omask;
|
||||
#else
|
||||
int omask;
|
||||
#endif
|
||||
#ifdef _POSIX_VERSION
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGHUP);
|
||||
sigprocmask(SIG_BLOCK, &mask, &omask);
|
||||
#else
|
||||
omask = sigblock(sigmask(SIGHUP));
|
||||
#endif
|
||||
|
||||
/* reset various things in the server's state */
|
||||
subscr_reset();
|
||||
server_reset();
|
||||
realm_init();
|
||||
access_reinit();
|
||||
syslog(LOG_INFO, "restart completed");
|
||||
doreset = 0;
|
||||
errno = oerrno;
|
||||
#ifdef _POSIX_VERSION
|
||||
sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
|
||||
#else
|
||||
sigsetmask(omask);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef DEBUG
|
||||
/*
|
||||
* detach from the terminal
|
||||
*/
|
||||
|
||||
static void
|
||||
detach(void)
|
||||
{
|
||||
/* detach from terminal and fork. */
|
||||
int i;
|
||||
long size;
|
||||
|
||||
#ifdef _POSIX_VERSION
|
||||
size = sysconf(_SC_OPEN_MAX);
|
||||
#else
|
||||
size = getdtablesize();
|
||||
#endif
|
||||
/* profiling seems to get confused by fork() */
|
||||
i = fork ();
|
||||
if (i) {
|
||||
if (i < 0)
|
||||
perror("fork");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
close(i);
|
||||
|
||||
i = open("/dev/tty", O_RDWR, 666);
|
||||
#ifdef TIOCNOTTY /* Only necessary on old systems. */
|
||||
ioctl(i, TIOCNOTTY, NULL);
|
||||
#endif
|
||||
close(i);
|
||||
#ifdef _POSIX_VERSION
|
||||
setsid();
|
||||
#endif
|
||||
}
|
||||
#endif /* not DEBUG */
|
||||
|
||||
static void
|
||||
read_from_dump(char *dumpfile)
|
||||
{
|
||||
/* Not yet implemented. */
|
||||
return;
|
||||
}
|
||||
|
1664
server/realm.c
Normal file
1664
server/realm.c
Normal file
File diff suppressed because it is too large
Load Diff
1485
server/server.c
Normal file
1485
server/server.c
Normal file
File diff suppressed because it is too large
Load Diff
1317
server/subscr.c
Normal file
1317
server/subscr.c
Normal file
File diff suppressed because it is too large
Load Diff
348
server/test_server.c
Normal file
348
server/test_server.c
Normal file
@ -0,0 +1,348 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains the server unit tests.
|
||||
*
|
||||
* Created by: Karl Ramm
|
||||
*
|
||||
* Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "zserver.h"
|
||||
|
||||
int ulogin_add_user(ZNotice_t *notice, Exposure_type exposure,
|
||||
struct sockaddr_in *who);
|
||||
Exposure_type ulogin_remove_user(ZNotice_t *notice,
|
||||
struct sockaddr_in *who,
|
||||
int *err_return);
|
||||
int ulogin_find(char *user, struct in_addr *host,
|
||||
unsigned int port);
|
||||
int ulogin_find_user(char *user);
|
||||
void ulogin_flush_user(ZNotice_t *notice);
|
||||
extern Location *locations;
|
||||
|
||||
#define TEST(EXP) \
|
||||
do { \
|
||||
printf("%s:%d: %s: ", __FILE__, __LINE__, #EXP); \
|
||||
fflush(stdout); \
|
||||
if (EXP) { \
|
||||
puts("PASS"); \
|
||||
} else { \
|
||||
puts("FAIL"); \
|
||||
failures++; \
|
||||
} \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
#define V(EXP) \
|
||||
do { \
|
||||
printf("%s:%d: %s\n", __FILE__, __LINE__, #EXP); \
|
||||
fflush(stdout); \
|
||||
EXP; \
|
||||
} while (0)
|
||||
|
||||
#define VI(EXP) \
|
||||
do { \
|
||||
int result; \
|
||||
printf("%s:%d: %s ->", __FILE__, __LINE__, #EXP); \
|
||||
fflush(stdout); \
|
||||
result=EXP; \
|
||||
printf(" %d\n", result); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
#define PP(s) \
|
||||
do { \
|
||||
printf("%s:%d: %s\n", __FILE__, __LINE__, s); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
#define P1(fmt, x) \
|
||||
do { \
|
||||
printf("%s:%d: " fmt "\n", __FILE__, __LINE__, x); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
int failures = 0;
|
||||
|
||||
void test_uloc(void);
|
||||
void test_acl_files(void);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int logopt = 0;
|
||||
#if 0 && defined(LOG_PERROR)
|
||||
logopt = LOG_PERROR;
|
||||
#endif
|
||||
openlog("test_server", logopt, LOG_USER);
|
||||
puts("Zephyr server testing");
|
||||
puts("");
|
||||
|
||||
test_uloc();
|
||||
test_acl_files();
|
||||
|
||||
if(failures)
|
||||
printf("\n%d FAILURES\n", failures);
|
||||
|
||||
exit(!(failures == 0));
|
||||
}
|
||||
|
||||
void
|
||||
test_uloc(void)
|
||||
{
|
||||
ZNotice_t z1, z2, z0, z4;
|
||||
String *s1, *s2, *s0, *s4;
|
||||
struct sockaddr_in who1, who2, who3, who0, who4;
|
||||
int ret;
|
||||
|
||||
puts("uloc storage routines");
|
||||
|
||||
TEST(ulogin_find_user("nonexistent") == -1);
|
||||
|
||||
/* fake up just enough */
|
||||
who1.sin_family = AF_INET;
|
||||
who1.sin_port = 1;
|
||||
who1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
z1.z_class_inst = "user1";
|
||||
z1.z_port = 1;
|
||||
z1.z_message = "here\0now\0this\0";
|
||||
z1.z_message_len = 14;
|
||||
|
||||
s1 = make_string(z1.z_class_inst, 0);
|
||||
|
||||
TEST(ulogin_add_user(&z1, NET_ANN, &who1) == 0);
|
||||
TEST(ulogin_find_user("user1") != -1);
|
||||
|
||||
who2.sin_family = AF_INET;
|
||||
who2.sin_port = 2;
|
||||
who2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
z2.z_class_inst = "user2";
|
||||
z2.z_port = 2;
|
||||
z2.z_message = "here\0now\0this\0";
|
||||
z2.z_message_len = 14;
|
||||
|
||||
s2 = make_string(z2.z_class_inst, 0);
|
||||
|
||||
TEST(ulogin_add_user(&z2, NET_ANN, &who2) == 0);
|
||||
TEST(ulogin_find_user("user2") != -1);
|
||||
TEST(locations[ulogin_find_user("user1")].user == s1);
|
||||
TEST(locations[ulogin_find_user("user2")].user == s2);
|
||||
TEST(ulogin_add_user(&z1, NET_ANN, &who1) == 0);
|
||||
TEST(locations[ulogin_find_user("user1")].user == s1);
|
||||
TEST(locations[ulogin_find_user("user2")].user == s2);
|
||||
|
||||
who3.sin_family = AF_INET;
|
||||
who3.sin_port = 3;
|
||||
who3.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
TEST(ulogin_find("user1", &who3.sin_addr, 3) == -1);
|
||||
|
||||
who0.sin_family = AF_INET;
|
||||
who0.sin_port = 3;
|
||||
who0.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
z0.z_class_inst = "user0";
|
||||
z0.z_port = 3;
|
||||
z0.z_message = "here\0now\0this\0";
|
||||
z0.z_message_len = 14;
|
||||
|
||||
s0 = make_string(z0.z_class_inst, 0);
|
||||
|
||||
TEST(ulogin_add_user(&z0, NET_ANN, &who0) == 0);
|
||||
TEST(ulogin_find_user("user0") != -1);
|
||||
TEST(locations[ulogin_find_user("user1")].user == s1);
|
||||
TEST(locations[ulogin_find_user("user2")].user == s2);
|
||||
|
||||
TEST(ulogin_remove_user(&z0, &who0, &ret) == NET_ANN && ret == 0);
|
||||
/* 1 = NOLOC */
|
||||
TEST(ulogin_remove_user(&z0, &who0, &ret) == NONE && ret == 1);
|
||||
|
||||
TEST(ulogin_add_user(&z0, NET_ANN, &who0) == 0);
|
||||
TEST(ulogin_remove_user(&z1, &who0, &ret) == NET_ANN && ret == 0);
|
||||
|
||||
V(ulogin_flush_user(&z0));
|
||||
TEST(ulogin_find_user("user0") == -1);
|
||||
|
||||
TEST(ulogin_add_user(&z0, NET_ANN, &who0) == 0);
|
||||
TEST(ulogin_add_user(&z1, NET_ANN, &who1) == 0);
|
||||
V(ulogin_flush_user(&z1));
|
||||
TEST(ulogin_find_user("user1") == -1);
|
||||
|
||||
who4.sin_family = AF_INET;
|
||||
who4.sin_port = 4;
|
||||
who4.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
z4.z_class_inst = "user4";
|
||||
z4.z_port = 4;
|
||||
z4.z_message = "here\0now\0this\0";
|
||||
z4.z_message_len = 14;
|
||||
|
||||
s4 = make_string(z4.z_class_inst, 0);
|
||||
|
||||
TEST(ulogin_add_user(&z4, NET_ANN, &who4) == 0);
|
||||
|
||||
V(uloc_flush_client(&who2));
|
||||
TEST(locations[ulogin_find_user("user0")].user == s0);
|
||||
TEST(ulogin_find_user("user1") == -1);
|
||||
TEST(ulogin_find_user("user2") == -1);
|
||||
TEST(locations[ulogin_find_user("user4")].user == s4);
|
||||
|
||||
V(uloc_hflush(&who0.sin_addr));
|
||||
TEST(ulogin_find_user("user0") == -1);
|
||||
TEST(ulogin_find_user("user1") == -1);
|
||||
TEST(ulogin_find_user("user2") == -1);
|
||||
TEST(locations[ulogin_find_user("user4")].user == s4);
|
||||
puts("");
|
||||
}
|
||||
|
||||
void
|
||||
test_acl_files(){
|
||||
char filename[]="/tmp/test_server_acl.XXXXXX";
|
||||
int fd;
|
||||
int result;
|
||||
struct sockaddr_in who_zero;
|
||||
struct sockaddr_in who_localhost;
|
||||
struct sockaddr_in who_broadcast;
|
||||
|
||||
memset(&who_zero, 0, sizeof(who_zero));
|
||||
memset(&who_localhost, 0, sizeof(who_localhost));
|
||||
memset(&who_broadcast, 0, sizeof(who_broadcast));
|
||||
|
||||
who_localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
who_broadcast.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
||||
|
||||
puts("low-level acl routines");
|
||||
puts("");
|
||||
|
||||
fd = mkstemp(filename);
|
||||
P1("acl file is %s", filename);
|
||||
|
||||
PP("empty acl");
|
||||
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 0);
|
||||
|
||||
write(fd, "*\n", 2);
|
||||
acl_cache_reset();
|
||||
PP("acl of *");
|
||||
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar", NULL) == 1);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
write(fd, "foo\n", 4);
|
||||
acl_cache_reset();
|
||||
PP("acl of just foo");
|
||||
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar", NULL) == 0);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
write(fd, "*@TIM.EDU\n", 10);
|
||||
acl_cache_reset();
|
||||
PP("acl of *@TIM.EDU");
|
||||
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar", NULL) == 0);
|
||||
TEST(acl_check(filename, "foo@TIM.EDU", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", NULL) == 1);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
write(fd, "*.*@TIM.EDU\n", 12);
|
||||
acl_cache_reset();
|
||||
PP("acl of *.*@TIM.EDU");
|
||||
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar", NULL) == 0);
|
||||
TEST(acl_check(filename, "foo@TIM.EDU", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", NULL) == 1);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
write(fd, "foo@TIM.EDU\n", 12);
|
||||
acl_cache_reset();
|
||||
PP("acl of foo@TIM.EDU");
|
||||
|
||||
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar", NULL) == 0);
|
||||
TEST(acl_check(filename, "foo@TIM.EDU", NULL) == 1);
|
||||
TEST(acl_check(filename, "f\\oo@TIM.EDU", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", NULL) == 0);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
write(fd, "!bar@TIM.EDU\n*@*\n", 17);
|
||||
acl_cache_reset();
|
||||
PP("acl of !bar@TIM.EDU, *@*");
|
||||
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar", NULL) == 1);
|
||||
TEST(acl_check(filename, "foo@TIM.EDU", NULL) == 1);
|
||||
TEST(acl_check(filename, "f\\oo@TIM.EDU", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", NULL) == 0);
|
||||
|
||||
TEST(acl_check(filename, NULL, &who_zero) == 0);
|
||||
TEST(acl_check(filename, NULL, &who_localhost) == 0);
|
||||
|
||||
write(fd, "@0.0.0.0\n", 9);
|
||||
acl_cache_reset();
|
||||
PP("acl +@0.0.0.0");
|
||||
|
||||
TEST(acl_check(filename, NULL, &who_zero) == 1);
|
||||
TEST(acl_check(filename, NULL, &who_localhost) == 0);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", &who_zero) == 0);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
ftruncate(fd, 0);
|
||||
write(fd, "*@*\n@0.0.0.0\n!@127.0.0.0/8\n", 27);
|
||||
acl_cache_reset();
|
||||
PP("acl *@*, @0.0.0.0, !@127/8");
|
||||
|
||||
TEST(acl_check(filename, NULL, &who_zero) == 1);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", &who_zero) == 1);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", &who_localhost) == 0);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
ftruncate(fd, 0);
|
||||
write(fd, "*/root@TIM.EDU\n", 15);
|
||||
acl_cache_reset();
|
||||
PP("acl */root@TIM.EDU");
|
||||
TEST(acl_check(filename, NULL, NULL) == 0);
|
||||
TEST(acl_check(filename, "foo", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", NULL) == 0);
|
||||
TEST(acl_check(filename, "foo/root@TIM.EDU", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar/root@TIM.EDU", NULL) == 1);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
ftruncate(fd, 0);
|
||||
write(fd, "foo/*@TIM.EDU\n", 14);
|
||||
acl_cache_reset();
|
||||
PP("acl foo/*@TIM.EDU");
|
||||
TEST(acl_check(filename, "foo@TIM.EDU", NULL) == 0);
|
||||
TEST(acl_check(filename, "bar@TIM.EDU", NULL) == 0);
|
||||
TEST(acl_check(filename, "foo/root@TIM.EDU", NULL) == 1);
|
||||
TEST(acl_check(filename, "bar/root@TIM.EDU", NULL) == 0);
|
||||
|
||||
PP("check vs. nonexistent acl");
|
||||
TEST(acl_check("/nonexistent", "foo", NULL) == 0);
|
||||
unlink(filename);
|
||||
puts("");
|
||||
}
|
250
server/timer.c
Normal file
250
server/timer.c
Normal file
@ -0,0 +1,250 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for managing multiple timeouts.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
* Derived from timer_manager_ by Ken Raeburn
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "zserver.h"
|
||||
|
||||
#ifndef SABER
|
||||
#ifndef lint
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
#endif /* lint */
|
||||
#endif /* SABER */
|
||||
|
||||
/*
|
||||
* timer_manager_ -- routines for handling timers in login_shell
|
||||
* (and elsewhere)
|
||||
*
|
||||
* Copyright 1986 Student Information Processing Board,
|
||||
* Massachusetts Institute of Technology
|
||||
*
|
||||
* written by Ken Raeburn
|
||||
|
||||
Permission to use, copy, modify, and distribute this
|
||||
software and its documentation for any purpose and without
|
||||
fee is hereby granted, provided that the above copyright
|
||||
notice appear in all copies and that both that copyright
|
||||
notice and this permission notice appear in supporting
|
||||
documentation, and that the name of M.I.T. and the Student
|
||||
Information Processing Board not be used in
|
||||
advertising or publicity pertaining to distribution of the
|
||||
software without specific, written prior permission.
|
||||
M.I.T. and the Student Information Processing Board
|
||||
make no representations about the suitability of
|
||||
this software for any purpose. It is provided "as is"
|
||||
without express or implied warranty.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* External functions:
|
||||
*
|
||||
* Timer *timer_set_rel (time_rel, proc, arg)
|
||||
* long time_rel;
|
||||
* void (*proc)();
|
||||
* void *arg;
|
||||
* Timer *timer_set_abs (time_abs, proc, arg)
|
||||
* long time_abs;
|
||||
* void (*proc)();
|
||||
* void *arg;
|
||||
*
|
||||
* void timer_reset(tmr)
|
||||
* Timer *tmr;
|
||||
*
|
||||
* void timer_process()
|
||||
*
|
||||
*/
|
||||
|
||||
/* DELTA is just an offset to keep the size a bit less than a power
|
||||
* of two. It's measured in pointers, so it's 32 bytes on most
|
||||
* systems. */
|
||||
#define DELTA 8
|
||||
#define INITIAL_HEAP_SIZE (1024 - DELTA)
|
||||
|
||||
/* We have three operations which we need to be able to perform
|
||||
* quickly: adding a timer, deleting a timer given a pointer to
|
||||
* it, and determining which timer will be the next to go off. A
|
||||
* heap is an ideal data structure for these purposes, so we use
|
||||
* one. The heap is an array of pointers to timers, and each timer
|
||||
* knows the position of its pointer in the heap.
|
||||
*
|
||||
* Okay, what is the heap, exactly? It's a data structure,
|
||||
* represented as an array, with the invariant condition that
|
||||
* the timeout of heap[i] is less than or equal to the timeout of
|
||||
* heap[i * 2 + 1] and heap[i * 2 + 2] (assuming i * 2 + 1 and
|
||||
* i * 2 + 2 are valid * indices). An obvious consequence of this
|
||||
* is that heap[0] has the lowest timer value, so finding the first
|
||||
* timer to go off is easy. We say that an index i has "children"
|
||||
* i * 2 + 1 and i * 2 + 1, and the "parent" (i - 1) / 2.
|
||||
*
|
||||
* To add a timer to the heap, we start by adding it to the end, and
|
||||
* then keep swapping it with its parent until it has a parent with
|
||||
* a timer value less than its value. With a little bit of thought,
|
||||
* you can see that this preserves the heap property on all indices
|
||||
* of the array.
|
||||
*
|
||||
* To delete a timer at position i from the heap, we discard it and
|
||||
* fill in its position with the last timer in the heap. In order
|
||||
* to restore the heap, we have to consider two cases: the timer
|
||||
* value at i is less than that of its parent, or the timer value at
|
||||
* i is greater than that of one of its children. In the first case,
|
||||
* we propagate the timer at i up the tree, swapping it with its
|
||||
* parent, until the heap is restored; in the second case, we
|
||||
* propagate the timer down the tree, swapping it with its least
|
||||
* child, until the heap is restored. */
|
||||
|
||||
/* In order to ensure that the back pointers from timers are consistent
|
||||
* with the heap pointers, all heap assignments should be done with the
|
||||
* HEAP_ASSIGN() macro, which sets the back pointer and updates the
|
||||
* heap at the same time. */
|
||||
#define PARENT(i) (((i) - 1) / 2)
|
||||
#define CHILD1(i) ((i) * 2 + 1)
|
||||
#define CHILD2(i) ((i) * 2 + 2)
|
||||
#define TIME(i) (heap[i]->abstime)
|
||||
#define HEAP_ASSIGN(pos, tmr) ((heap[pos] = (tmr))->heap_pos = (pos))
|
||||
|
||||
static Timer **heap;
|
||||
static int num_timers = 0;
|
||||
static int heap_size = 0;
|
||||
|
||||
static void timer_botch (void*);
|
||||
static Timer *add_timer (Timer *);
|
||||
|
||||
Timer *
|
||||
timer_set_rel(long time_rel,
|
||||
void (*proc)(void *),
|
||||
void *arg)
|
||||
{
|
||||
Timer *new_t;
|
||||
|
||||
new_t = (Timer *) malloc(sizeof(*new_t));
|
||||
if (new_t == NULL)
|
||||
return(NULL);
|
||||
new_t->abstime = time_rel + NOW;
|
||||
new_t->func = proc;
|
||||
new_t->arg = arg;
|
||||
return add_timer(new_t);
|
||||
}
|
||||
|
||||
void
|
||||
timer_reset(Timer *tmr)
|
||||
{
|
||||
int pos, min;
|
||||
|
||||
/* Free the timer, saving its heap position. */
|
||||
pos = tmr->heap_pos;
|
||||
free(tmr);
|
||||
|
||||
if (pos != num_timers - 1) {
|
||||
/* Replace the timer with the last timer in the heap and
|
||||
* restore the heap, propagating the timer either up or
|
||||
* down, depending on which way it violates the heap
|
||||
* property to insert the last timer in place of the
|
||||
* deleted timer. */
|
||||
if (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos))) {
|
||||
do {
|
||||
HEAP_ASSIGN(pos, heap[PARENT(pos)]);
|
||||
pos = PARENT(pos);
|
||||
} while (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos)));
|
||||
HEAP_ASSIGN(pos, heap[num_timers - 1]);
|
||||
} else {
|
||||
while (CHILD2(pos) < num_timers) {
|
||||
min = num_timers - 1;
|
||||
if (TIME(CHILD1(pos)) < TIME(min))
|
||||
min = CHILD1(pos);
|
||||
if (TIME(CHILD2(pos)) < TIME(min))
|
||||
min = CHILD2(pos);
|
||||
HEAP_ASSIGN(pos, heap[min]);
|
||||
pos = min;
|
||||
}
|
||||
if (pos != num_timers - 1)
|
||||
HEAP_ASSIGN(pos, heap[num_timers - 1]);
|
||||
}
|
||||
}
|
||||
num_timers--;
|
||||
}
|
||||
|
||||
|
||||
#define set_timeval(t,s) ((t).tv_sec=(s),(t).tv_usec=0,(t))
|
||||
|
||||
static Timer *
|
||||
add_timer(Timer *new)
|
||||
{
|
||||
int pos;
|
||||
|
||||
/* Create or resize the heap as necessary. */
|
||||
if (heap_size == 0) {
|
||||
heap_size = INITIAL_HEAP_SIZE;
|
||||
heap = (Timer **) malloc(heap_size * sizeof(Timer *));
|
||||
} else if (num_timers >= heap_size) {
|
||||
heap_size = heap_size * 2 + DELTA;
|
||||
heap = (Timer **) realloc(heap, heap_size * sizeof(Timer *));
|
||||
}
|
||||
if (!heap) {
|
||||
free(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Insert the Timer *into the heap. */
|
||||
pos = num_timers;
|
||||
while (pos > 0 && new->abstime < TIME(PARENT(pos))) {
|
||||
HEAP_ASSIGN(pos, heap[PARENT(pos)]);
|
||||
pos = PARENT(pos);
|
||||
}
|
||||
HEAP_ASSIGN(pos, new);
|
||||
num_timers++;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
void
|
||||
timer_process(void)
|
||||
{
|
||||
Timer *t;
|
||||
timer_proc func;
|
||||
void *arg;
|
||||
|
||||
if (num_timers == 0 || heap[0]->abstime > NOW)
|
||||
return;
|
||||
|
||||
/* Remove the first timer from the heap, remembering its
|
||||
* function and argument. */
|
||||
t = heap[0];
|
||||
func = t->func;
|
||||
arg = t->arg;
|
||||
t->func = timer_botch;
|
||||
t->arg = NULL;
|
||||
timer_reset(t);
|
||||
|
||||
/* Run the function. */
|
||||
func(arg);
|
||||
}
|
||||
|
||||
struct timeval *
|
||||
timer_timeout(struct timeval *tvbuf)
|
||||
{
|
||||
if (num_timers > 0) {
|
||||
tvbuf->tv_sec = heap[0]->abstime - NOW;
|
||||
if (tvbuf->tv_sec < 0)
|
||||
tvbuf->tv_sec = 0;
|
||||
tvbuf->tv_usec = 0;
|
||||
return tvbuf;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
timer_botch(void *arg)
|
||||
{
|
||||
syslog(LOG_CRIT, "timer botch\n");
|
||||
abort();
|
||||
}
|
||||
|
54
server/timer.h
Normal file
54
server/timer.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains definitions used by timer.c
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
* Derived from timer_manager_.h by Ken Raeburn
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TIMER_H
|
||||
|
||||
/*
|
||||
* timer_manager_ -- routines for handling timers in login_shell
|
||||
* (and elsewhere)
|
||||
*
|
||||
* Copyright 1986 Student Information Processing Board,
|
||||
* Massachusetts Institute of Technology
|
||||
*
|
||||
* written by Ken Raeburn
|
||||
|
||||
Permission to use, copy, modify, and distribute this
|
||||
software and its documentation for any purpose and without
|
||||
fee is hereby granted, provided that the above copyright
|
||||
notice appear in all copies and that both that copyright
|
||||
notice and this permission notice appear in supporting
|
||||
documentation, and that the name of M.I.T. and the Student
|
||||
Information Processing Board not be used in
|
||||
advertising or publicity pertaining to distribution of the
|
||||
software without specific, written prior permission.
|
||||
M.I.T. and the Student Information Processing Board
|
||||
make no representations about the suitability of
|
||||
this software for any purpose. It is provided "as is"
|
||||
without express or implied warranty.
|
||||
|
||||
*/
|
||||
|
||||
typedef void (*timer_proc) __P((void *));
|
||||
|
||||
typedef struct _Timer {
|
||||
int heap_pos; /* Position in timer heap */
|
||||
long abstime;
|
||||
timer_proc func;
|
||||
void *arg;
|
||||
} Timer;
|
||||
|
||||
Timer *timer_set_rel(long, timer_proc, void *);
|
||||
Timer *timer_set_abs(long, timer_proc, void *);
|
||||
void timer_reset(Timer *);
|
||||
void timer_process(void);
|
||||
struct timeval *timer_timeout(struct timeval *tvbuf);
|
||||
|
||||
#endif /* __TIMER_H */
|
||||
|
932
server/uloc.c
Normal file
932
server/uloc.c
Normal file
@ -0,0 +1,932 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains functions for the User Locator service.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifndef lint
|
||||
#ifndef SABER
|
||||
static const char rcsid_uloc_c[] =
|
||||
"$Id$";
|
||||
#endif /* SABER */
|
||||
#endif /* lint */
|
||||
|
||||
/*
|
||||
* The user locator functions.
|
||||
*
|
||||
* External functions:
|
||||
*
|
||||
* void ulocate_dispatch(notice, auth, who, server)
|
||||
* ZNotice_t *notice;
|
||||
* int auth;
|
||||
* struct sockaddr_in *who;
|
||||
* Server *server;
|
||||
*
|
||||
* void ulogin_dispatch(notice, auth, who, server)
|
||||
* ZNotice_t *notice;
|
||||
* int auth;
|
||||
* struct sockaddr_in *who;
|
||||
* Server *server;
|
||||
*
|
||||
* void uloc_hflush(addr)
|
||||
* struct in_addr *addr;
|
||||
*
|
||||
* void uloc_flush_client(sin)
|
||||
* struct sockaddr_in *sin;
|
||||
*
|
||||
* Code_t uloc_send_locations()
|
||||
*
|
||||
* void uloc_dump_locs(fp)
|
||||
* FILE *fp;
|
||||
*/
|
||||
|
||||
/*
|
||||
* The user locator.
|
||||
* We maintain an array of Location sorted by user (so we can do
|
||||
* binary searches), growing and shrinking it as necessary.
|
||||
*/
|
||||
|
||||
/* WARNING: make sure this is the same as the number of strings you */
|
||||
/* plan to hand back to the user in response to a locate request, */
|
||||
/* else you will lose. See ulogin_locate() and uloc_send_locations() */
|
||||
#define NUM_FIELDS 3
|
||||
|
||||
#define NOLOC 1
|
||||
#define QUIET -1
|
||||
#define UNAUTH -2
|
||||
|
||||
static void ulogin_locate(ZNotice_t *notice, struct sockaddr_in *who,
|
||||
int auth);
|
||||
void ulogin_flush_user(ZNotice_t *notice);
|
||||
int ulogin_find(char *user, struct in_addr *host,
|
||||
unsigned int port);
|
||||
int ulogin_find_user(char *user);
|
||||
static int ulogin_setup(ZNotice_t *notice, Location *locs,
|
||||
Exposure_type exposure, struct sockaddr_in *who);
|
||||
int ulogin_add_user(ZNotice_t *notice, Exposure_type exposure,
|
||||
struct sockaddr_in *who);
|
||||
static int ulogin_parse(ZNotice_t *notice, Location *locs);
|
||||
Exposure_type ulogin_remove_user(ZNotice_t *notice,
|
||||
struct sockaddr_in *who,
|
||||
int *err_return);
|
||||
static void login_sendit(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who, int external);
|
||||
static char **ulogin_marshal_locs(ZNotice_t *notice, int *found, int auth);
|
||||
|
||||
static void free_loc(Location *loc);
|
||||
static void ulogin_locate_forward(ZNotice_t *notice, struct sockaddr_in *who,
|
||||
ZRealm *realm);
|
||||
static int reallocate_locations(int new_num);
|
||||
|
||||
Location *locations = NULL; /* ptr to first in array */
|
||||
static int num_locs = 0; /* number in array */
|
||||
|
||||
/*
|
||||
* Dispatch a LOGIN notice.
|
||||
*/
|
||||
|
||||
Code_t
|
||||
ulogin_dispatch(ZNotice_t *notice,
|
||||
int auth,
|
||||
struct sockaddr_in *who,
|
||||
Server *server)
|
||||
{
|
||||
Exposure_type retval;
|
||||
int err_ret;
|
||||
|
||||
if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
|
||||
retval = ulogin_remove_user(notice, who, &err_ret);
|
||||
switch (retval) {
|
||||
case NONE:
|
||||
if (err_ret == UNAUTH) {
|
||||
if (server == me_server)
|
||||
clt_ack(notice, who, AUTH_FAILED);
|
||||
return ZERR_NONE;
|
||||
} else if (err_ret == NOLOC) {
|
||||
if (server == me_server)
|
||||
clt_ack(notice, who, NOT_FOUND);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
syslog(LOG_ERR,"bogus location exposure NONE, %s",
|
||||
notice->z_sender);
|
||||
break;
|
||||
case OPSTAFF_VIS:
|
||||
case REALM_VIS:
|
||||
/* he is not announced to people. Silently ack */
|
||||
if (server == me_server)
|
||||
ack(notice, who);
|
||||
break;
|
||||
case REALM_ANN:
|
||||
case NET_VIS:
|
||||
if (server == me_server)
|
||||
sendit(notice, 1, who, 0);
|
||||
break;
|
||||
case NET_ANN:
|
||||
/* currently no distinction between these.
|
||||
just announce */
|
||||
/* we assume that if this user is at a certain
|
||||
IP address, we can trust the logout to be
|
||||
authentic. ulogin_remove_user checks the
|
||||
ip addrs */
|
||||
if (server == me_server)
|
||||
sendit(notice, 1, who, 1);
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR,"bogus location exposure %d/%s",
|
||||
(int) retval, notice->z_sender);
|
||||
break;
|
||||
}
|
||||
if (server == me_server) /* tell the other servers */
|
||||
server_forward(notice, auth, who);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
if (!bdumping &&
|
||||
(!auth || strcmp(notice->z_sender, notice->z_class_inst) != 0) &&
|
||||
(!auth || !opstaff_check(notice->z_sender))) {
|
||||
zdbug((LOG_DEBUG,"unauthentic ulogin: %d %s %s", auth,
|
||||
notice->z_sender, notice->z_class_inst));
|
||||
if (server == me_server)
|
||||
clt_ack(notice, who, AUTH_FAILED);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
if (strcmp(notice->z_opcode, LOGIN_USER_FLUSH) == 0) {
|
||||
ulogin_flush_user(notice);
|
||||
if (server == me_server)
|
||||
ack(notice, who);
|
||||
} else if (strcmp(notice->z_opcode, EXPOSE_NONE) == 0) {
|
||||
ulogin_remove_user(notice, who, &err_ret);
|
||||
if (err_ret == UNAUTH) {
|
||||
if (server == me_server)
|
||||
clt_ack(notice, who, AUTH_FAILED);
|
||||
return ZERR_NONE;
|
||||
} else if (err_ret == NOLOC) {
|
||||
if (server == me_server)
|
||||
clt_ack(notice, who, NOT_FOUND);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
if (server == me_server) {
|
||||
ack(notice, who);
|
||||
server_forward(notice, auth, who);
|
||||
}
|
||||
return ZERR_NONE;
|
||||
} else if (strcmp(notice->z_opcode, EXPOSE_OPSTAFF) == 0) {
|
||||
err_ret = ulogin_add_user(notice, OPSTAFF_VIS, who);
|
||||
if (server == me_server) {
|
||||
if (err_ret)
|
||||
nack(notice, who);
|
||||
else
|
||||
ack(notice, who);
|
||||
}
|
||||
} else if (strcmp(notice->z_opcode, EXPOSE_REALMVIS) == 0) {
|
||||
err_ret = ulogin_add_user(notice, REALM_VIS, who);
|
||||
if (server == me_server) { /* realm vis is not broadcast,
|
||||
so we ack it here */
|
||||
if (err_ret)
|
||||
nack(notice, who);
|
||||
else
|
||||
ack(notice, who);
|
||||
}
|
||||
} else if (!strcmp(notice->z_opcode, EXPOSE_REALMANN)) {
|
||||
err_ret = ulogin_add_user(notice, REALM_ANN, who);
|
||||
if (server == me_server) { /* announce to the realm */
|
||||
if (err_ret)
|
||||
nack(notice, who);
|
||||
else
|
||||
login_sendit(notice, auth, who, 0);
|
||||
}
|
||||
} else if (!strcmp(notice->z_opcode, EXPOSE_NETVIS)) {
|
||||
err_ret = ulogin_add_user(notice, NET_VIS, who);
|
||||
if (server == me_server) { /* announce to the realm */
|
||||
if (err_ret)
|
||||
nack(notice, who);
|
||||
else
|
||||
login_sendit(notice, auth, who, 0);
|
||||
}
|
||||
} else if (!strcmp(notice->z_opcode, EXPOSE_NETANN)) {
|
||||
err_ret = ulogin_add_user(notice, NET_ANN, who);
|
||||
if (server == me_server) { /* tell the world */
|
||||
if (err_ret)
|
||||
nack(notice, who);
|
||||
else
|
||||
login_sendit(notice, auth, who, 1);
|
||||
}
|
||||
} else {
|
||||
if (!strcmp(notice->z_opcode, LOGIN_USER_LOGIN)) {
|
||||
zdbug((LOG_DEBUG, "ulog opcode from unknown foreign realm %s",
|
||||
notice->z_opcode));
|
||||
} else {
|
||||
syslog(LOG_ERR, "unknown ulog opcode %s", notice->z_opcode);
|
||||
}
|
||||
if (server == me_server)
|
||||
nack(notice, who);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
if (server == me_server)
|
||||
server_forward(notice, auth, who);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
login_sendit(ZNotice_t *notice,
|
||||
int auth,
|
||||
struct sockaddr_in *who,
|
||||
int external)
|
||||
{
|
||||
ZNotice_t log_notice;
|
||||
|
||||
/* we must copy the notice struct here because we need the original
|
||||
for forwarding. We needn't copy the private data of the notice,
|
||||
since that isn't modified by sendit and its subroutines. */
|
||||
|
||||
log_notice = *notice;
|
||||
|
||||
log_notice.z_opcode = LOGIN_USER_LOGIN;
|
||||
sendit(&log_notice, auth, who, external);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dispatch a LOCATE notice.
|
||||
*/
|
||||
Code_t
|
||||
ulocate_dispatch(ZNotice_t *notice,
|
||||
int auth,
|
||||
struct sockaddr_in *who,
|
||||
Server *server)
|
||||
{
|
||||
char *cp;
|
||||
ZRealm *realm;
|
||||
|
||||
if (!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
|
||||
/* we are talking to a current-rev client; send an ack */
|
||||
ack(notice, who);
|
||||
cp = strchr(notice->z_class_inst, '@');
|
||||
if (cp && (realm = realm_get_realm_by_name(cp + 1)))
|
||||
ulogin_locate_forward(notice, who, realm);
|
||||
else
|
||||
ulogin_locate(notice, who, auth);
|
||||
return ZERR_NONE;
|
||||
} else {
|
||||
syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
|
||||
if (server == me_server)
|
||||
nack(notice, who);
|
||||
return ZERR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* reallocate locations
|
||||
*/
|
||||
static int
|
||||
reallocate_locations(int new_num) {
|
||||
Location *new_loc;
|
||||
|
||||
if (new_num) {
|
||||
new_loc = realloc(locations, new_num * sizeof(Location));
|
||||
if (new_loc == NULL) {
|
||||
syslog(LOG_ERR, "failed to resize uloc table: %m");
|
||||
if (new_num < num_locs)
|
||||
num_locs = new_num;
|
||||
return errno;
|
||||
}
|
||||
locations = new_loc;
|
||||
} else {
|
||||
free(locations);
|
||||
locations = NULL;
|
||||
}
|
||||
|
||||
num_locs = new_num;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush all locations at the address.
|
||||
*/
|
||||
|
||||
void
|
||||
uloc_hflush(struct in_addr *addr)
|
||||
{
|
||||
int i, new_num = 0;
|
||||
|
||||
if (num_locs == 0)
|
||||
return; /* none to flush */
|
||||
|
||||
for (i = 0; i < num_locs; i++)
|
||||
if (locations[i].addr.sin_addr.s_addr != addr->s_addr)
|
||||
locations[new_num++] = locations[i];
|
||||
else
|
||||
free_loc(&locations[i]);
|
||||
|
||||
reallocate_locations(new_num);
|
||||
}
|
||||
|
||||
void
|
||||
uloc_flush_client(struct sockaddr_in *sin)
|
||||
{
|
||||
int i, new_num = 0;
|
||||
|
||||
if (num_locs == 0)
|
||||
return; /* none to flush */
|
||||
|
||||
for (i = 0; i < num_locs; i++)
|
||||
if ((locations[i].addr.sin_addr.s_addr != sin->sin_addr.s_addr)
|
||||
|| (locations[i].addr.sin_port != sin->sin_port))
|
||||
locations[new_num++] = locations[i];
|
||||
else
|
||||
free_loc(&locations[i]);
|
||||
|
||||
num_locs = new_num;
|
||||
|
||||
reallocate_locations(new_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the locations for host for a brain dump
|
||||
*/
|
||||
|
||||
/*ARGSUSED*/
|
||||
Code_t
|
||||
uloc_send_locations(void)
|
||||
{
|
||||
Location *loc;
|
||||
int i;
|
||||
char *lyst[NUM_FIELDS];
|
||||
char *exposure_level;
|
||||
Code_t retval;
|
||||
|
||||
for (i = 0, loc = locations; i < num_locs; i++, loc++) {
|
||||
lyst[0] = (char *) loc->machine->string;
|
||||
lyst[1] = (char *) loc->time;
|
||||
lyst[2] = (char *) loc->tty->string;
|
||||
|
||||
switch (loc->exposure) {
|
||||
case OPSTAFF_VIS:
|
||||
exposure_level = EXPOSE_OPSTAFF;
|
||||
break;
|
||||
case REALM_VIS:
|
||||
exposure_level = EXPOSE_REALMVIS;
|
||||
break;
|
||||
case REALM_ANN:
|
||||
exposure_level = EXPOSE_REALMANN;
|
||||
break;
|
||||
case NET_VIS:
|
||||
exposure_level = EXPOSE_NETVIS;
|
||||
break;
|
||||
case NET_ANN:
|
||||
exposure_level = EXPOSE_NETANN;
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR,"broken location state %s/%d",
|
||||
loc->user->string, (int) loc->exposure);
|
||||
exposure_level = EXPOSE_OPSTAFF;
|
||||
break;
|
||||
}
|
||||
retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
|
||||
loc->user->string, exposure_level, myname,
|
||||
"", lyst, NUM_FIELDS);
|
||||
if (retval != ZERR_NONE) {
|
||||
syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
return ZERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the user to the internal table of locations.
|
||||
*/
|
||||
|
||||
int
|
||||
ulogin_add_user(ZNotice_t *notice,
|
||||
Exposure_type exposure,
|
||||
struct sockaddr_in *who)
|
||||
{
|
||||
Location newloc;
|
||||
int i, here;
|
||||
|
||||
here = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
|
||||
if (here >= 0) {
|
||||
/* Update the time, tty, and exposure on the existing location. */
|
||||
locations[here].exposure = exposure;
|
||||
if (ulogin_parse(notice, &newloc) == 0) {
|
||||
free_string(locations[here].tty);
|
||||
locations[here].tty = dup_string(newloc.tty);
|
||||
free(locations[here].time);
|
||||
locations[here].time = strsave(newloc.time);
|
||||
free_loc(&newloc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ulogin_setup(notice, &newloc, exposure, who))
|
||||
return 1;
|
||||
|
||||
if(reallocate_locations(num_locs + 1))
|
||||
return 1;
|
||||
|
||||
/* skip old locs */
|
||||
for (i = 0;
|
||||
i < num_locs-1 && comp_string(locations[i].user, newloc.user) < 0;
|
||||
i++)
|
||||
;
|
||||
|
||||
here = i;
|
||||
|
||||
/* copy the rest */
|
||||
for(i = num_locs - 1; i > here; i--)
|
||||
locations[i] = locations[i - 1];
|
||||
|
||||
locations[here] = newloc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the location locs with the information in the notice.
|
||||
*/
|
||||
|
||||
static int
|
||||
ulogin_setup(ZNotice_t *notice,
|
||||
Location *locs,
|
||||
Exposure_type exposure,
|
||||
struct sockaddr_in *who)
|
||||
{
|
||||
if (ulogin_parse(notice, locs))
|
||||
return 1;
|
||||
|
||||
locs->exposure = exposure;
|
||||
locs->addr.sin_family = AF_INET;
|
||||
locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
|
||||
locs->addr.sin_port = notice->z_port;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the location information in the notice, and fill it into *locs
|
||||
*/
|
||||
|
||||
static int
|
||||
ulogin_parse(ZNotice_t *notice,
|
||||
Location *locs)
|
||||
{
|
||||
char *cp, *base;
|
||||
int nulls = 0;
|
||||
|
||||
if (!notice->z_message_len) {
|
||||
syslog(LOG_ERR, "short ulogin");
|
||||
return 1;
|
||||
}
|
||||
|
||||
base = notice->z_message;
|
||||
for (cp = base; cp < base + notice->z_message_len; cp++) {
|
||||
if (!*cp)
|
||||
nulls++;
|
||||
}
|
||||
if (nulls < 3) {
|
||||
syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
|
||||
notice->z_sender, nulls);
|
||||
return 1;
|
||||
}
|
||||
|
||||
locs->user = make_string(notice->z_class_inst,0);
|
||||
|
||||
cp = base;
|
||||
locs->machine = make_string(cp,0);
|
||||
|
||||
cp += (strlen(cp) + 1);
|
||||
locs->time = strsave(cp);
|
||||
|
||||
/* This field might not be null-terminated */
|
||||
cp += (strlen(cp) + 1);
|
||||
locs->tty = make_string(cp, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ulogin_find(char *user,
|
||||
struct in_addr *host,
|
||||
unsigned int port)
|
||||
{
|
||||
int i;
|
||||
String *str;
|
||||
|
||||
/* Find the first location for this user. */
|
||||
i = ulogin_find_user(user);
|
||||
if (i == -1)
|
||||
return -1;
|
||||
|
||||
/* Look for a location which matches the host and port. */
|
||||
str = make_string(user, 0);
|
||||
while (i < num_locs && locations[i].user == str) {
|
||||
if (locations[i].addr.sin_addr.s_addr == host->s_addr
|
||||
&& locations[i].addr.sin_port == port) {
|
||||
free_string(str);
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
free_string(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the table index of the first instance of this user@realm in the
|
||||
* table.
|
||||
*/
|
||||
int
|
||||
ulogin_find_user(char *user)
|
||||
{
|
||||
int i, rlo, rhi;
|
||||
int compar;
|
||||
String *str;
|
||||
|
||||
if (!locations)
|
||||
return -1;
|
||||
|
||||
str = make_string(user, 0);
|
||||
|
||||
/* i is the current midpoint location, rlo is the lowest we will
|
||||
* still check, and rhi is the highest we will still check. */
|
||||
|
||||
i = num_locs / 2;
|
||||
rlo = 0;
|
||||
rhi = num_locs - 1;
|
||||
|
||||
while ((compar = comp_string(locations[i].user, str)) != 0) {
|
||||
if (compar < 0)
|
||||
rlo = i + 1;
|
||||
else
|
||||
rhi = i - 1;
|
||||
if (rhi - rlo < 0) {
|
||||
free_string(str);
|
||||
return -1;
|
||||
}
|
||||
i = (rhi + rlo) / 2;
|
||||
}
|
||||
|
||||
/* Back up to the first location for this user. */
|
||||
while (i > 0 && locations[i - 1].user == str)
|
||||
i--;
|
||||
free_string(str);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove the user specified in notice from the internal table
|
||||
*/
|
||||
|
||||
Exposure_type
|
||||
ulogin_remove_user(ZNotice_t *notice,
|
||||
struct sockaddr_in *who,
|
||||
int *err_return)
|
||||
{
|
||||
int here, i;
|
||||
Exposure_type quiet;
|
||||
|
||||
*err_return = 0;
|
||||
here = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
|
||||
if (here == -1) {
|
||||
*err_return = NOLOC;
|
||||
return NONE;
|
||||
}
|
||||
|
||||
quiet = locations[here].exposure;
|
||||
|
||||
/* free up this one */
|
||||
free_loc(&locations[here]);
|
||||
|
||||
for(i = here; i < num_locs - 1; i++)
|
||||
locations[i] = locations[i + 1];
|
||||
|
||||
reallocate_locations(num_locs - 1);
|
||||
|
||||
/* all done */
|
||||
return quiet;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove all locs of the user specified in notice from the internal table
|
||||
*/
|
||||
|
||||
void
|
||||
ulogin_flush_user(ZNotice_t *notice)
|
||||
{
|
||||
int here, i, j, num_match, num_left;
|
||||
|
||||
i = num_match = num_left = 0;
|
||||
|
||||
here = ulogin_find_user(notice->z_class_inst);
|
||||
if (here == -1)
|
||||
return;
|
||||
|
||||
num_left = num_locs - here;
|
||||
|
||||
while (num_left &&
|
||||
!strcasecmp(locations[here + num_match].user->string,
|
||||
notice->z_class_inst)) {
|
||||
/* as long as we keep matching, march up the list */
|
||||
num_match++;
|
||||
num_left--;
|
||||
}
|
||||
|
||||
for (j = 0; j < num_match; j++)
|
||||
free_loc(&locations[here + j]);
|
||||
|
||||
/* copy the rest */
|
||||
for (i = here; i < num_locs - num_match; i++)
|
||||
locations[i] = locations[i + 1];
|
||||
|
||||
reallocate_locations(num_locs - num_match);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ulogin_locate(ZNotice_t *notice,
|
||||
struct sockaddr_in *who,
|
||||
int auth)
|
||||
{
|
||||
char **answer;
|
||||
int found;
|
||||
Code_t retval;
|
||||
struct sockaddr_in send_to_who;
|
||||
|
||||
answer = ulogin_marshal_locs(notice, &found, auth);
|
||||
|
||||
send_to_who = *who;
|
||||
send_to_who.sin_port = notice->z_port;
|
||||
|
||||
retval = ZSetDestAddr(&send_to_who);
|
||||
if (retval != ZERR_NONE) {
|
||||
syslog(LOG_WARNING, "ulogin_locate set addr: %s",
|
||||
error_message(retval));
|
||||
if (answer)
|
||||
free(answer);
|
||||
return;
|
||||
}
|
||||
|
||||
notice->z_kind = ACKED;
|
||||
|
||||
/* use xmit_frag() to send each piece of the notice */
|
||||
|
||||
retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
|
||||
if (retval != ZERR_NONE)
|
||||
syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
|
||||
if (answer)
|
||||
free(answer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate the user and collect the locations into an array. Return the # of
|
||||
* locations in *found.
|
||||
*/
|
||||
|
||||
static char **
|
||||
ulogin_marshal_locs(ZNotice_t *notice,
|
||||
int *found,
|
||||
int auth)
|
||||
{
|
||||
Location **matches = (Location **) 0;
|
||||
char **answer;
|
||||
int i = 0;
|
||||
String *inst;
|
||||
int local = (auth && realm_sender_in_realm(ZGetRealm(), notice->z_sender));
|
||||
int opstaff = (auth && opstaff_check(notice->z_sender));
|
||||
|
||||
*found = 0; /* # of matches */
|
||||
|
||||
i = ulogin_find_user(notice->z_class_inst);
|
||||
if (i == -1)
|
||||
return NULL;
|
||||
|
||||
inst = make_string(notice->z_class_inst,0);
|
||||
while (i < num_locs && (inst == locations[i].user)) {
|
||||
/* these locations match */
|
||||
switch (locations[i].exposure) {
|
||||
case OPSTAFF_VIS:
|
||||
if (!opstaff) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
case REALM_VIS:
|
||||
case REALM_ANN:
|
||||
if (!local) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
case NET_VIS:
|
||||
case NET_ANN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!*found) {
|
||||
matches = (Location **) malloc(sizeof(Location *));
|
||||
if (!matches) {
|
||||
syslog(LOG_ERR, "ulog_loc: no mem");
|
||||
break; /* from the while */
|
||||
}
|
||||
matches[0] = &locations[i];
|
||||
(*found)++;
|
||||
} else {
|
||||
matches = (Location **) realloc(matches,
|
||||
++(*found) * sizeof(Location *));
|
||||
if (!matches) {
|
||||
syslog(LOG_ERR, "ulog_loc: realloc no mem");
|
||||
*found = 0;
|
||||
break; /* from the while */
|
||||
}
|
||||
matches[*found - 1] = &locations[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
free_string(inst);
|
||||
|
||||
/* OK, now we have a list of user@host's to return to the client
|
||||
in matches */
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
if (zdebug) {
|
||||
for (i = 0; i < *found ; i++)
|
||||
zdbug((LOG_DEBUG,"found %s",
|
||||
matches[i]->user->string));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* coalesce the location information into a list of char *'s */
|
||||
answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
|
||||
if (!answer) {
|
||||
syslog(LOG_ERR, "zloc no mem(answer)");
|
||||
*found = 0;
|
||||
} else
|
||||
for (i = 0; i < *found ; i++) {
|
||||
answer[i * NUM_FIELDS] = matches[i]->machine->string;
|
||||
answer[i * NUM_FIELDS + 1] = matches[i]->time;
|
||||
answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
|
||||
}
|
||||
|
||||
if (matches)
|
||||
free(matches);
|
||||
return answer;
|
||||
}
|
||||
|
||||
void
|
||||
uloc_dump_locs(FILE *fp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_locs; i++) {
|
||||
fputs("'", fp);
|
||||
dump_quote(locations[i].user->string, fp);
|
||||
fputs("' '", fp);
|
||||
dump_quote(locations[i].machine->string, fp);
|
||||
fputs("' '", fp);
|
||||
dump_quote(locations[i].time, fp);
|
||||
fputs("' '", fp);
|
||||
dump_quote(locations[i].tty->string, fp);
|
||||
fputs("' ", fp);
|
||||
switch (locations[i].exposure) {
|
||||
case OPSTAFF_VIS:
|
||||
fputs("OPSTAFF", fp);
|
||||
break;
|
||||
case REALM_VIS:
|
||||
fputs("RLM_VIS", fp);
|
||||
break;
|
||||
case REALM_ANN:
|
||||
fputs("RLM_ANN", fp);
|
||||
break;
|
||||
case NET_VIS:
|
||||
fputs("NET_VIS", fp);
|
||||
break;
|
||||
case NET_ANN:
|
||||
fputs("NET_ANN", fp);
|
||||
break;
|
||||
default:
|
||||
fprintf(fp, "? %d ?", locations[i].exposure);
|
||||
break;
|
||||
}
|
||||
fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
|
||||
ntohs(locations[i].addr.sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_loc(Location *loc)
|
||||
{
|
||||
free_string(loc->user);
|
||||
free_string(loc->machine);
|
||||
free_string(loc->tty);
|
||||
free(loc->time);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
ulogin_locate_forward(ZNotice_t *notice,
|
||||
struct sockaddr_in *who,
|
||||
ZRealm *realm)
|
||||
{
|
||||
ZNotice_t lnotice;
|
||||
|
||||
lnotice = *notice;
|
||||
lnotice.z_opcode = REALM_REQ_LOCATE;
|
||||
|
||||
realm_handoff(&lnotice, 1, who, realm, 0);
|
||||
}
|
||||
|
||||
void
|
||||
ulogin_realm_locate(ZNotice_t *notice,
|
||||
struct sockaddr_in *who,
|
||||
ZRealm *realm)
|
||||
{
|
||||
char **answer;
|
||||
int found;
|
||||
Code_t retval;
|
||||
ZNotice_t lnotice;
|
||||
char *pack;
|
||||
int packlen;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (zdebug)
|
||||
zdbug((LOG_DEBUG, "ulogin_realm_locate"));
|
||||
#endif
|
||||
|
||||
answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
|
||||
|
||||
lnotice = *notice;
|
||||
lnotice.z_opcode = REALM_ANS_LOCATE;
|
||||
|
||||
if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
|
||||
syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
|
||||
error_message(retval));
|
||||
|
||||
if (answer)
|
||||
free(answer);
|
||||
return;
|
||||
}
|
||||
if (answer)
|
||||
free(answer);
|
||||
|
||||
if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
|
||||
syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
|
||||
error_message(retval));
|
||||
free(pack);
|
||||
return;
|
||||
}
|
||||
|
||||
realm_handoff(&lnotice, 1, who, realm, 0);
|
||||
free(pack);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
ulogin_relay_locate(ZNotice_t *notice,
|
||||
struct sockaddr_in *who)
|
||||
{
|
||||
ZNotice_t lnotice;
|
||||
Code_t retval;
|
||||
struct sockaddr_in newwho;
|
||||
char *pack;
|
||||
int packlen;
|
||||
|
||||
notice_extract_address(notice, &newwho);
|
||||
|
||||
if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
|
||||
syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
|
||||
error_message(retval));
|
||||
return;
|
||||
}
|
||||
|
||||
lnotice = *notice;
|
||||
lnotice.z_opcode = LOCATE_LOCATE;
|
||||
lnotice.z_kind = ACKED;
|
||||
lnotice.z_auth = 0;
|
||||
lnotice.z_authent_len = 0;
|
||||
lnotice.z_ascii_authent = "";
|
||||
lnotice.z_checksum = 0;
|
||||
lnotice.z_ascii_checksum = "";
|
||||
|
||||
if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
|
||||
syslog(LOG_WARNING, "ulog_relay_loc format: %s",
|
||||
error_message(retval));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
|
||||
syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
|
||||
error_message(retval));
|
||||
}
|
||||
free(pack);
|
||||
}
|
585
server/utf8proc.c
Normal file
585
server/utf8proc.c
Normal file
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2007 Jan Behrens, FlexiGuided GmbH, Berlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This library contains derived data from a modified version of the
|
||||
* Unicode data files.
|
||||
*
|
||||
* The original data files are available at
|
||||
* http://www.unicode.org/Public/UNIDATA/
|
||||
*
|
||||
* Please notice the copyright statement in the file "utf8proc_data.c".
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* File name: utf8proc.c
|
||||
* Version: 1.1.1
|
||||
* Last changed: 2007-07-22
|
||||
*
|
||||
* Description:
|
||||
* Implementation of libutf8proc.
|
||||
*/
|
||||
|
||||
|
||||
#include "utf8proc.h"
|
||||
#include "utf8proc_data.c"
|
||||
|
||||
|
||||
const int8_t utf8proc_utf8class[256] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
#define UTF8PROC_HANGUL_SBASE 0xAC00
|
||||
#define UTF8PROC_HANGUL_LBASE 0x1100
|
||||
#define UTF8PROC_HANGUL_VBASE 0x1161
|
||||
#define UTF8PROC_HANGUL_TBASE 0x11A7
|
||||
#define UTF8PROC_HANGUL_LCOUNT 19
|
||||
#define UTF8PROC_HANGUL_VCOUNT 21
|
||||
#define UTF8PROC_HANGUL_TCOUNT 28
|
||||
#define UTF8PROC_HANGUL_NCOUNT 588
|
||||
#define UTF8PROC_HANGUL_SCOUNT 11172
|
||||
// END is exclusive
|
||||
#define UTF8PROC_HANGUL_L_START 0x1100
|
||||
#define UTF8PROC_HANGUL_L_END 0x115A
|
||||
#define UTF8PROC_HANGUL_L_FILLER 0x115F
|
||||
#define UTF8PROC_HANGUL_V_START 0x1160
|
||||
#define UTF8PROC_HANGUL_V_END 0x11A3
|
||||
#define UTF8PROC_HANGUL_T_START 0x11A8
|
||||
#define UTF8PROC_HANGUL_T_END 0x11FA
|
||||
#define UTF8PROC_HANGUL_S_START 0xAC00
|
||||
#define UTF8PROC_HANGUL_S_END 0xD7A4
|
||||
|
||||
|
||||
#define UTF8PROC_BOUNDCLASS_START 0
|
||||
#define UTF8PROC_BOUNDCLASS_OTHER 1
|
||||
#define UTF8PROC_BOUNDCLASS_CR 2
|
||||
#define UTF8PROC_BOUNDCLASS_LF 3
|
||||
#define UTF8PROC_BOUNDCLASS_CONTROL 4
|
||||
#define UTF8PROC_BOUNDCLASS_EXTEND 5
|
||||
#define UTF8PROC_BOUNDCLASS_L 6
|
||||
#define UTF8PROC_BOUNDCLASS_V 7
|
||||
#define UTF8PROC_BOUNDCLASS_T 8
|
||||
#define UTF8PROC_BOUNDCLASS_LV 9
|
||||
#define UTF8PROC_BOUNDCLASS_LVT 10
|
||||
|
||||
|
||||
const char *utf8proc_errmsg(ssize_t errcode) {
|
||||
switch (errcode) {
|
||||
case UTF8PROC_ERROR_NOMEM:
|
||||
return "Memory for processing UTF-8 data could not be allocated.";
|
||||
case UTF8PROC_ERROR_OVERFLOW:
|
||||
return "UTF-8 string is too long to be processed.";
|
||||
case UTF8PROC_ERROR_INVALIDUTF8:
|
||||
return "Invalid UTF-8 string";
|
||||
case UTF8PROC_ERROR_NOTASSIGNED:
|
||||
return "Unassigned Unicode code point found in UTF-8 string.";
|
||||
case UTF8PROC_ERROR_INVALIDOPTS:
|
||||
return "Invalid options for UTF-8 processing chosen.";
|
||||
default:
|
||||
return "An unknown error occured while processing UTF-8 data.";
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t utf8proc_iterate(
|
||||
const uint8_t *str, ssize_t strlen, int32_t *dst
|
||||
) {
|
||||
int length;
|
||||
int i;
|
||||
int32_t uc = -1;
|
||||
*dst = -1;
|
||||
if (!strlen) return 0;
|
||||
length = utf8proc_utf8class[str[0]];
|
||||
if (!length) return UTF8PROC_ERROR_INVALIDUTF8;
|
||||
if (strlen >= 0 && length > strlen) return UTF8PROC_ERROR_INVALIDUTF8;
|
||||
for (i=1; i<length; i++) {
|
||||
if ((str[i] & 0xC0) != 0x80) return UTF8PROC_ERROR_INVALIDUTF8;
|
||||
}
|
||||
switch (length) {
|
||||
case 1:
|
||||
uc = str[0];
|
||||
break;
|
||||
case 2:
|
||||
uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
|
||||
if (uc < 0x80) uc = -1;
|
||||
break;
|
||||
case 3:
|
||||
uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
|
||||
+ (str[2] & 0x3F);
|
||||
if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
|
||||
(uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
|
||||
break;
|
||||
case 4:
|
||||
uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
|
||||
+ ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
|
||||
if (uc < 0x10000 || uc >= 0x110000) uc = -1;
|
||||
break;
|
||||
}
|
||||
if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
|
||||
return UTF8PROC_ERROR_INVALIDUTF8;
|
||||
*dst = uc;
|
||||
return length;
|
||||
}
|
||||
|
||||
bool utf8proc_codepoint_valid(int32_t uc) {
|
||||
if (uc < 0 || uc >= 0x110000 ||
|
||||
((uc & 0xFFFF) >= 0xFFFE) || (uc >= 0xD800 && uc < 0xE000) ||
|
||||
(uc >= 0xFDD0 && uc < 0xFDF0)) return false;
|
||||
else return true;
|
||||
}
|
||||
|
||||
ssize_t utf8proc_encode_char(int32_t uc, uint8_t *dst) {
|
||||
if (uc < 0x00) {
|
||||
return 0;
|
||||
} else if (uc < 0x80) {
|
||||
dst[0] = uc;
|
||||
return 1;
|
||||
} else if (uc < 0x800) {
|
||||
dst[0] = 0xC0 + (uc >> 6);
|
||||
dst[1] = 0x80 + (uc & 0x3F);
|
||||
return 2;
|
||||
} else if (uc == 0xFFFF) {
|
||||
dst[0] = 0xFF;
|
||||
return 1;
|
||||
} else if (uc == 0xFFFE) {
|
||||
dst[0] = 0xFE;
|
||||
return 1;
|
||||
} else if (uc < 0x10000) {
|
||||
dst[0] = 0xE0 + (uc >> 12);
|
||||
dst[1] = 0x80 + ((uc >> 6) & 0x3F);
|
||||
dst[2] = 0x80 + (uc & 0x3F);
|
||||
return 3;
|
||||
} else if (uc < 0x110000) {
|
||||
dst[0] = 0xF0 + (uc >> 18);
|
||||
dst[1] = 0x80 + ((uc >> 12) & 0x3F);
|
||||
dst[2] = 0x80 + ((uc >> 6) & 0x3F);
|
||||
dst[3] = 0x80 + (uc & 0x3F);
|
||||
return 4;
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
const utf8proc_property_t *utf8proc_get_property(int32_t uc) {
|
||||
// ASSERT: uc >= 0 && uc < 0x110000
|
||||
return utf8proc_properties + (
|
||||
utf8proc_stage2table[
|
||||
utf8proc_stage1table[uc >> 8] + (uc & 0xFF)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#define utf8proc_decompose_lump(replacement_uc) \
|
||||
return utf8proc_decompose_char((replacement_uc), dst, bufsize, \
|
||||
options & ~UTF8PROC_LUMP, last_boundclass)
|
||||
|
||||
ssize_t utf8proc_decompose_char(int32_t uc, int32_t *dst, ssize_t bufsize,
|
||||
int options, int *last_boundclass) {
|
||||
// ASSERT: uc >= 0 && uc < 0x110000
|
||||
const utf8proc_property_t *property;
|
||||
utf8proc_propval_t category;
|
||||
int32_t hangul_sindex;
|
||||
property = utf8proc_get_property(uc);
|
||||
category = property->category;
|
||||
hangul_sindex = uc - UTF8PROC_HANGUL_SBASE;
|
||||
if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) {
|
||||
if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT) {
|
||||
int32_t hangul_tindex;
|
||||
if (bufsize >= 1) {
|
||||
dst[0] = UTF8PROC_HANGUL_LBASE +
|
||||
hangul_sindex / UTF8PROC_HANGUL_NCOUNT;
|
||||
if (bufsize >= 2) dst[1] = UTF8PROC_HANGUL_VBASE +
|
||||
(hangul_sindex % UTF8PROC_HANGUL_NCOUNT) / UTF8PROC_HANGUL_TCOUNT;
|
||||
}
|
||||
hangul_tindex = hangul_sindex % UTF8PROC_HANGUL_TCOUNT;
|
||||
if (!hangul_tindex) return 2;
|
||||
if (bufsize >= 3) dst[2] = UTF8PROC_HANGUL_TBASE + hangul_tindex;
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
if (options & UTF8PROC_REJECTNA) {
|
||||
if (!category) return UTF8PROC_ERROR_NOTASSIGNED;
|
||||
}
|
||||
if (options & UTF8PROC_IGNORE) {
|
||||
if (property->ignorable) return 0;
|
||||
}
|
||||
if (options & UTF8PROC_LUMP) {
|
||||
if (category == UTF8PROC_CATEGORY_ZS) utf8proc_decompose_lump(0x0020);
|
||||
if (uc == 0x2018 || uc == 0x2019 || uc == 0x02BC || uc == 0x02C8)
|
||||
utf8proc_decompose_lump(0x0027);
|
||||
if (category == UTF8PROC_CATEGORY_PD || uc == 0x2212)
|
||||
utf8proc_decompose_lump(0x002D);
|
||||
if (uc == 0x2044 || uc == 0x2215) utf8proc_decompose_lump(0x002F);
|
||||
if (uc == 0x2236) utf8proc_decompose_lump(0x003A);
|
||||
if (uc == 0x2039 || uc == 0x2329 || uc == 0x3008)
|
||||
utf8proc_decompose_lump(0x003C);
|
||||
if (uc == 0x203A || uc == 0x232A || uc == 0x3009)
|
||||
utf8proc_decompose_lump(0x003E);
|
||||
if (uc == 0x2216) utf8proc_decompose_lump(0x005C);
|
||||
if (uc == 0x02C4 || uc == 0x02C6 || uc == 0x2038 || uc == 0x2303)
|
||||
utf8proc_decompose_lump(0x005E);
|
||||
if (category == UTF8PROC_CATEGORY_PC || uc == 0x02CD)
|
||||
utf8proc_decompose_lump(0x005F);
|
||||
if (uc == 0x02CB) utf8proc_decompose_lump(0x0060);
|
||||
if (uc == 0x2223) utf8proc_decompose_lump(0x007C);
|
||||
if (uc == 0x223C) utf8proc_decompose_lump(0x007E);
|
||||
if ((options & UTF8PROC_NLF2LS) && (options & UTF8PROC_NLF2PS)) {
|
||||
if (category == UTF8PROC_CATEGORY_ZL ||
|
||||
category == UTF8PROC_CATEGORY_ZP)
|
||||
utf8proc_decompose_lump(0x000A);
|
||||
}
|
||||
}
|
||||
if (options & UTF8PROC_STRIPMARK) {
|
||||
if (category == UTF8PROC_CATEGORY_MN ||
|
||||
category == UTF8PROC_CATEGORY_MC ||
|
||||
category == UTF8PROC_CATEGORY_ME) return 0;
|
||||
}
|
||||
if (options & UTF8PROC_CASEFOLD) {
|
||||
if (property->casefold_mapping) {
|
||||
const int32_t *casefold_entry;
|
||||
ssize_t written = 0;
|
||||
for (casefold_entry = property->casefold_mapping;
|
||||
*casefold_entry >= 0; casefold_entry++) {
|
||||
written += utf8proc_decompose_char(*casefold_entry, dst+written,
|
||||
(bufsize > written) ? (bufsize - written) : 0, options,
|
||||
last_boundclass);
|
||||
if (written < 0) return UTF8PROC_ERROR_OVERFLOW;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
}
|
||||
if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) {
|
||||
if (property->decomp_mapping &&
|
||||
(!property->decomp_type || (options & UTF8PROC_COMPAT))) {
|
||||
const int32_t *decomp_entry;
|
||||
ssize_t written = 0;
|
||||
for (decomp_entry = property->decomp_mapping;
|
||||
*decomp_entry >= 0; decomp_entry++) {
|
||||
written += utf8proc_decompose_char(*decomp_entry, dst+written,
|
||||
(bufsize > written) ? (bufsize - written) : 0, options,
|
||||
last_boundclass);
|
||||
if (written < 0) return UTF8PROC_ERROR_OVERFLOW;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
}
|
||||
if (options & UTF8PROC_CHARBOUND) {
|
||||
bool boundary;
|
||||
int tbc, lbc;
|
||||
tbc =
|
||||
(uc == 0x000D) ? UTF8PROC_BOUNDCLASS_CR :
|
||||
(uc == 0x000A) ? UTF8PROC_BOUNDCLASS_LF :
|
||||
((category == UTF8PROC_CATEGORY_ZL ||
|
||||
category == UTF8PROC_CATEGORY_ZP ||
|
||||
category == UTF8PROC_CATEGORY_CC ||
|
||||
category == UTF8PROC_CATEGORY_CF) &&
|
||||
!(uc == 0x200C || uc == 0x200D)) ? UTF8PROC_BOUNDCLASS_CONTROL :
|
||||
property->extend ? UTF8PROC_BOUNDCLASS_EXTEND :
|
||||
((uc >= UTF8PROC_HANGUL_L_START && uc < UTF8PROC_HANGUL_L_END) ||
|
||||
uc == UTF8PROC_HANGUL_L_FILLER) ? UTF8PROC_BOUNDCLASS_L :
|
||||
(uc >= UTF8PROC_HANGUL_V_START && uc < UTF8PROC_HANGUL_V_END) ?
|
||||
UTF8PROC_BOUNDCLASS_V :
|
||||
(uc >= UTF8PROC_HANGUL_T_START && uc < UTF8PROC_HANGUL_T_END) ?
|
||||
UTF8PROC_BOUNDCLASS_T :
|
||||
(uc >= UTF8PROC_HANGUL_S_START && uc < UTF8PROC_HANGUL_S_END) ? (
|
||||
((uc-UTF8PROC_HANGUL_SBASE) % UTF8PROC_HANGUL_TCOUNT == 0) ?
|
||||
UTF8PROC_BOUNDCLASS_LV : UTF8PROC_BOUNDCLASS_LVT
|
||||
) :
|
||||
UTF8PROC_BOUNDCLASS_OTHER;
|
||||
lbc = *last_boundclass;
|
||||
boundary =
|
||||
(tbc == UTF8PROC_BOUNDCLASS_EXTEND) ? false :
|
||||
(lbc == UTF8PROC_BOUNDCLASS_START) ? true :
|
||||
(lbc == UTF8PROC_BOUNDCLASS_CR &&
|
||||
tbc == UTF8PROC_BOUNDCLASS_LF) ? false :
|
||||
(lbc == UTF8PROC_BOUNDCLASS_CONTROL) ? true :
|
||||
(tbc == UTF8PROC_BOUNDCLASS_CONTROL) ? true :
|
||||
(lbc == UTF8PROC_BOUNDCLASS_L &&
|
||||
(tbc == UTF8PROC_BOUNDCLASS_L ||
|
||||
tbc == UTF8PROC_BOUNDCLASS_V ||
|
||||
tbc == UTF8PROC_BOUNDCLASS_LV ||
|
||||
tbc == UTF8PROC_BOUNDCLASS_LVT)) ? false :
|
||||
((lbc == UTF8PROC_BOUNDCLASS_LV ||
|
||||
lbc == UTF8PROC_BOUNDCLASS_V) &&
|
||||
(tbc == UTF8PROC_BOUNDCLASS_V ||
|
||||
tbc == UTF8PROC_BOUNDCLASS_T)) ? false :
|
||||
((lbc == UTF8PROC_BOUNDCLASS_LVT ||
|
||||
lbc == UTF8PROC_BOUNDCLASS_T) &&
|
||||
tbc == UTF8PROC_BOUNDCLASS_T) ? false :
|
||||
true;
|
||||
*last_boundclass = tbc;
|
||||
if (boundary) {
|
||||
if (bufsize >= 1) dst[0] = 0xFFFF;
|
||||
if (bufsize >= 2) dst[1] = uc;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
if (bufsize >= 1) *dst = uc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t utf8proc_decompose(
|
||||
const uint8_t *str, ssize_t strlen,
|
||||
int32_t *buffer, ssize_t bufsize, int options
|
||||
) {
|
||||
// strlen will be ignored, if UTF8PROC_NULLTERM is set in options
|
||||
ssize_t wpos = 0;
|
||||
if ((options & UTF8PROC_COMPOSE) && (options & UTF8PROC_DECOMPOSE))
|
||||
return UTF8PROC_ERROR_INVALIDOPTS;
|
||||
if ((options & UTF8PROC_STRIPMARK) &&
|
||||
!(options & UTF8PROC_COMPOSE) && !(options & UTF8PROC_DECOMPOSE))
|
||||
return UTF8PROC_ERROR_INVALIDOPTS;
|
||||
{
|
||||
int32_t uc;
|
||||
ssize_t rpos = 0;
|
||||
ssize_t decomp_result;
|
||||
int boundclass = UTF8PROC_BOUNDCLASS_START;
|
||||
while (1) {
|
||||
if (options & UTF8PROC_NULLTERM) {
|
||||
rpos += utf8proc_iterate(str + rpos, -1, &uc);
|
||||
// checking of return value is not neccessary,
|
||||
// as 'uc' is < 0 in case of error
|
||||
if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8;
|
||||
if (rpos < 0) return UTF8PROC_ERROR_OVERFLOW;
|
||||
if (uc == 0) break;
|
||||
} else {
|
||||
if (rpos >= strlen) break;
|
||||
rpos += utf8proc_iterate(str + rpos, strlen - rpos, &uc);
|
||||
if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8;
|
||||
}
|
||||
decomp_result = utf8proc_decompose_char(
|
||||
uc, buffer + wpos, (bufsize > wpos) ? (bufsize - wpos) : 0, options,
|
||||
&boundclass
|
||||
);
|
||||
if (decomp_result < 0) return decomp_result;
|
||||
wpos += decomp_result;
|
||||
// prohibiting integer overflows due to too long strings:
|
||||
if (wpos < 0 || wpos > (ssize_t)(SSIZE_MAX/sizeof(int32_t)/2))
|
||||
return UTF8PROC_ERROR_OVERFLOW;
|
||||
}
|
||||
}
|
||||
if ((options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) && bufsize >= wpos) {
|
||||
ssize_t pos = 0;
|
||||
while (pos < wpos-1) {
|
||||
int32_t uc1, uc2;
|
||||
const utf8proc_property_t *property1, *property2;
|
||||
uc1 = buffer[pos];
|
||||
uc2 = buffer[pos+1];
|
||||
property1 = utf8proc_get_property(uc1);
|
||||
property2 = utf8proc_get_property(uc2);
|
||||
if (property1->combining_class > property2->combining_class &&
|
||||
property2->combining_class > 0) {
|
||||
buffer[pos] = uc2;
|
||||
buffer[pos+1] = uc1;
|
||||
if (pos > 0) pos--; else pos++;
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return wpos;
|
||||
}
|
||||
|
||||
ssize_t utf8proc_reencode(int32_t *buffer, ssize_t length, int options) {
|
||||
// UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored
|
||||
// ASSERT: 'buffer' has one spare byte of free space at the end!
|
||||
if (options & (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS | UTF8PROC_STRIPCC)) {
|
||||
ssize_t rpos;
|
||||
ssize_t wpos = 0;
|
||||
int32_t uc;
|
||||
for (rpos = 0; rpos < length; rpos++) {
|
||||
uc = buffer[rpos];
|
||||
if (uc == 0x000D && rpos < length-1 && buffer[rpos+1] == 0x000A) rpos++;
|
||||
if (uc == 0x000A || uc == 0x000D || uc == 0x0085 ||
|
||||
((options & UTF8PROC_STRIPCC) && (uc == 0x000B || uc == 0x000C))) {
|
||||
if (options & UTF8PROC_NLF2LS) {
|
||||
if (options & UTF8PROC_NLF2PS) {
|
||||
buffer[wpos++] = 0x000A;
|
||||
} else {
|
||||
buffer[wpos++] = 0x2028;
|
||||
}
|
||||
} else {
|
||||
if (options & UTF8PROC_NLF2PS) {
|
||||
buffer[wpos++] = 0x2029;
|
||||
} else {
|
||||
buffer[wpos++] = 0x0020;
|
||||
}
|
||||
}
|
||||
} else if ((options & UTF8PROC_STRIPCC) &&
|
||||
(uc < 0x0020 || (uc >= 0x007F && uc < 0x00A0))) {
|
||||
if (uc == 0x0009) buffer[wpos++] = 0x0020;
|
||||
} else {
|
||||
buffer[wpos++] = uc;
|
||||
}
|
||||
}
|
||||
length = wpos;
|
||||
}
|
||||
if (options & UTF8PROC_COMPOSE) {
|
||||
int32_t *starter = NULL;
|
||||
int32_t current_char;
|
||||
const utf8proc_property_t *starter_property = NULL, *current_property;
|
||||
utf8proc_propval_t max_combining_class = -1;
|
||||
ssize_t rpos;
|
||||
ssize_t wpos = 0;
|
||||
int32_t composition;
|
||||
for (rpos = 0; rpos < length; rpos++) {
|
||||
current_char = buffer[rpos];
|
||||
current_property = utf8proc_get_property(current_char);
|
||||
if (starter && current_property->combining_class > max_combining_class) {
|
||||
// combination perhaps possible
|
||||
int32_t hangul_lindex;
|
||||
int32_t hangul_sindex;
|
||||
hangul_lindex = *starter - UTF8PROC_HANGUL_LBASE;
|
||||
if (hangul_lindex >= 0 && hangul_lindex < UTF8PROC_HANGUL_LCOUNT) {
|
||||
int32_t hangul_vindex;
|
||||
hangul_vindex = current_char - UTF8PROC_HANGUL_VBASE;
|
||||
if (hangul_vindex >= 0 && hangul_vindex < UTF8PROC_HANGUL_VCOUNT) {
|
||||
*starter = UTF8PROC_HANGUL_SBASE +
|
||||
(hangul_lindex * UTF8PROC_HANGUL_VCOUNT + hangul_vindex) *
|
||||
UTF8PROC_HANGUL_TCOUNT;
|
||||
starter_property = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
hangul_sindex = *starter - UTF8PROC_HANGUL_SBASE;
|
||||
if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT &&
|
||||
(hangul_sindex % UTF8PROC_HANGUL_TCOUNT) == 0) {
|
||||
int32_t hangul_tindex;
|
||||
hangul_tindex = current_char - UTF8PROC_HANGUL_TBASE;
|
||||
if (hangul_tindex >= 0 && hangul_tindex < UTF8PROC_HANGUL_TCOUNT) {
|
||||
*starter += hangul_tindex;
|
||||
starter_property = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!starter_property) {
|
||||
starter_property = utf8proc_get_property(*starter);
|
||||
}
|
||||
if (starter_property->comb1st_index >= 0 &&
|
||||
current_property->comb2nd_index >= 0) {
|
||||
composition = utf8proc_combinations[
|
||||
starter_property->comb1st_index +
|
||||
current_property->comb2nd_index
|
||||
];
|
||||
if (composition >= 0 && (!(options & UTF8PROC_STABLE) ||
|
||||
!(utf8proc_get_property(composition)->comp_exclusion))) {
|
||||
*starter = composition;
|
||||
starter_property = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer[wpos] = current_char;
|
||||
if (current_property->combining_class) {
|
||||
if (current_property->combining_class > max_combining_class) {
|
||||
max_combining_class = current_property->combining_class;
|
||||
}
|
||||
} else {
|
||||
starter = buffer + wpos;
|
||||
starter_property = NULL;
|
||||
max_combining_class = -1;
|
||||
}
|
||||
wpos++;
|
||||
}
|
||||
length = wpos;
|
||||
}
|
||||
{
|
||||
ssize_t rpos, wpos = 0;
|
||||
int32_t uc;
|
||||
for (rpos = 0; rpos < length; rpos++) {
|
||||
uc = buffer[rpos];
|
||||
wpos += utf8proc_encode_char(uc, ((uint8_t *)buffer) + wpos);
|
||||
}
|
||||
((uint8_t *)buffer)[wpos] = 0;
|
||||
return wpos;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t utf8proc_map(
|
||||
const uint8_t *str, ssize_t strlen, uint8_t **dstptr, int options
|
||||
) {
|
||||
int32_t *buffer;
|
||||
ssize_t result;
|
||||
*dstptr = NULL;
|
||||
result = utf8proc_decompose(str, strlen, NULL, 0, options);
|
||||
if (result < 0) return result;
|
||||
buffer = malloc(result * sizeof(int32_t) + 1);
|
||||
if (!buffer) return UTF8PROC_ERROR_NOMEM;
|
||||
result = utf8proc_decompose(str, strlen, buffer, result, options);
|
||||
if (result < 0) {
|
||||
free(buffer);
|
||||
return result;
|
||||
}
|
||||
result = utf8proc_reencode(buffer, result, options);
|
||||
if (result < 0) {
|
||||
free(buffer);
|
||||
return result;
|
||||
}
|
||||
{
|
||||
int32_t *newptr;
|
||||
newptr = realloc(buffer, result+1);
|
||||
if (newptr) buffer = newptr;
|
||||
}
|
||||
*dstptr = (uint8_t *)buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t *utf8proc_NFD(const uint8_t *str) {
|
||||
uint8_t *retval;
|
||||
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
|
||||
UTF8PROC_DECOMPOSE);
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint8_t *utf8proc_NFC(const uint8_t *str) {
|
||||
uint8_t *retval;
|
||||
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
|
||||
UTF8PROC_COMPOSE);
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint8_t *utf8proc_NFKD(const uint8_t *str) {
|
||||
uint8_t *retval;
|
||||
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
|
||||
UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint8_t *utf8proc_NFKC(const uint8_t *str) {
|
||||
uint8_t *retval;
|
||||
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
|
||||
UTF8PROC_COMPOSE | UTF8PROC_COMPAT);
|
||||
return retval;
|
||||
}
|
||||
|
367
server/utf8proc.h
Normal file
367
server/utf8proc.h
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2007 Jan Behrens, FlexiGuided GmbH, Berlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* File name: utf8proc.h
|
||||
* Version: 1.1.1
|
||||
* Last changed: 2007-07-22
|
||||
*
|
||||
* Description:
|
||||
* Header files for libutf8proc, which is a mapping tool for UTF-8 strings
|
||||
* with following features:
|
||||
* - decomposing and composing of strings
|
||||
* - replacing compatibility characters with their equivalents
|
||||
* - stripping of "default ignorable characters"
|
||||
* like SOFT-HYPHEN or ZERO-WIDTH-SPACE
|
||||
* - folding of certain characters for string comparison
|
||||
* (e.g. HYPHEN U+2010 and MINUS U+2212 to ASCII "-")
|
||||
* (see "LUMP" option)
|
||||
* - optional rejection of strings containing non-assigned code points
|
||||
* - stripping of control characters
|
||||
* - stripping of character marks (accents, etc.)
|
||||
* - transformation of LF, CRLF, CR and NEL to line-feed (LF)
|
||||
* or to the unicode chararacters for paragraph separation (PS)
|
||||
* or line separation (LS).
|
||||
* - unicode case folding (for case insensitive string comparisons)
|
||||
* - rejection of illegal UTF-8 data
|
||||
* (i.e. UTF-8 encoded UTF-16 surrogates)
|
||||
* - support for korean hangul characters
|
||||
* Unicode Version 5.0.0 is supported.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8PROC_H
|
||||
#define UTF8PROC_H
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_STDBOOL_H
|
||||
#include <stdbool.h>
|
||||
#else
|
||||
typedef enum {false = 0, true = 1} bool;
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef SSIZE_MAX
|
||||
#define SSIZE_MAX (SIZE_MAX/2)
|
||||
#endif
|
||||
|
||||
#define UTF8PROC_NULLTERM (1<<0)
|
||||
#define UTF8PROC_STABLE (1<<1)
|
||||
#define UTF8PROC_COMPAT (1<<2)
|
||||
#define UTF8PROC_COMPOSE (1<<3)
|
||||
#define UTF8PROC_DECOMPOSE (1<<4)
|
||||
#define UTF8PROC_IGNORE (1<<5)
|
||||
#define UTF8PROC_REJECTNA (1<<6)
|
||||
#define UTF8PROC_NLF2LS (1<<7)
|
||||
#define UTF8PROC_NLF2PS (1<<8)
|
||||
#define UTF8PROC_NLF2LF (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS)
|
||||
#define UTF8PROC_STRIPCC (1<<9)
|
||||
#define UTF8PROC_CASEFOLD (1<<10)
|
||||
#define UTF8PROC_CHARBOUND (1<<11)
|
||||
#define UTF8PROC_LUMP (1<<12)
|
||||
#define UTF8PROC_STRIPMARK (1<<13)
|
||||
/*
|
||||
* Flags being regarded by several functions in the library:
|
||||
* NULLTERM: The given UTF-8 input is NULL terminated.
|
||||
* STABLE: Unicode Versioning Stability has to be respected.
|
||||
* COMPAT: Compatiblity decomposition
|
||||
* (i.e. formatting information is lost)
|
||||
* COMPOSE: Return a result with composed characters.
|
||||
* DECOMPOSE: Return a result with decomposed characters.
|
||||
* IGNORE: Strip "default ignorable characters"
|
||||
* REJECTNA: Return an error, if the input contains unassigned
|
||||
* code points.
|
||||
* NLF2LS: Indicating that NLF-sequences (LF, CRLF, CR, NEL) are
|
||||
* representing a line break, and should be converted to the
|
||||
* unicode character for line separation (LS).
|
||||
* NLF2PS: Indicating that NLF-sequences are representing a paragraph
|
||||
* break, and should be converted to the unicode character for
|
||||
* paragraph separation (PS).
|
||||
* NLF2LF: Indicating that the meaning of NLF-sequences is unknown.
|
||||
* STRIPCC: Strips and/or convers control characters.
|
||||
* NLF-sequences are transformed into space, except if one of
|
||||
* the NLF2LS/PS/LF options is given.
|
||||
* HorizontalTab (HT) and FormFeed (FF) are treated as a
|
||||
* NLF-sequence in this case.
|
||||
* All other control characters are simply removed.
|
||||
* CASEFOLD: Performs unicode case folding, to be able to do a
|
||||
* case-insensitive string comparison.
|
||||
* CHARBOUND: Inserts 0xFF bytes at the beginning of each sequence which
|
||||
* is representing a single grapheme cluster (see UAX#29).
|
||||
* LUMP: Lumps certain characters together
|
||||
* (e.g. HYPHEN U+2010 and MINUS U+2212 to ASCII "-").
|
||||
* (See lump.txt for details.)
|
||||
* If NLF2LF is set, this includes a transformation of
|
||||
* paragraph and line separators to ASCII line-feed (LF).
|
||||
* STRIPMARK: Strips all character markings
|
||||
* (non-spacing, spacing and enclosing) (i.e. accents)
|
||||
* NOTE: this option works only with COMPOSE or DECOMPOSE
|
||||
*/
|
||||
|
||||
#define UTF8PROC_ERROR_NOMEM -1
|
||||
#define UTF8PROC_ERROR_OVERFLOW -2
|
||||
#define UTF8PROC_ERROR_INVALIDUTF8 -3
|
||||
#define UTF8PROC_ERROR_NOTASSIGNED -4
|
||||
#define UTF8PROC_ERROR_INVALIDOPTS -5
|
||||
/*
|
||||
* Error codes being returned by almost all functions:
|
||||
* ERROR_NOMEM: Memory could not be allocated.
|
||||
* ERROR_OVERFLOW: The given string is too long to be processed.
|
||||
* ERROR_INVALIDUTF8: The given string is not a legal UTF-8 string.
|
||||
* ERROR_NOTASSIGNED: The REJECTNA flag was set,
|
||||
* and an unassigned code point was found.
|
||||
* ERROR_INVALIDOPTS: Invalid options have been used.
|
||||
*/
|
||||
|
||||
typedef int16_t utf8proc_propval_t;
|
||||
typedef struct utf8proc_property_struct {
|
||||
utf8proc_propval_t category;
|
||||
utf8proc_propval_t combining_class;
|
||||
utf8proc_propval_t bidi_class;
|
||||
utf8proc_propval_t decomp_type;
|
||||
const int32_t *decomp_mapping;
|
||||
unsigned bidi_mirrored:1;
|
||||
int32_t uppercase_mapping;
|
||||
int32_t lowercase_mapping;
|
||||
int32_t titlecase_mapping;
|
||||
int32_t comb1st_index;
|
||||
int32_t comb2nd_index;
|
||||
unsigned comp_exclusion:1;
|
||||
unsigned ignorable:1;
|
||||
unsigned control_boundary:1;
|
||||
unsigned extend:1;
|
||||
const int32_t *casefold_mapping;
|
||||
} utf8proc_property_t;
|
||||
|
||||
#define UTF8PROC_CATEGORY_LU 1
|
||||
#define UTF8PROC_CATEGORY_LL 2
|
||||
#define UTF8PROC_CATEGORY_LT 3
|
||||
#define UTF8PROC_CATEGORY_LM 4
|
||||
#define UTF8PROC_CATEGORY_LO 5
|
||||
#define UTF8PROC_CATEGORY_MN 6
|
||||
#define UTF8PROC_CATEGORY_MC 7
|
||||
#define UTF8PROC_CATEGORY_ME 8
|
||||
#define UTF8PROC_CATEGORY_ND 9
|
||||
#define UTF8PROC_CATEGORY_NL 10
|
||||
#define UTF8PROC_CATEGORY_NO 11
|
||||
#define UTF8PROC_CATEGORY_PC 12
|
||||
#define UTF8PROC_CATEGORY_PD 13
|
||||
#define UTF8PROC_CATEGORY_PS 14
|
||||
#define UTF8PROC_CATEGORY_PE 15
|
||||
#define UTF8PROC_CATEGORY_PI 16
|
||||
#define UTF8PROC_CATEGORY_PF 17
|
||||
#define UTF8PROC_CATEGORY_PO 18
|
||||
#define UTF8PROC_CATEGORY_SM 19
|
||||
#define UTF8PROC_CATEGORY_SC 20
|
||||
#define UTF8PROC_CATEGORY_SK 21
|
||||
#define UTF8PROC_CATEGORY_SO 22
|
||||
#define UTF8PROC_CATEGORY_ZS 23
|
||||
#define UTF8PROC_CATEGORY_ZL 24
|
||||
#define UTF8PROC_CATEGORY_ZP 25
|
||||
#define UTF8PROC_CATEGORY_CC 26
|
||||
#define UTF8PROC_CATEGORY_CF 27
|
||||
#define UTF8PROC_CATEGORY_CS 28
|
||||
#define UTF8PROC_CATEGORY_CO 29
|
||||
#define UTF8PROC_CATEGORY_CN 30
|
||||
#define UTF8PROC_BIDI_CLASS_L 1
|
||||
#define UTF8PROC_BIDI_CLASS_LRE 2
|
||||
#define UTF8PROC_BIDI_CLASS_LRO 3
|
||||
#define UTF8PROC_BIDI_CLASS_R 4
|
||||
#define UTF8PROC_BIDI_CLASS_AL 5
|
||||
#define UTF8PROC_BIDI_CLASS_RLE 6
|
||||
#define UTF8PROC_BIDI_CLASS_RLO 7
|
||||
#define UTF8PROC_BIDI_CLASS_PDF 8
|
||||
#define UTF8PROC_BIDI_CLASS_EN 9
|
||||
#define UTF8PROC_BIDI_CLASS_ES 10
|
||||
#define UTF8PROC_BIDI_CLASS_ET 11
|
||||
#define UTF8PROC_BIDI_CLASS_AN 12
|
||||
#define UTF8PROC_BIDI_CLASS_CS 13
|
||||
#define UTF8PROC_BIDI_CLASS_NSM 14
|
||||
#define UTF8PROC_BIDI_CLASS_BN 15
|
||||
#define UTF8PROC_BIDI_CLASS_B 16
|
||||
#define UTF8PROC_BIDI_CLASS_S 17
|
||||
#define UTF8PROC_BIDI_CLASS_WS 18
|
||||
#define UTF8PROC_BIDI_CLASS_ON 19
|
||||
#define UTF8PROC_DECOMP_TYPE_FONT 1
|
||||
#define UTF8PROC_DECOMP_TYPE_NOBREAK 2
|
||||
#define UTF8PROC_DECOMP_TYPE_INITIAL 3
|
||||
#define UTF8PROC_DECOMP_TYPE_MEDIAL 4
|
||||
#define UTF8PROC_DECOMP_TYPE_FINAL 5
|
||||
#define UTF8PROC_DECOMP_TYPE_ISOLATED 6
|
||||
#define UTF8PROC_DECOMP_TYPE_CIRCLE 7
|
||||
#define UTF8PROC_DECOMP_TYPE_SUPER 8
|
||||
#define UTF8PROC_DECOMP_TYPE_SUB 9
|
||||
#define UTF8PROC_DECOMP_TYPE_VERTICAL 10
|
||||
#define UTF8PROC_DECOMP_TYPE_WIDE 11
|
||||
#define UTF8PROC_DECOMP_TYPE_NARROW 12
|
||||
#define UTF8PROC_DECOMP_TYPE_SMALL 13
|
||||
#define UTF8PROC_DECOMP_TYPE_SQUARE 14
|
||||
#define UTF8PROC_DECOMP_TYPE_FRACTION 15
|
||||
#define UTF8PROC_DECOMP_TYPE_COMPAT 16
|
||||
|
||||
extern const int8_t utf8proc_utf8class[256];
|
||||
|
||||
const char *utf8proc_errmsg(ssize_t errcode);
|
||||
/*
|
||||
* Returns a static error string for the given error code.
|
||||
*/
|
||||
|
||||
ssize_t utf8proc_iterate(const uint8_t *str, ssize_t strlen, int32_t *dst);
|
||||
/*
|
||||
* Reads a single char from the UTF-8 sequence being pointed to by 'str'.
|
||||
* The maximum number of bytes read is 'strlen', unless 'strlen' is
|
||||
* negative.
|
||||
* If a valid unicode char could be read, it is stored in the variable
|
||||
* being pointed to by 'dst', otherwise that variable will be set to -1.
|
||||
* In case of success the number of bytes read is returned, otherwise a
|
||||
* negative error code is returned.
|
||||
*/
|
||||
|
||||
bool utf8proc_codepoint_valid(int32_t uc);
|
||||
/*
|
||||
* Returns 1, if the given unicode code-point is valid, otherwise 0.
|
||||
*/
|
||||
|
||||
ssize_t utf8proc_encode_char(int32_t uc, uint8_t *dst);
|
||||
/*
|
||||
* Encodes the unicode char with the code point 'uc' as an UTF-8 string in
|
||||
* the byte array being pointed to by 'dst'. This array has to be at least
|
||||
* 4 bytes long.
|
||||
* In case of success the number of bytes written is returned,
|
||||
* otherwise 0.
|
||||
* This function does not check if 'uc' is a valid unicode code point.
|
||||
*/
|
||||
|
||||
const utf8proc_property_t *utf8proc_get_property(int32_t uc);
|
||||
/*
|
||||
* Returns a pointer to a (constant) struct containing information about
|
||||
* the unicode char with the given code point 'uc'.
|
||||
* If the character is not existent a pointer to a special struct is
|
||||
* returned, where 'category' is a NULL pointer.
|
||||
* WARNING: The parameter 'uc' has to be in the range of 0x0000 to
|
||||
* 0x10FFFF, otherwise the program might crash!
|
||||
*/
|
||||
|
||||
ssize_t utf8proc_decompose_char(
|
||||
int32_t uc, int32_t *dst, ssize_t bufsize,
|
||||
int options, int *last_boundclass
|
||||
);
|
||||
/*
|
||||
* Writes a decomposition of the unicode char 'uc' into the array being
|
||||
* pointed to by 'dst'.
|
||||
* Following flags in the 'options' field are regarded:
|
||||
* REJECTNA: an unassigned unicode code point leads to an error
|
||||
* IGNORE: "default ignorable" chars are stripped
|
||||
* CASEFOLD: unicode casefolding is applied
|
||||
* COMPAT: replace certain characters with their
|
||||
* compatibility decomposition
|
||||
* CHARBOUND: Inserts 0xFF bytes before each grapheme cluster
|
||||
* LUMP: lumps certain different characters together
|
||||
* STRIPMARK: removes all character marks
|
||||
* The pointer 'last_boundclass' has to point to an integer variable which
|
||||
* is storing the last character boundary class, if the CHARBOUND option
|
||||
* is used.
|
||||
* In case of success the number of chars written is returned,
|
||||
* in case of an error, a negative error code is returned.
|
||||
* If the number of written chars would be bigger than 'bufsize',
|
||||
* the buffer (up to 'bufsize') has inpredictable data, and the needed
|
||||
* buffer size is returned.
|
||||
* WARNING: The parameter 'uc' has to be in the range of 0x0000 to
|
||||
* 0x10FFFF, otherwise the program might crash!
|
||||
*/
|
||||
|
||||
ssize_t utf8proc_decompose(
|
||||
const uint8_t *str, ssize_t strlen,
|
||||
int32_t *buffer, ssize_t bufsize, int options
|
||||
);
|
||||
/*
|
||||
* Does the same as 'utf8proc_decompose_char', but acts on a whole UTF-8
|
||||
* string, and orders the decomposed sequences correctly.
|
||||
* If the NULLTERM flag in 'options' is set, processing will be stopped,
|
||||
* when a NULL byte is encounted, otherwise 'strlen' bytes are processed.
|
||||
* The result in form of unicode code points is written into the buffer
|
||||
* being pointed to by 'buffer', having the length of 'bufsize' entries.
|
||||
* In case of success the number of chars written is returned,
|
||||
* in case of an error, a negative error code is returned.
|
||||
* If the number of written chars would be bigger than 'bufsize',
|
||||
* the buffer (up to 'bufsize') has inpredictable data, and the needed
|
||||
* buffer size is returned.
|
||||
*/
|
||||
|
||||
ssize_t utf8proc_reencode(int32_t *buffer, ssize_t length, int options);
|
||||
/*
|
||||
* Reencodes the sequence of unicode characters given by the pointer
|
||||
* 'buffer' and 'length' as UTF-8.
|
||||
* The result is stored in the same memory area where the data is read.
|
||||
* Following flags in the 'options' field are regarded:
|
||||
* NLF2LS: converts LF, CRLF, CR and NEL into LS
|
||||
* NLF2PS: converts LF, CRLF, CR and NEL into PS
|
||||
* NLF2LF: converts LF, CRLF, CR and NEL into LF
|
||||
* STRIPCC: strips or converts all non-affected control characters
|
||||
* COMPOSE: tries to combine decomposed characters into composite
|
||||
* characters
|
||||
* STABLE: prohibits combining characters which would violate
|
||||
* the unicode versioning stability
|
||||
* In case of success the length of the resulting UTF-8 string is
|
||||
* returned, otherwise a negative error code is returned.
|
||||
* WARNING: The amount of free space being pointed to by 'buffer', has to
|
||||
* exceed the amount of the input data by one byte, and the
|
||||
* entries of the array pointed to by 'str' have to be in the
|
||||
* range of 0x0000 to 0x10FFFF, otherwise the program might
|
||||
* crash!
|
||||
*/
|
||||
|
||||
ssize_t utf8proc_map(
|
||||
const uint8_t *str, ssize_t strlen, uint8_t **dstptr, int options
|
||||
);
|
||||
/*
|
||||
* Maps the given UTF-8 string being pointed to by 'str' to a new UTF-8
|
||||
* string, which is allocated dynamically, and afterwards pointed to by
|
||||
* the pointer being pointed to by 'dstptr'.
|
||||
* If the NULLTERM flag in the 'options' field is set, the length is
|
||||
* determined by a NULL terminator, otherwise the parameter 'strlen' is
|
||||
* evaluated to determine the string length, but in any case the result
|
||||
* will be NULL terminated (though it might contain NULL characters
|
||||
* before). Other flags in the 'options' field are passed to the functions
|
||||
* defined above, and regarded as described.
|
||||
* In case of success the length of the new string is returned,
|
||||
* otherwise a negative error code is returned.
|
||||
* NOTICE: The memory of the new UTF-8 string will have been allocated with
|
||||
* 'malloc', and has theirfore to be freed with 'free'.
|
||||
*/
|
||||
|
||||
uint8_t *utf8proc_NFD(const uint8_t *str);
|
||||
uint8_t *utf8proc_NFC(const uint8_t *str);
|
||||
uint8_t *utf8proc_NFKD(const uint8_t *str);
|
||||
uint8_t *utf8proc_NFKC(const uint8_t *str);
|
||||
/*
|
||||
* Returns a pointer to newly allocated memory of a NFD, NFC, NFKD or NFKC
|
||||
* normalized version of the null-terminated string 'str'.
|
||||
*/
|
||||
|
||||
|
||||
#endif
|
||||
|
13383
server/utf8proc_data.c
Normal file
13383
server/utf8proc_data.c
Normal file
File diff suppressed because it is too large
Load Diff
51
server/version.c
Normal file
51
server/version.c
Normal file
@ -0,0 +1,51 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains the version identification of the Zephyr server
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1989 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "zserver.h"
|
||||
#include <zephyr_version.h>
|
||||
|
||||
const char zephyr_version[] = "Zephyr system version" ZEPHYR_VERSION_STRING;
|
||||
|
||||
#ifdef DEBUG
|
||||
const char version[] = "Zephyr Server (DEBUG) " ZEPHYR_VERSION_STRING;
|
||||
#else
|
||||
const char version[] = "Zephyr Server " ZEPHYR_VERSION_STRING;
|
||||
#endif
|
||||
|
||||
#if !defined (lint) && !defined (SABER)
|
||||
static const char copyright[] =
|
||||
"Copyright (c) 1987,1988,1989,1990 Massachusetts Institute of Technology.\n";
|
||||
#endif
|
||||
|
||||
char *
|
||||
get_version(void)
|
||||
{
|
||||
static char vers_buf[256];
|
||||
struct utsname un;
|
||||
|
||||
if (vers_buf[0] == '\0') {
|
||||
strcpy(vers_buf, version);
|
||||
|
||||
(void) strcat(vers_buf, "/");
|
||||
|
||||
uname(&un);
|
||||
(void) strcat(vers_buf, un.machine);
|
||||
(void) strcat(vers_buf, "-");
|
||||
(void) strcat(vers_buf, un.sysname);
|
||||
}
|
||||
|
||||
return(vers_buf);
|
||||
}
|
129
server/zephyrd.8.in
Normal file
129
server/zephyrd.8.in
Normal file
@ -0,0 +1,129 @@
|
||||
.\" $Id$
|
||||
.\"
|
||||
.\" Copyright 1987 by the Massachusetts Institute of Technology
|
||||
.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
|
||||
.\" specifies the terms and conditions for redistribution.
|
||||
.\"
|
||||
.TH ZEPHYRD 8 "July 1, 1988" "MIT Project Athena"
|
||||
.ds ]W MIT Project Athena
|
||||
.SH NAME
|
||||
zephyrd \- Zephyr server daemon
|
||||
.SH SYNOPSIS
|
||||
.I @sbindir@/zephyrd
|
||||
[
|
||||
.BI \-d
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.I zephyrd
|
||||
is the central server for the Zephyr Notification System.
|
||||
It maintains a location database of all currently logged-in users, and a
|
||||
subscription database for each user's Zephyr clients.
|
||||
.PP
|
||||
.I zephyrd
|
||||
communicates with daemons running on other Zephyr server hosts, to
|
||||
provide a reliable service.
|
||||
.PP
|
||||
While running, any unusual conditions are recorded via
|
||||
.I syslog(3)
|
||||
to facility local6 at various levels.
|
||||
The
|
||||
.BI \-d
|
||||
option enables logging of additional debugging information.
|
||||
.PP
|
||||
When a
|
||||
.B zephyrd
|
||||
is executed, it requests a list of server machines from Hesiod and
|
||||
initializes its state from any
|
||||
\fIzephyrd\fRs executing on the other known servers. This initialization
|
||||
is only performed after the \fIzephyrd\fRs have authenticated themselves
|
||||
to each other via Kerberos.
|
||||
The server then enters a dispatch loop, servicing requests from clients and
|
||||
other servers.
|
||||
.SH SIGNALS
|
||||
.B SIGUSR1
|
||||
enables logging of additional debugging information.
|
||||
.br
|
||||
.B SIGUSR2
|
||||
disables the logging of additional debugging information.
|
||||
.br
|
||||
.B SIGHUP
|
||||
causes
|
||||
.I zephyrd
|
||||
to re-read the default subscription file and to re-query Hesiod about
|
||||
valid peers. Any peers which are not responding and no longer
|
||||
mentioned in Hesiod are flushed; any peers not previously named by
|
||||
Hesiod are added.
|
||||
.br
|
||||
.B SIGINT \fRand\fB SIGTERM
|
||||
cause
|
||||
.I zephyrd
|
||||
to gracefully shut down.
|
||||
.br
|
||||
.B SIGFPE
|
||||
causes
|
||||
.I zephyrd
|
||||
to dump the location and subscription databases to
|
||||
.I /var/tmp/zephyr.db
|
||||
in an ASCII format.
|
||||
.SH ACCESS CONTROL
|
||||
Certain notice classes are restricted by the Zephyr server. Each such
|
||||
class has access control lists enumerating who may transmit (xmt-*.acl) or
|
||||
subscribe to that particular class. Subscriptions may be
|
||||
restricted either absolutely (sub-*.acl files), or by instance restrictions.
|
||||
iws-*.acl files control subscriptions to wildcarded instances.
|
||||
iui-*.acl files control subscriptions to instances which are not the
|
||||
Kerberos principal identity of the subscriber.
|
||||
If an access control list of a given type is absent, there is no
|
||||
restriction of that type on the class, except that any notices of the
|
||||
class must be authenticated.
|
||||
The class registry lists all classes which are restricted.
|
||||
.SH FILES
|
||||
.TP 10
|
||||
.I @sysconfdir@/zephyr/acl/class-registry.acl:
|
||||
List of classes which are restricted
|
||||
.TP
|
||||
.I @sysconfdir@/zephyr/acl/iws-*.acl:
|
||||
Access Control Lists for instance-wildcard restrictions
|
||||
.TP
|
||||
.I @sysconfdir@/zephyr/acl/iui-*.acl:
|
||||
Access Control Lists for instance-identity restrictions
|
||||
.TP
|
||||
.I @sysconfdir@/zephyr/acl/sub-*.acl:
|
||||
Access Control Lists for subscribing
|
||||
.TP
|
||||
.I @sysconfdir@/zephyr/acl/xmt-*.acl:
|
||||
Access Control Lists for transmitting
|
||||
.TP
|
||||
.I @sysconfdir@/zephyr/srvtab:
|
||||
Kerberos 4 Service keys
|
||||
.TP
|
||||
.I @sysconfdir@/zephyr/krb5.keytab:
|
||||
Kerberos V Service keys
|
||||
.TP
|
||||
.I /var/run/zephyrd.tkt4:
|
||||
Current Kerberos 4 tickets for exchange with other servers
|
||||
.TP
|
||||
.I /var/run/zephyrd.tkt:
|
||||
Current Kerberos 5 tickets for exchange with other servers
|
||||
.TP
|
||||
.I /var/tmp/zephyr.db:
|
||||
File containing an ASCII dump of the database.
|
||||
.SH BUGS
|
||||
The current implementation of the Zephyr server (\fIzephyrd(8)\fR) makes
|
||||
no distinction between realm-announced, net-visible and net-announced
|
||||
exposure levels.
|
||||
.SH SEE ALSO
|
||||
zephyr(1), zhm(8), kerberosintro(1), hesiod(3), access_control_lists(?),
|
||||
syslog(3)
|
||||
.br
|
||||
Athena Technical Plan, Sections E.4.1 (Zephyr Notification Service) and
|
||||
E.2.1 (Kerberos Authentication and Authorization System)
|
||||
.SH AUTHOR
|
||||
.PP
|
||||
John T. Kohl, MIT Project Athena and Digital Equipment Corporation
|
||||
.SH RESTRICTIONS
|
||||
Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
|
||||
All Rights Reserved.
|
||||
.br
|
||||
.I zephyr(1)
|
||||
specifies the terms and conditions for redistribution.
|
532
server/zserver.h
Normal file
532
server/zserver.h
Normal file
@ -0,0 +1,532 @@
|
||||
#ifndef __ZSERVER_H__
|
||||
#define __ZSERVER_H__
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains declarations for use in the server.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include <internal.h>
|
||||
#ifdef HAVE_ARES
|
||||
#include <ares.h>
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "zsrv_err.h"
|
||||
|
||||
#include "timer.h"
|
||||
#include "zsrv_conf.h" /* configuration params */
|
||||
|
||||
#include "zstring.h"
|
||||
#include "access.h"
|
||||
#include "acl.h"
|
||||
#include "utf8proc.h"
|
||||
|
||||
|
||||
/* Kerberos-specific library interfaces used only by the server. */
|
||||
#ifdef HAVE_KRB5
|
||||
extern krb5_keyblock *__Zephyr_keyblock;
|
||||
#define ZGetSession() (__Zephyr_keyblock)
|
||||
void ZSetSession(krb5_keyblock *keyblock);
|
||||
krb5_error_code Z_krb5_init_keyblock(krb5_context, krb5_enctype, size_t,
|
||||
krb5_keyblock **);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KRB4
|
||||
void ZSetSessionDES(C_Block *key);
|
||||
|
||||
Code_t ZFormatAuthenticNotice(ZNotice_t*, char*, int, int*, C_Block);
|
||||
|
||||
#ifndef HAVE_KRB5
|
||||
extern C_Block __Zephyr_session;
|
||||
#define ZGetSession() (__Zephyr_session)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* For krb_rd_req prototype and definition. */
|
||||
#ifndef KRB_INT32
|
||||
#define KRB_INT32 ZEPHYR_INT32
|
||||
#endif
|
||||
|
||||
/* Current time as cached by main(); use instead of time(). */
|
||||
#define NOW t_local.tv_sec
|
||||
|
||||
#ifdef HAVE_KRB4
|
||||
/* Kerberos shouldn't stick us with array types... */
|
||||
typedef struct {
|
||||
des_key_schedule s;
|
||||
} Sched;
|
||||
#endif
|
||||
|
||||
enum _ZRealm_state {
|
||||
REALM_NEW, /* New realm; no servers yet */
|
||||
REALM_UP, /* ZRealm is up */
|
||||
REALM_TARDY, /* ZRealm due for a hello XXX */
|
||||
REALM_DEAD, /* ZRealm is considered dead */
|
||||
REALM_STARTING /* ZRealm is between dead and up */
|
||||
};
|
||||
|
||||
enum _Server_state {
|
||||
SERV_UP, /* Server is up */
|
||||
SERV_TARDY, /* Server due for a hello */
|
||||
SERV_DEAD, /* Server is considered dead */
|
||||
SERV_STARTING /* Server is between dead and up */
|
||||
};
|
||||
|
||||
enum _Sent_type {
|
||||
NOT_SENT, /* message was not xmitted */
|
||||
SENT, /* message was xmitted */
|
||||
AUTH_FAILED, /* authentication failed */
|
||||
NOT_FOUND /* user not found for uloc */
|
||||
};
|
||||
|
||||
typedef struct _Destination Destination;
|
||||
typedef struct _Destlist Destlist;
|
||||
typedef struct _ZRealm ZRealm;
|
||||
typedef struct _ZRealmname ZRealmname;
|
||||
typedef enum _ZRealm_state ZRealm_state;
|
||||
typedef struct _ZRealm_server ZRealm_server;
|
||||
typedef struct _Client Client;
|
||||
typedef struct _Triplet Triplet;
|
||||
typedef enum _Server_state Server_state;
|
||||
typedef struct _Unacked Unacked;
|
||||
typedef struct _Pending Pending;
|
||||
typedef struct _Server Server;
|
||||
typedef enum _Sent_type Sent_type;
|
||||
typedef struct _Statistic Statistic;
|
||||
|
||||
struct _Destination {
|
||||
String *classname;
|
||||
String *inst;
|
||||
String *recip;
|
||||
};
|
||||
|
||||
struct _Destlist {
|
||||
Destination dest;
|
||||
struct _Destlist *next, **prev_p;
|
||||
};
|
||||
|
||||
struct _ZRealm_server {
|
||||
String *name; /* server's hostname */
|
||||
struct sockaddr_in addr; /* server's address */
|
||||
ZRealm *realm; /* realm this server belongs to */
|
||||
Timer *timer; /* timer for name lookup */
|
||||
unsigned int dontsend :1; /* private server, do not send */
|
||||
unsigned int got_addr :1; /* IP address is valid */
|
||||
unsigned int deleted :1; /* server no longer exists */
|
||||
};
|
||||
|
||||
struct _ZRealm {
|
||||
String *namestr; /* realm's name */
|
||||
char *name; /* always namestr->string */
|
||||
int count;
|
||||
ZRealm_server **srvrs;
|
||||
int idx; /* which server we are connected to */
|
||||
Destlist *subs; /* what their clients sub to */
|
||||
Destlist *remsubs; /* our subs on their end */
|
||||
Client *client;
|
||||
int child_pid;
|
||||
int have_tkt;
|
||||
ZRealm_state state;
|
||||
};
|
||||
|
||||
struct _ZRealmname {
|
||||
String *name;
|
||||
struct _ZRealm_server *servers;
|
||||
int nused;
|
||||
int nservers;
|
||||
};
|
||||
|
||||
struct _Client {
|
||||
struct sockaddr_in addr; /* ipaddr/port of client */
|
||||
Destlist *subs ; /* subscriptions */
|
||||
#ifdef HAVE_KRB5
|
||||
krb5_keyblock *session_keyblock;
|
||||
#else
|
||||
#ifdef HAVE_KRB4
|
||||
C_Block session_key; /* session key for this client */
|
||||
#endif /* HAVE_KRB4 */
|
||||
#endif
|
||||
String *principal; /* krb principal of user */
|
||||
int last_send; /* Counter for last sent packet. */
|
||||
time_t last_ack; /* Time of last received ack */
|
||||
ZRealm *realm;
|
||||
struct _Client *next, **prev_p;
|
||||
};
|
||||
|
||||
struct _Triplet {
|
||||
Destination dest;
|
||||
Acl *acl;
|
||||
Client **clients;
|
||||
int clients_size;
|
||||
struct _Triplet *next, **prev_p;
|
||||
};
|
||||
|
||||
struct _Unacked {
|
||||
Timer *timer; /* timer for retransmit */
|
||||
Client *client; /* responsible client, or NULL */
|
||||
short rexmits; /* number of retransmits */
|
||||
short packsz; /* size of packet */
|
||||
char *packet; /* ptr to packet */
|
||||
ZUnique_Id_t uid; /* uid of packet */
|
||||
struct sockaddr_in ack_addr;
|
||||
union { /* address to send to */
|
||||
struct sockaddr_in addr; /* client address */
|
||||
int srv_idx; /* index of server */
|
||||
struct {
|
||||
ZRealm *realm; /* pointer to realm */
|
||||
int rlm_srv_idx; /* index of server in realm */
|
||||
} rlm;
|
||||
} dest;
|
||||
struct _Unacked *next, **prev_p;
|
||||
};
|
||||
|
||||
struct _Pending {
|
||||
char *packet; /* the notice (in pkt form) */
|
||||
short len; /* len of pkt */
|
||||
unsigned int auth; /* whether it is authentic */
|
||||
struct sockaddr_in who; /* the addr of the sender */
|
||||
struct _Pending *next;
|
||||
};
|
||||
|
||||
struct _Server {
|
||||
Server_state state; /* server's state */
|
||||
struct sockaddr_in addr; /* server's address */
|
||||
long timeout; /* Length of timeout in sec */
|
||||
Timer *timer; /* timer for this server */
|
||||
Pending *queue; /* queue of packets to send
|
||||
to this server when done dumping */
|
||||
Pending *queue_last; /* last packet on queue */
|
||||
short num_hello_sent; /* number of hello's sent */
|
||||
unsigned int dumping; /* 1 if dumping, so we should queue */
|
||||
char addr_str[16]; /* text version of address */
|
||||
};
|
||||
|
||||
/* statistics gathering */
|
||||
struct _Statistic {
|
||||
int val;
|
||||
char *str;
|
||||
};
|
||||
|
||||
typedef enum _Exposure_type {
|
||||
NONE,
|
||||
OPSTAFF_VIS,
|
||||
REALM_VIS,
|
||||
REALM_ANN,
|
||||
NET_VIS,
|
||||
NET_ANN
|
||||
} Exposure_type;
|
||||
|
||||
typedef struct _Location {
|
||||
String *user;
|
||||
String *machine;
|
||||
char *time; /* in ctime format */
|
||||
String *tty;
|
||||
struct sockaddr_in addr; /* IP address and port of location */
|
||||
Exposure_type exposure;
|
||||
} Location;
|
||||
|
||||
/* Function declarations */
|
||||
|
||||
/* These macros instantiate inline functions that do the work of the formder
|
||||
LIST_INSERT and LIST_DELETE functions, which unfortunately triggered gcc's
|
||||
pedanticism. The comment before the *former* macros was: */
|
||||
/* These macros are for insertion into and deletion from a singly-linked list
|
||||
* with back pointers to the previous element's next pointer. In order to
|
||||
* make these macros act like expressions, they use the comma operator for
|
||||
* sequenced evaluations of assignment, and "a && b" for "evaluate assignment
|
||||
* b if expression a is true". */
|
||||
|
||||
#define MAKE_LIST_INSERT(type) inline static void type##_insert(type **head, type *elem) \
|
||||
{\
|
||||
(elem)->next = *(head); \
|
||||
if(*head) (*(head))->prev_p = &(elem)->next; \
|
||||
(*head) = (elem); \
|
||||
(elem)->prev_p = (head); \
|
||||
}
|
||||
|
||||
#define MAKE_LIST_DELETE(type) inline static void type##_delete(type *elem) \
|
||||
{\
|
||||
*(elem)->prev_p = (elem)->next; \
|
||||
if((elem)->next) (elem)->next->prev_p = (elem)->prev_p; \
|
||||
}
|
||||
|
||||
MAKE_LIST_INSERT(Destlist)
|
||||
MAKE_LIST_DELETE(Destlist)
|
||||
MAKE_LIST_INSERT(Client)
|
||||
MAKE_LIST_DELETE(Client)
|
||||
MAKE_LIST_INSERT(Triplet)
|
||||
MAKE_LIST_DELETE(Triplet)
|
||||
MAKE_LIST_INSERT(Unacked)
|
||||
MAKE_LIST_DELETE(Unacked)
|
||||
|
||||
/* found in bdump.c */
|
||||
void bdump_get(ZNotice_t *notice, int auth, struct sockaddr_in *who,
|
||||
Server *server);
|
||||
void bdump_send(void);
|
||||
void bdump_offer(struct sockaddr_in *who);
|
||||
Code_t bdump_send_list_tcp(ZNotice_Kind_t kind, struct sockaddr_in *addr,
|
||||
char *class_name, char *inst, char *opcode,
|
||||
char *sender, char *recip, char **lyst,
|
||||
int num);
|
||||
int get_tgt(void);
|
||||
|
||||
/* found in class.c */
|
||||
extern String *class_control, *class_admin, *class_hm;
|
||||
extern String *class_ulogin, *class_ulocate;
|
||||
int ZDest_eq(Destination *d1, Destination *d2);
|
||||
Code_t triplet_register(Client *client, Destination *dest, ZRealm *realm);
|
||||
Code_t triplet_deregister(Client *client, Destination *dest,
|
||||
ZRealm *realm);
|
||||
Code_t class_restrict(char *class_name, Acl *acl);
|
||||
Code_t class_setup_restricted(char *class_name, Acl *acl);
|
||||
Client **triplet_lookup(Destination *dest);
|
||||
Acl *class_get_acl(String *class_name);
|
||||
int dest_eq(Destination *d1, Destination *d2);
|
||||
int order_dest_strings(Destination *d1, Destination *d2);
|
||||
void triplet_dump_subs(FILE *fp);
|
||||
|
||||
/* found in client.c */
|
||||
Code_t client_register(ZNotice_t *notice, struct in_addr *host,
|
||||
Client **client_p, int wantdefaults);
|
||||
void client_deregister(Client *client, int flush);
|
||||
void client_flush_host(struct in_addr *host);
|
||||
void client_flush_princ(char *target);
|
||||
void client_dump_clients(FILE *fp);
|
||||
Client *client_find(struct in_addr *host, unsigned int port);
|
||||
Code_t client_send_clients(void);
|
||||
|
||||
/* found in common.c */
|
||||
char *strsave(const char *str);
|
||||
unsigned long hash (const char *);
|
||||
void dump_quote(char *p, FILE *fp);
|
||||
void notice_extract_address(ZNotice_t *notice, struct sockaddr_in *addr);
|
||||
int packets_waiting(void);
|
||||
|
||||
/* found in dispatch.c */
|
||||
void handle_packet(void);
|
||||
void clt_ack(ZNotice_t *notice, struct sockaddr_in *who, Sent_type sent);
|
||||
void nack_release(Client *client);
|
||||
void sendit(ZNotice_t *notice, int auth, struct sockaddr_in *who,
|
||||
int external);
|
||||
void rexmit(void *);
|
||||
void xmit(ZNotice_t *notice, struct sockaddr_in *dest, int auth,
|
||||
Client *client);
|
||||
Code_t hostm_dispatch(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who, Server *server);
|
||||
Code_t control_dispatch(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who, Server *server);
|
||||
Code_t xmit_frag(ZNotice_t *notice, char *buf, int len, int waitforack);
|
||||
void hostm_shutdown(void);
|
||||
|
||||
/* found in kstuff.c */
|
||||
Code_t ZCheckSrvAuthentication(ZNotice_t *notice, struct sockaddr_in *from, char *realm);
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_KRB5)
|
||||
Code_t ReadKerberosData(int, int *, char **, int *);
|
||||
void sweep_ticket_hash_table(void *);
|
||||
#endif
|
||||
#ifdef HAVE_KRB4
|
||||
int GetKerberosData (int, struct in_addr, AUTH_DAT *, char *, char *);
|
||||
Code_t SendKerberosData (int, KTEXT, char *, char *);
|
||||
#endif
|
||||
#ifdef HAVE_KRB5
|
||||
Code_t SendKrb5Data(int, krb5_data *);
|
||||
Code_t GetKrb5Data(int, krb5_data *);
|
||||
#endif
|
||||
|
||||
/* found in server.c */
|
||||
void server_timo(void *which);
|
||||
void server_dump_servers(FILE *fp);
|
||||
void server_init(void);
|
||||
void server_shutdown(void);
|
||||
void server_forward(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who);
|
||||
void server_kill_clt(Client *client);
|
||||
void server_pending_free(Pending *pending);
|
||||
void server_self_queue(ZNotice_t *, int, struct sockaddr_in *);
|
||||
void server_send_queue(Server *);
|
||||
void server_reset(void);
|
||||
Server *server_which_server(struct sockaddr_in *who);
|
||||
Pending *server_dequeue(Server *server);
|
||||
Code_t server_dispatch(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who);
|
||||
Code_t server_adispatch(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who, Server *server);
|
||||
|
||||
/* found in subscr.c */
|
||||
Code_t subscr_foreign_user(ZNotice_t *, struct sockaddr_in *, Server *, ZRealm *);
|
||||
Code_t subscr_cancel(struct sockaddr_in *sin, ZNotice_t *notice);
|
||||
Code_t subscr_subscribe(Client *who, ZNotice_t *notice, Server *server);
|
||||
Code_t subscr_send_subs(Client *client);
|
||||
void subscr_cancel_client(Client *client);
|
||||
void subscr_sendlist(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who);
|
||||
void subscr_dump_subs(FILE *fp, Destlist *subs);
|
||||
void subscr_reset(void);
|
||||
Code_t subscr_def_subs(Client *who);
|
||||
Code_t subscr_realm(ZRealm *, ZNotice_t *);
|
||||
Code_t subscr_send_realm_subs(ZRealm *);
|
||||
Code_t subscr_realm_subs(ZRealm *);
|
||||
Code_t subscr_realm_cancel(struct sockaddr_in *, ZNotice_t *, ZRealm *);
|
||||
|
||||
/* found in uloc.c */
|
||||
void uloc_hflush(struct in_addr *addr);
|
||||
void uloc_flush_client(struct sockaddr_in *sin);
|
||||
void uloc_dump_locs(FILE *fp);
|
||||
Code_t ulogin_dispatch(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who, Server *server);
|
||||
Code_t ulocate_dispatch(ZNotice_t *notice, int auth,
|
||||
struct sockaddr_in *who, Server *server);
|
||||
Code_t uloc_send_locations(void);
|
||||
void ulogin_relay_locate(ZNotice_t *, struct sockaddr_in *);
|
||||
void ulogin_realm_locate(ZNotice_t *, struct sockaddr_in *, ZRealm *);
|
||||
|
||||
/* found in realm.c */
|
||||
int realm_sender_in_realm(const char *realm, char *sender);
|
||||
int realm_bound_for_realm(const char *realm, char *recip);
|
||||
ZRealm *realm_which_realm(struct sockaddr_in *who);
|
||||
ZRealm *realm_get_realm_by_name(char *name);
|
||||
ZRealm *realm_get_realm_by_name_string(String *namestr);
|
||||
ZRealm *realm_get_realm_by_pid(int);
|
||||
void realm_handoff(ZNotice_t *, int, struct sockaddr_in *, ZRealm *, int);
|
||||
const char *realm_expand_realm(char *);
|
||||
void realm_init(void);
|
||||
Code_t ZCheckZRealmAuthentication(ZNotice_t *, struct sockaddr_in *,
|
||||
char *);
|
||||
Code_t realm_control_dispatch(ZNotice_t *, int, struct sockaddr_in *,
|
||||
Server *, ZRealm *);
|
||||
void realm_shutdown(void);
|
||||
void realm_deathgram(Server *);
|
||||
Code_t realm_send_realms(void);
|
||||
Code_t realm_dispatch(ZNotice_t *, int, struct sockaddr_in *, Server *);
|
||||
void kill_realm_pids(void);
|
||||
void realm_dump_realms(FILE *);
|
||||
|
||||
/* found in version.c */
|
||||
char *get_version(void);
|
||||
|
||||
/* found in access.c */
|
||||
int access_check(char *, struct sockaddr_in *, Acl *, Access);
|
||||
int opstaff_check(char *);
|
||||
|
||||
/* global identifiers */
|
||||
|
||||
/* found in global.c */
|
||||
extern struct sockaddr_in srv_addr; /* server socket address */
|
||||
extern unsigned short hm_port; /* host manager receiver port */
|
||||
extern unsigned short hm_srv_port; /* host manager server sending port */
|
||||
extern int srv_socket; /* dgram sockets for clients
|
||||
and other servers */
|
||||
extern int bdump_socket; /* brain dump socket
|
||||
(closed most of the time) */
|
||||
#ifdef HAVE_ARES
|
||||
extern ares_channel achannel;
|
||||
#endif
|
||||
|
||||
extern fd_set interesting; /* the file descrips we are listening
|
||||
to right now */
|
||||
extern int nfds; /* number to look at in select() */
|
||||
extern int zdebug;
|
||||
#ifdef DEBUG
|
||||
extern int zalone;
|
||||
#endif
|
||||
extern char myname[]; /* domain name of this host */
|
||||
extern char list_file[];
|
||||
#ifdef HAVE_KRB5
|
||||
extern char keytab_file[];
|
||||
extern krb5_ccache Z_krb5_ccache;
|
||||
#endif
|
||||
#ifdef HAVE_KRB4
|
||||
extern char srvtab_file[];
|
||||
#endif
|
||||
extern char acl_dir[];
|
||||
extern char subs_file[];
|
||||
extern const char version[];
|
||||
extern u_long npackets; /* num of packets processed */
|
||||
extern time_t uptime; /* time we started */
|
||||
extern struct in_addr my_addr; /* my inet address */
|
||||
extern struct timeval t_local; /* current time */
|
||||
extern char *bdump_version;
|
||||
extern int bdump_auth_proto;
|
||||
|
||||
/* found in bdump.c */
|
||||
extern int bdumping; /* are we processing a bdump packet? */
|
||||
extern int bdump_concurrent; /* set while processing a packet
|
||||
* concurrently during a braindump. */
|
||||
|
||||
/* found in dispatch.c */
|
||||
extern Statistic i_s_ctls, i_s_logins, i_s_admins, i_s_locates;
|
||||
extern int rexmit_times[];
|
||||
|
||||
/* found in server.c */
|
||||
extern Server *otherservers; /* array of servers */
|
||||
extern int me_server_idx; /* me (in the array of servers) */
|
||||
extern int nservers; /* number of other servers*/
|
||||
|
||||
/* found in subscr.c */
|
||||
extern String *empty;
|
||||
extern String *wildcard_instance;
|
||||
|
||||
extern ZRealm **otherrealms;
|
||||
extern int nrealms;
|
||||
|
||||
|
||||
#define class_is_control(classname) (classname == class_control)
|
||||
#define class_is_admin(classname) (classname == class_admin)
|
||||
#define class_is_hm(classname) (classname == class_hm)
|
||||
#define class_is_ulogin(classname) (classname == class_ulogin)
|
||||
#define class_is_ulocate(classname) (classname == class_ulocate)
|
||||
|
||||
#define ADMIN_HELLO "HELLO" /* Opcode: hello, are you there */
|
||||
#define ADMIN_IMHERE "IHEARDYOU" /* Opcode: yes, I am here */
|
||||
#define ADMIN_SHUTDOWN "GOODBYE" /* Opcode: I am shutting down */
|
||||
#define ADMIN_BDUMP "DUMP_AVAIL" /* Opcode: I will give you a dump */
|
||||
#define ADMIN_DONE "DUMP_DONE" /* Opcode: brain dump for this server
|
||||
is complete */
|
||||
#define ADMIN_NEWCLT "NEXT_CLIENT" /* Opcode: this is a new client */
|
||||
#define ADMIN_KILL_CLT "KILL_CLIENT" /* Opcode: client is dead, remove */
|
||||
#define ADMIN_STATUS "STATUS" /* Opcode: please send status */
|
||||
|
||||
#define ADMIN_NEWREALM "NEXT_REALM" /* Opcode: this is a new realm */
|
||||
#define REALM_REQ_LOCATE "REQ_LOCATE" /* Opcode: request a location */
|
||||
#define REALM_ANS_LOCATE "ANS_LOCATE" /* Opcode: answer to location */
|
||||
#define REALM_BOOT "SENDSUBS" /* Opcode: first server in realm */
|
||||
|
||||
/* me_server_idx is the index into otherservers of this server descriptor. */
|
||||
/* the 'limbo' server is always the first server */
|
||||
|
||||
#define me_server (&otherservers[me_server_idx])
|
||||
#define limbo_server_idx() (0)
|
||||
#define limbo_server (&otherservers[limbo_server_idx()])
|
||||
|
||||
#define msgs_queued() (ZQLength() || otherservers[me_server_idx].queue)
|
||||
|
||||
#define ack(a,b) clt_ack(a,b,SENT)
|
||||
#define nack(a,b) clt_ack(a,b,NOT_SENT)
|
||||
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define START_CRITICAL_CODE
|
||||
#define END_CRITICAL_CODE
|
||||
|
||||
/* the instance that matches all instances */
|
||||
#define WILDCARD_INSTANCE "*"
|
||||
|
||||
/* debugging macros */
|
||||
#ifdef DEBUG
|
||||
#define zdbug(s1) if (zdebug) syslog s1;
|
||||
#else /* !DEBUG */
|
||||
#define zdbug(s1)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* !__ZSERVER_H__ */
|
55
server/zsrv_conf.h
Normal file
55
server/zsrv_conf.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains site-specific definitions for use in the server.
|
||||
*
|
||||
* Created by: John T. Kohl
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1988 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#ifndef __ZSRV_CONF_H__
|
||||
#define __ZSRV_CONF_H__
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/* Path names are relative to CONFDIR, except for the class registry and
|
||||
ticket files. */
|
||||
|
||||
#define SERVER_LIST_FILE "server.list"
|
||||
#define REALM_LIST_FILE "realm.list"
|
||||
#ifdef HAVE_KRB5
|
||||
#define ZEPHYR_KEYTAB "krb5.keytab"
|
||||
#define ZEPHYR_TK5FILE "/var/run/zephyrd.tkt"
|
||||
#endif
|
||||
#ifdef HAVE_KRB4
|
||||
#define ZEPHYR_SRVTAB "srvtab"
|
||||
#define ZEPHYR_TKFILE "/var/run/zephyrd.tkt4"
|
||||
#endif
|
||||
#define ZEPHYR_ACL_DIR "acl/"
|
||||
#define ZEPHYR_CLASS_REGISTRY "class-registry.acl"
|
||||
#define DEFAULT_SUBS_FILE "default.subscriptions"
|
||||
|
||||
#define REXMIT_TIMES { 2, 2, 4, 4, 8, 8, 16, 32, 64, 128, 256, 512, -1 }
|
||||
#define NUM_REXMIT_TIMES 12
|
||||
#define CLIENT_GIVEUP_MIN 512
|
||||
|
||||
/* hostmanager defines */
|
||||
#define LOSE_TIMO (60) /* time during which a losing host
|
||||
must respond to a ping */
|
||||
|
||||
/* server-server defines */
|
||||
#define TIMO_UP ((long) 60) /* timeout between up and tardy */
|
||||
#define TIMO_TARDY ((long) 120) /* timeout btw tardy hellos */
|
||||
#define TIMO_DEAD ((long)(15*60)) /* timeout between hello's for dead */
|
||||
|
||||
#define H_NUM_TARDY 5 /* num hello's before going dead
|
||||
when tardy */
|
||||
#define H_NUM_STARTING 2 /* num hello's before going dead
|
||||
when starting */
|
||||
|
||||
#define SWEEP_INTERVAL 3600 /* Time between sweeps of the ticket
|
||||
hash table */
|
||||
|
||||
#endif /* __ZSRV_CONF_H__ */
|
43
server/zsrv_err.et
Normal file
43
server/zsrv_err.et
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright (c) 1987,1988 Massachusetts Institute of Technology
|
||||
#
|
||||
# For copying and distribution information, see the file
|
||||
# "mit-copyright.h".
|
||||
#
|
||||
# $Id$
|
||||
et zsrv
|
||||
|
||||
ec ZSRV_BADASSOC,
|
||||
"Client not associated with class"
|
||||
ec ZSRV_NOCLT,
|
||||
"No such client"
|
||||
ec ZSRV_NOSUB,
|
||||
"No such subscription"
|
||||
ec ZSRV_NOCLASS,
|
||||
"Class unknown"
|
||||
ec ZSRV_CLASSXISTS,
|
||||
"Class already registered"
|
||||
ec ZSRV_CLASSRESTRICTED,
|
||||
"Class already restricted"
|
||||
ec ZSRV_HNOTFOUND,
|
||||
"Host manager unknown"
|
||||
ec ZSRV_WRONGSRV,
|
||||
"Host not on this server"
|
||||
ec ZSRV_PKSHORT,
|
||||
"Pkt length too short"
|
||||
ec ZSRV_BUFSHORT,
|
||||
"Buffer too short"
|
||||
ec ZSRV_LEN,
|
||||
"Read/Write length wrong"
|
||||
ec ZSRV_UNKNOWNOPCODE,
|
||||
"Unknown opcode"
|
||||
ec ZSRV_REQUEUE,
|
||||
"Requeue for later processing"
|
||||
ec ZSRV_RCSID,
|
||||
"$Id$"
|
||||
ec ZSRV_BADSUBPORT,
|
||||
"Illegal port specified in subscription"
|
||||
ec ZSRV_NORLM,
|
||||
"No such realm"
|
||||
ec ZSRV_EMPTYCLASS,
|
||||
"Class is now empty"
|
||||
end
|
191
server/zstring.c
Normal file
191
server/zstring.c
Normal file
@ -0,0 +1,191 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It contains the main loop of the Zephyr server
|
||||
*
|
||||
* Created by: Lucien W. Van Elsen
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1991 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include "zserver.h"
|
||||
|
||||
#ifndef lint
|
||||
#ifndef SABER
|
||||
static const char rcsid_zstring_c[] =
|
||||
"$Id$";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static String *zhash[STRING_HASH_TABLE_SIZE];
|
||||
|
||||
static int
|
||||
valid_utf8_p(const char* s)
|
||||
{
|
||||
ssize_t len;
|
||||
int32_t uc;
|
||||
|
||||
while ((len = utf8proc_iterate((const unsigned char *)s, -1, &uc))) {
|
||||
if (len <=0) return 0; /* Not valid UTF-8 encoding. */
|
||||
if (!(utf8proc_codepoint_valid(uc))) return 0; /* Not valid unicode codepoint. */
|
||||
if (uc == 0) return 1; /* NULL, we're done. */
|
||||
s += len;
|
||||
}
|
||||
return 0; /* We shouldn't get here. */
|
||||
}
|
||||
|
||||
static char *zdowncase(const char* s)
|
||||
{
|
||||
unsigned char *new_s_u; /* Avoid strict aliasing violation */
|
||||
char *new_s, *p;
|
||||
|
||||
if (valid_utf8_p(s)) {
|
||||
/* Use utf8proc if we're dealing with UTF-8.
|
||||
* Rather than downcase, casefold and normalize to NFKC.
|
||||
*/
|
||||
utf8proc_map((const unsigned char *)s, 0, (unsigned char **)&new_s_u,
|
||||
UTF8PROC_NULLTERM | UTF8PROC_STABLE
|
||||
| UTF8PROC_CASEFOLD | UTF8PROC_COMPAT
|
||||
| UTF8PROC_COMPOSE);
|
||||
new_s = (char *)new_s_u;
|
||||
} else {
|
||||
/* If not, fall back to old methods. */
|
||||
new_s = strsave(s);
|
||||
p = new_s;
|
||||
while(*p) {
|
||||
if (isascii(*p) && isupper(*p))
|
||||
*p = tolower(*p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
return new_s;
|
||||
}
|
||||
|
||||
String *
|
||||
make_string(char *s,
|
||||
int downcase)
|
||||
{
|
||||
char *new_s;
|
||||
String *new_z,*hp;
|
||||
int i;
|
||||
|
||||
if (downcase) {
|
||||
new_s = zdowncase(s);
|
||||
} else {
|
||||
new_s = s;
|
||||
}
|
||||
|
||||
new_z = find_string(new_s,0);
|
||||
if (new_z != NULL) {
|
||||
if (downcase)
|
||||
free(new_s);
|
||||
new_z->ref_count++;
|
||||
return(new_z);
|
||||
}
|
||||
|
||||
/* Initialize new String */
|
||||
|
||||
if (!downcase)
|
||||
new_s = strsave(s);
|
||||
new_z = (String *) malloc(sizeof(String));
|
||||
new_z->string = new_s;
|
||||
new_z->ref_count = 1;
|
||||
|
||||
/* Add to beginning of hash table */
|
||||
new_z->hash_val = hash(new_s);
|
||||
i = new_z->hash_val % STRING_HASH_TABLE_SIZE;
|
||||
hp = zhash[i];
|
||||
new_z->next = hp;
|
||||
if (hp != NULL)
|
||||
hp->prev = new_z;
|
||||
new_z->prev = NULL;
|
||||
zhash[i] = new_z;
|
||||
|
||||
return new_z;
|
||||
}
|
||||
|
||||
void
|
||||
free_string(String *z)
|
||||
{
|
||||
if (z == (String *) NULL)
|
||||
return;
|
||||
|
||||
z->ref_count--;
|
||||
if (z->ref_count > 0)
|
||||
return;
|
||||
|
||||
/* delete string completely */
|
||||
if(z->prev == NULL)
|
||||
zhash[hash(z->string) % STRING_HASH_TABLE_SIZE] = z->next;
|
||||
else
|
||||
z->prev->next = z->next;
|
||||
|
||||
if (z->next != NULL)
|
||||
z->next->prev = z->prev;
|
||||
|
||||
free(z->string);
|
||||
free(z);
|
||||
}
|
||||
|
||||
String *
|
||||
find_string(char *s,
|
||||
int downcase)
|
||||
{
|
||||
char *new_s;
|
||||
String *z;
|
||||
|
||||
if (downcase) {
|
||||
new_s = zdowncase(s);
|
||||
} else {
|
||||
new_s = s;
|
||||
}
|
||||
|
||||
z = zhash[hash(new_s) % STRING_HASH_TABLE_SIZE];
|
||||
while (z != NULL) {
|
||||
if (strcmp(new_s, z->string) == 0)
|
||||
break;
|
||||
z = z->next;
|
||||
}
|
||||
|
||||
if (downcase)
|
||||
free(new_s);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
int
|
||||
comp_string(String *a,
|
||||
String *b)
|
||||
{
|
||||
if (a->hash_val > b->hash_val)
|
||||
return 1;
|
||||
if (a->hash_val < b->hash_val)
|
||||
return -1;
|
||||
return strcmp(a->string,b->string);
|
||||
}
|
||||
|
||||
void
|
||||
print_string_table(FILE *f)
|
||||
{
|
||||
String *p;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < STRING_HASH_TABLE_SIZE; i++) {
|
||||
p = zhash[i];
|
||||
while (p != (String *) NULL) {
|
||||
fprintf(f,"[%d] %s\n",p->ref_count,p->string);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String *
|
||||
dup_string(String *z)
|
||||
{
|
||||
z->ref_count++;
|
||||
return z;
|
||||
}
|
||||
|
33
server/zstring.h
Normal file
33
server/zstring.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 1991 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file "mit-copyright.h".
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef __zstring_h
|
||||
#define __zstring_h __FILE__
|
||||
|
||||
#define STRING_HASH_TABLE_SIZE 1024
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct _String
|
||||
{
|
||||
char *string; /* the string itself */
|
||||
int ref_count; /* for gc */
|
||||
unsigned long hash_val; /* hash value for this string */
|
||||
struct _String *next, *prev; /* for linking in hash table */
|
||||
} String;
|
||||
|
||||
String *make_string(char *s, int downcase);
|
||||
void free_string(String *z);
|
||||
String *find_string(char *s, int downcase);
|
||||
String *dup_string(String *z);
|
||||
int comp_string(String *a, String *b);
|
||||
void print_string_table(FILE *f);
|
||||
|
||||
#endif /* __zstring_h */
|
||||
|
Reference in New Issue
Block a user