1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains functions for the User Locator service.
4 * Created by: John T. Kohl
8 * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file
13 #include <zephyr/mit-copyright.h>
15 #include <sys/socket.h>
19 static const char rcsid_uloc_c[] =
25 * The user locator functions.
29 * void ulocate_dispatch(notice, auth, who, server)
32 * struct sockaddr_in *who;
35 * void ulogin_dispatch(notice, auth, who, server)
38 * struct sockaddr_in *who;
41 * void uloc_hflush(addr)
42 * struct in_addr *addr;
44 * void uloc_flush_client(sin)
45 * struct sockaddr_in *sin;
47 * Code_t uloc_send_locations()
49 * void uloc_dump_locs(fp)
55 * We maintain an array of Location sorted by user (so we can do
56 * binary searches), growing and shrinking it as necessary.
59 /* WARNING: make sure this is the same as the number of strings you */
60 /* plan to hand back to the user in response to a locate request, */
61 /* else you will lose. See ulogin_locate() and uloc_send_locations() */
64 typedef enum _Exposure_type {
73 typedef struct _Location {
76 char *time; /* in ctime format */
78 struct sockaddr_in addr; /* IP address and port of location */
79 Exposure_type exposure;
86 static void ulogin_locate __P((ZNotice_t *notice, struct sockaddr_in *who,
88 ulogin_flush_user __P((ZNotice_t *notice));
89 static Location *ulogin_find __P((char *user, struct in_addr *host,
91 static Location *ulogin_find_user __P((char *user));
92 static int ulogin_setup __P((ZNotice_t *notice, Location *locs,
93 Exposure_type exposure, struct sockaddr_in *who)),
94 ulogin_add_user __P((ZNotice_t *notice, Exposure_type exposure,
95 struct sockaddr_in *who)),
96 ulogin_parse __P((ZNotice_t *notice, Location *locs));
97 static Exposure_type ulogin_remove_user __P((ZNotice_t *notice,
98 struct sockaddr_in *who,
100 static void login_sendit __P((ZNotice_t *notice, int auth,
101 struct sockaddr_in *who, int external));
102 static char **ulogin_marshal_locs __P((ZNotice_t *notice, int *found,
105 static int ul_equiv __P((Location *l1, Location *l2));
107 static void free_loc __P((Location *loc));
108 static void ulogin_locate_forward __P((ZNotice_t *notice,
109 struct sockaddr_in *who, ZRealm *realm));
111 static Location *locations = NULL; /* ptr to first in array */
112 static int num_locs = 0; /* number in array */
115 * Dispatch a LOGIN notice.
119 ulogin_dispatch(ZNotice_t *notice,
121 struct sockaddr_in *who,
124 Exposure_type retval;
127 if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
128 retval = ulogin_remove_user(notice, who, &err_ret);
131 if (err_ret == UNAUTH) {
132 if (server == me_server)
133 clt_ack(notice, who, AUTH_FAILED);
135 } else if (err_ret == NOLOC) {
136 if (server == me_server)
137 clt_ack(notice, who, NOT_FOUND);
140 syslog(LOG_ERR,"bogus location exposure NONE, %s",
145 /* he is not announced to people. Silently ack */
146 if (server == me_server)
151 if (server == me_server)
152 sendit(notice, 1, who, 0);
155 /* currently no distinction between these.
157 /* we assume that if this user is at a certain
158 IP address, we can trust the logout to be
159 authentic. ulogin_remove_user checks the
161 if (server == me_server)
162 sendit(notice, 1, who, 1);
165 syslog(LOG_ERR,"bogus location exposure %d/%s",
166 (int) retval, notice->z_sender);
169 if (server == me_server) /* tell the other servers */
170 server_forward(notice, auth, who);
174 (!auth || strcmp(notice->z_sender, notice->z_class_inst) != 0)) {
175 zdbug((LOG_DEBUG,"unauthentic ulogin: %d %s %s", auth,
176 notice->z_sender, notice->z_class_inst));
177 if (server == me_server)
178 clt_ack(notice, who, AUTH_FAILED);
181 if (strcmp(notice->z_opcode, LOGIN_USER_FLUSH) == 0) {
182 ulogin_flush_user(notice);
183 if (server == me_server)
185 } else if (strcmp(notice->z_opcode, EXPOSE_NONE) == 0) {
186 ulogin_remove_user(notice, who, &err_ret);
187 if (err_ret == UNAUTH) {
188 if (server == me_server)
189 clt_ack(notice, who, AUTH_FAILED);
191 } else if (err_ret == NOLOC) {
192 if (server == me_server)
193 clt_ack(notice, who, NOT_FOUND);
196 if (server == me_server) {
198 server_forward(notice, auth, who);
201 } else if (strcmp(notice->z_opcode, EXPOSE_OPSTAFF) == 0) {
202 err_ret = ulogin_add_user(notice, OPSTAFF_VIS, who);
203 if (server == me_server) {
209 } else if (strcmp(notice->z_opcode, EXPOSE_REALMVIS) == 0) {
210 err_ret = ulogin_add_user(notice, REALM_VIS, who);
211 if (server == me_server) { /* realm vis is not broadcast,
218 } else if (!strcmp(notice->z_opcode, EXPOSE_REALMANN)) {
219 err_ret = ulogin_add_user(notice, REALM_ANN, who);
220 if (server == me_server) { /* announce to the realm */
224 login_sendit(notice, auth, who, 0);
226 } else if (!strcmp(notice->z_opcode, EXPOSE_NETVIS)) {
227 err_ret = ulogin_add_user(notice, NET_VIS, who);
228 if (server == me_server) { /* announce to the realm */
232 login_sendit(notice, auth, who, 0);
234 } else if (!strcmp(notice->z_opcode, EXPOSE_NETANN)) {
235 err_ret = ulogin_add_user(notice, NET_ANN, who);
236 if (server == me_server) { /* tell the world */
240 login_sendit(notice, auth, who, 1);
243 if (!strcmp(notice->z_opcode, LOGIN_USER_LOGIN)) {
244 zdbug((LOG_DEBUG, "ulog opcode from unknown foreign realm %s",
247 syslog(LOG_ERR, "unknown ulog opcode %s", notice->z_opcode);
249 if (server == me_server)
253 if (server == me_server)
254 server_forward(notice, auth, who);
259 login_sendit(ZNotice_t *notice,
261 struct sockaddr_in *who,
264 ZNotice_t log_notice;
266 /* we must copy the notice struct here because we need the original
267 for forwarding. We needn't copy the private data of the notice,
268 since that isn't modified by sendit and its subroutines. */
270 log_notice = *notice;
272 log_notice.z_opcode = LOGIN_USER_LOGIN;
273 sendit(&log_notice, auth, who, external);
278 * Dispatch a LOCATE notice.
281 ulocate_dispatch(ZNotice_t *notice,
283 struct sockaddr_in *who,
289 if (!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
290 /* we are talking to a current-rev client; send an ack */
292 cp = strchr(notice->z_class_inst, '@');
293 if (cp && (realm = realm_get_realm_by_name(cp + 1)))
294 ulogin_locate_forward(notice, who, realm);
296 ulogin_locate(notice, who, auth);
299 syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
300 if (server == me_server)
307 * Flush all locations at the address.
311 uloc_hflush(struct in_addr *addr)
314 int i = 0, new_num = 0;
317 return; /* none to flush */
319 /* slightly inefficient, assume the worst, and allocate enough space */
320 loc = (Location *) malloc(num_locs *sizeof(Location));
322 syslog(LOG_CRIT, "uloc_flush alloc");
326 /* copy entries which don't match */
327 while (i < num_locs) {
328 if (locations[i].addr.sin_addr.s_addr != addr->s_addr)
329 loc[new_num++] = locations[i];
331 free_loc(&locations[i]);
353 uloc_flush_client(struct sockaddr_in *sin)
356 int i = 0, new_num = 0;
359 return; /* none to flush */
361 /* slightly inefficient, assume the worst, and allocate enough space */
362 loc = (Location *) malloc(num_locs *sizeof(Location));
364 syslog(LOG_CRIT, "uloc_flush_clt alloc");
368 /* copy entries which don't match */
369 while (i < num_locs) {
370 if ((locations[i].addr.sin_addr.s_addr != sin->sin_addr.s_addr)
371 || (locations[i].addr.sin_port != sin->sin_port)) {
372 loc[new_num++] = locations[i];
374 free_loc(&locations[i]);
396 for (i = 0; i < num_locs; i++) {
397 syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
398 (int) locations[i].exposure);
407 * Send the locations for host for a brain dump
412 uloc_send_locations(void)
416 char *lyst[NUM_FIELDS];
417 char *exposure_level;
420 for (i = 0, loc = locations; i < num_locs; i++, loc++) {
421 lyst[0] = (char *) loc->machine->string;
422 lyst[1] = (char *) loc->time;
423 lyst[2] = (char *) loc->tty->string;
425 switch (loc->exposure) {
427 exposure_level = EXPOSE_OPSTAFF;
430 exposure_level = EXPOSE_REALMVIS;
433 exposure_level = EXPOSE_REALMANN;
436 exposure_level = EXPOSE_NETVIS;
439 exposure_level = EXPOSE_NETANN;
442 syslog(LOG_ERR,"broken location state %s/%d",
443 loc->user->string, (int) loc->exposure);
444 exposure_level = EXPOSE_OPSTAFF;
447 retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
448 loc->user->string, exposure_level, myname,
449 "", lyst, NUM_FIELDS);
450 if (retval != ZERR_NONE) {
451 syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
459 * Add the user to the internal table of locations.
463 ulogin_add_user(ZNotice_t *notice,
464 Exposure_type exposure,
465 struct sockaddr_in *who)
467 Location *loc, *oldlocs, newloc;
470 loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
472 /* Update the time, tty, and exposure on the existing location. */
473 loc->exposure = exposure;
474 if (ulogin_parse(notice, &newloc) == 0) {
475 free_string(loc->tty);
476 loc->tty = dup_string(newloc.tty);
478 loc->time = strsave(newloc.time);
486 locations = (Location *) malloc((num_locs + 1) * sizeof(Location));
488 syslog(LOG_ERR, "zloc mem alloc");
493 if (num_locs == 0) { /* first one */
494 if (ulogin_setup(notice, locations, exposure, who)) {
503 /* not the first one, insert him */
505 if (ulogin_setup(notice, &newloc, exposure, who)) {
514 while ((i < num_locs-1) &&
515 (comp_string(oldlocs[i].user,newloc.user) < 0)) {
516 locations[i] = oldlocs[i];
520 /* add him in here */
521 locations[i++] = newloc;
524 while (i < num_locs) {
525 locations[i] = oldlocs[i - 1];
536 * Set up the location locs with the information in the notice.
540 ulogin_setup(ZNotice_t *notice,
542 Exposure_type exposure,
543 struct sockaddr_in *who)
545 if (ulogin_parse(notice, locs))
548 locs->exposure = exposure;
549 locs->addr.sin_family = AF_INET;
550 locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
551 locs->addr.sin_port = notice->z_port;
556 * Parse the location information in the notice, and fill it into *locs
560 ulogin_parse(ZNotice_t *notice,
566 if (!notice->z_message_len) {
567 syslog(LOG_ERR, "short ulogin");
571 base = notice->z_message;
572 for (cp = base; cp < base + notice->z_message_len; cp++) {
577 syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
578 notice->z_sender, nulls);
582 locs->user = make_string(notice->z_class_inst,0);
585 locs->machine = make_string(cp,0);
587 cp += (strlen(cp) + 1);
588 locs->time = strsave(cp);
590 /* This field might not be null-terminated */
591 cp += (strlen(cp) + 1);
592 locs->tty = make_string(cp, 0);
599 ulogin_find(char *user,
600 struct in_addr *host,
606 /* Find the first location for this user. */
607 loc = ulogin_find_user(user);
611 /* Look for a location which matches the host and port. */
612 str = make_string(user, 0);
613 while (loc < locations + num_locs && loc->user == str) {
614 if (loc->addr.sin_addr.s_addr == host->s_addr
615 && loc->addr.sin_port == port) {
627 * Return a pointer to the first instance of this user@realm in the
632 ulogin_find_user(char *user)
641 str = make_string(user, 0);
643 /* i is the current midpoint location, rlo is the lowest we will
644 * still check, and rhi is the highest we will still check. */
650 while ((compar = comp_string(locations[i].user, str)) != 0) {
662 /* Back up to the first location for this user. */
663 while (i > 0 && locations[i - 1].user == str)
666 return &locations[i];
670 ul_equiv(Location *l1, Location *l2)
672 if (l1->machine != l2->machine)
674 if (l1->tty != l2->tty)
680 * remove the user specified in notice from the internal table
684 ulogin_remove_user(ZNotice_t *notice,
685 struct sockaddr_in *who,
688 Location *new_locs, *loc;
693 loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
699 quiet = loc->exposure;
701 if (--num_locs == 0) { /* last one */
708 new_locs = (Location *) malloc(num_locs * sizeof(Location));
710 syslog(LOG_CRIT, "ul_rem alloc");
714 /* copy old entries */
715 while (i < num_locs && &locations[i] < loc) {
716 new_locs[i] = locations[i];
720 /* free up this one */
721 free_loc(&locations[i]);
722 i++; /* skip over this one */
725 while (i <= num_locs) {
726 new_locs[i - 1] = locations[i];
732 locations = new_locs;
739 * remove all locs of the user specified in notice from the internal table
743 ulogin_flush_user(ZNotice_t *notice)
745 Location *loc, *loc2;
746 int i, j, num_match, num_left;
748 i = num_match = num_left = 0;
750 if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
753 /* compute # locations left in the list, after loc2 (inclusive) */
754 num_left = num_locs - (loc2 - locations);
757 !strcasecmp(loc2[num_match].user->string,
758 notice->z_class_inst)) {
759 /* as long as we keep matching, march up the list */
763 if (num_locs == num_match) { /* no other locations left */
764 for (j = 0; j < num_match; j++)
765 free_loc(&locations[j]); /* free storage */
772 loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
774 syslog(LOG_CRIT, "ul_rem alloc");
778 /* copy old entries */
779 while (i < num_locs && &locations[i] < loc2) {
780 loc[i] = locations[i];
784 for(j = 0; j < num_match; j++) {
785 free_loc(&locations[i]);
790 while (i < num_locs) {
791 loc[i - num_match] = locations[i];
798 num_locs -= num_match;
804 for (i = 0; i < num_locs; i++) {
805 syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
806 (int) locations[i].exposure);
814 ulogin_locate(ZNotice_t *notice,
815 struct sockaddr_in *who,
821 struct sockaddr_in send_to_who;
823 answer = ulogin_marshal_locs(notice, &found, auth);
826 send_to_who.sin_port = notice->z_port;
828 retval = ZSetDestAddr(&send_to_who);
829 if (retval != ZERR_NONE) {
830 syslog(LOG_WARNING, "ulogin_locate set addr: %s",
831 error_message(retval));
837 notice->z_kind = ACKED;
839 /* use xmit_frag() to send each piece of the notice */
841 retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
842 if (retval != ZERR_NONE)
843 syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
849 * Locate the user and collect the locations into an array. Return the # of
850 * locations in *found.
854 ulogin_marshal_locs(ZNotice_t *notice,
858 Location **matches = (Location **) 0;
863 int local = (auth && realm_sender_in_realm(ZGetRealm(), notice->z_sender));
865 *found = 0; /* # of matches */
867 loc = ulogin_find_user(notice->z_class_inst);
873 inst = make_string(notice->z_class_inst,0);
874 while (i < num_locs && (inst == locations[i].user)) {
875 /* these locations match */
876 switch (locations[i].exposure) {
892 matches = (Location **) malloc(sizeof(Location *));
894 syslog(LOG_ERR, "ulog_loc: no mem");
895 break; /* from the while */
897 matches[0] = &locations[i];
900 matches = (Location **) realloc(matches,
901 ++(*found) * sizeof(Location *));
903 syslog(LOG_ERR, "ulog_loc: realloc no mem");
905 break; /* from the while */
907 matches[*found - 1] = &locations[i];
913 /* OK, now we have a list of user@host's to return to the client
919 for (i = 0; i < *found ; i++)
920 zdbug((LOG_DEBUG,"found %s",
921 matches[i]->user->string));
925 /* coalesce the location information into a list of char *'s */
926 answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
928 syslog(LOG_ERR, "zloc no mem(answer)");
931 for (i = 0; i < *found ; i++) {
932 answer[i * NUM_FIELDS] = matches[i]->machine->string;
933 answer[i * NUM_FIELDS + 1] = matches[i]->time;
934 answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
943 uloc_dump_locs(FILE *fp)
947 for (i = 0; i < num_locs; i++) {
949 dump_quote(locations[i].user->string, fp);
951 dump_quote(locations[i].machine->string, fp);
953 dump_quote(locations[i].time, fp);
955 dump_quote(locations[i].tty->string, fp);
957 switch (locations[i].exposure) {
959 fputs("OPSTAFF", fp);
962 fputs("RLM_VIS", fp);
965 fputs("RLM_ANN", fp);
968 fputs("NET_VIS", fp);
971 fputs("NET_ANN", fp);
974 fprintf(fp, "? %d ?", locations[i].exposure);
977 fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
978 ntohs(locations[i].addr.sin_port));
983 free_loc(Location *loc)
985 free_string(loc->user);
986 free_string(loc->machine);
987 free_string(loc->tty);
993 ulogin_locate_forward(ZNotice_t *notice,
994 struct sockaddr_in *who,
1000 lnotice.z_opcode = REALM_REQ_LOCATE;
1002 realm_handoff(&lnotice, 1, who, realm, 0);
1006 ulogin_realm_locate(ZNotice_t *notice,
1007 struct sockaddr_in *who,
1019 zdbug((LOG_DEBUG, "ulogin_realm_locate"));
1022 answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
1025 lnotice.z_opcode = REALM_ANS_LOCATE;
1027 if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
1028 syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
1029 error_message(retval));
1038 if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
1039 syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1040 error_message(retval));
1045 realm_handoff(&lnotice, 1, who, realm, 0);
1052 ulogin_relay_locate(ZNotice_t *notice,
1053 struct sockaddr_in *who)
1057 struct sockaddr_in newwho;
1061 newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
1062 newwho.sin_port = notice->z_port;
1063 newwho.sin_family = AF_INET;
1065 if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
1066 syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
1067 error_message(retval));
1072 lnotice.z_opcode = LOCATE_LOCATE;
1073 lnotice.z_kind = ACKED;
1075 lnotice.z_authent_len = 0;
1076 lnotice.z_ascii_authent = "";
1077 lnotice.z_checksum = 0;
1078 lnotice.z_ascii_checksum = "";
1080 if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
1081 syslog(LOG_WARNING, "ulog_relay_loc format: %s",
1082 error_message(retval));
1086 if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
1087 syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
1088 error_message(retval));