adding zephyr-im master branch

This commit is contained in:
2022-07-25 09:01:27 -06:00
parent 61f5ed8f01
commit ee1236fa5c
311 changed files with 62265 additions and 0 deletions

89
server/Makefile.in Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

392
server/class.c Normal file
View 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
View 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
View 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);
}

View File

@ -0,0 +1,3 @@
operations,message,*
message,personal,%me%
message,urgent,%me%

1239
server/dispatch.c Normal file

File diff suppressed because it is too large Load Diff

75
server/global.c Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1485
server/server.c Normal file

File diff suppressed because it is too large Load Diff

1317
server/subscr.c Normal file

File diff suppressed because it is too large Load Diff

348
server/test_server.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

51
server/version.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */