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 void free_loc __P((Location *loc));
106 static void ulogin_locate_forward __P((ZNotice_t *notice,
107 struct sockaddr_in *who, ZRealm *realm));
109 static Location *locations = NULL; /* ptr to first in array */
110 static int num_locs = 0; /* number in array */
113 * Dispatch a LOGIN notice.
117 ulogin_dispatch(ZNotice_t *notice,
119 struct sockaddr_in *who,
122 Exposure_type retval;
125 if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
126 retval = ulogin_remove_user(notice, who, &err_ret);
129 if (err_ret == UNAUTH) {
130 if (server == me_server)
131 clt_ack(notice, who, AUTH_FAILED);
133 } else if (err_ret == NOLOC) {
134 if (server == me_server)
135 clt_ack(notice, who, NOT_FOUND);
138 syslog(LOG_ERR,"bogus location exposure NONE, %s",
143 /* he is not announced to people. Silently ack */
144 if (server == me_server)
149 if (server == me_server)
150 sendit(notice, 1, who, 0);
153 /* currently no distinction between these.
155 /* we assume that if this user is at a certain
156 IP address, we can trust the logout to be
157 authentic. ulogin_remove_user checks the
159 if (server == me_server)
160 sendit(notice, 1, who, 1);
163 syslog(LOG_ERR,"bogus location exposure %d/%s",
164 (int) retval, notice->z_sender);
167 if (server == me_server) /* tell the other servers */
168 server_forward(notice, auth, who);
172 (!auth || strcmp(notice->z_sender, notice->z_class_inst) != 0)) {
173 zdbug((LOG_DEBUG,"unauthentic ulogin: %d %s %s", auth,
174 notice->z_sender, notice->z_class_inst));
175 if (server == me_server)
176 clt_ack(notice, who, AUTH_FAILED);
179 if (strcmp(notice->z_opcode, LOGIN_USER_FLUSH) == 0) {
180 ulogin_flush_user(notice);
181 if (server == me_server)
183 } else if (strcmp(notice->z_opcode, EXPOSE_NONE) == 0) {
184 ulogin_remove_user(notice, who, &err_ret);
185 if (err_ret == UNAUTH) {
186 if (server == me_server)
187 clt_ack(notice, who, AUTH_FAILED);
189 } else if (err_ret == NOLOC) {
190 if (server == me_server)
191 clt_ack(notice, who, NOT_FOUND);
194 if (server == me_server) {
196 server_forward(notice, auth, who);
199 } else if (strcmp(notice->z_opcode, EXPOSE_OPSTAFF) == 0) {
200 err_ret = ulogin_add_user(notice, OPSTAFF_VIS, who);
201 if (server == me_server) {
207 } else if (strcmp(notice->z_opcode, EXPOSE_REALMVIS) == 0) {
208 err_ret = ulogin_add_user(notice, REALM_VIS, who);
209 if (server == me_server) { /* realm vis is not broadcast,
216 } else if (!strcmp(notice->z_opcode, EXPOSE_REALMANN)) {
217 err_ret = ulogin_add_user(notice, REALM_ANN, who);
218 if (server == me_server) { /* announce to the realm */
222 login_sendit(notice, auth, who, 0);
224 } else if (!strcmp(notice->z_opcode, EXPOSE_NETVIS)) {
225 err_ret = ulogin_add_user(notice, NET_VIS, who);
226 if (server == me_server) { /* announce to the realm */
230 login_sendit(notice, auth, who, 0);
232 } else if (!strcmp(notice->z_opcode, EXPOSE_NETANN)) {
233 err_ret = ulogin_add_user(notice, NET_ANN, who);
234 if (server == me_server) { /* tell the world */
238 login_sendit(notice, auth, who, 1);
241 if (!strcmp(notice->z_opcode, LOGIN_USER_LOGIN)) {
242 zdbug((LOG_DEBUG, "ulog opcode from unknown foreign realm %s",
245 syslog(LOG_ERR, "unknown ulog opcode %s", notice->z_opcode);
247 if (server == me_server)
251 if (server == me_server)
252 server_forward(notice, auth, who);
257 login_sendit(ZNotice_t *notice,
259 struct sockaddr_in *who,
262 ZNotice_t log_notice;
264 /* we must copy the notice struct here because we need the original
265 for forwarding. We needn't copy the private data of the notice,
266 since that isn't modified by sendit and its subroutines. */
268 log_notice = *notice;
270 log_notice.z_opcode = LOGIN_USER_LOGIN;
271 sendit(&log_notice, auth, who, external);
276 * Dispatch a LOCATE notice.
279 ulocate_dispatch(ZNotice_t *notice,
281 struct sockaddr_in *who,
287 if (!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
288 /* we are talking to a current-rev client; send an ack */
290 cp = strchr(notice->z_class_inst, '@');
291 if (cp && (realm = realm_get_realm_by_name(cp + 1)))
292 ulogin_locate_forward(notice, who, realm);
294 ulogin_locate(notice, who, auth);
297 syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
298 if (server == me_server)
305 * Flush all locations at the address.
309 uloc_hflush(struct in_addr *addr)
312 int i = 0, new_num = 0;
315 return; /* none to flush */
317 /* slightly inefficient, assume the worst, and allocate enough space */
318 loc = (Location *) malloc(num_locs *sizeof(Location));
320 syslog(LOG_CRIT, "uloc_flush alloc");
324 /* copy entries which don't match */
325 while (i < num_locs) {
326 if (locations[i].addr.sin_addr.s_addr != addr->s_addr)
327 loc[new_num++] = locations[i];
329 free_loc(&locations[i]);
351 uloc_flush_client(struct sockaddr_in *sin)
354 int i = 0, new_num = 0;
357 return; /* none to flush */
359 /* slightly inefficient, assume the worst, and allocate enough space */
360 loc = (Location *) malloc(num_locs *sizeof(Location));
362 syslog(LOG_CRIT, "uloc_flush_clt alloc");
366 /* copy entries which don't match */
367 while (i < num_locs) {
368 if ((locations[i].addr.sin_addr.s_addr != sin->sin_addr.s_addr)
369 || (locations[i].addr.sin_port != sin->sin_port)) {
370 loc[new_num++] = locations[i];
372 free_loc(&locations[i]);
394 for (i = 0; i < num_locs; i++) {
395 syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
396 (int) locations[i].exposure);
405 * Send the locations for host for a brain dump
410 uloc_send_locations(void)
414 char *lyst[NUM_FIELDS];
415 char *exposure_level;
418 for (i = 0, loc = locations; i < num_locs; i++, loc++) {
419 lyst[0] = (char *) loc->machine->string;
420 lyst[1] = (char *) loc->time;
421 lyst[2] = (char *) loc->tty->string;
423 switch (loc->exposure) {
425 exposure_level = EXPOSE_OPSTAFF;
428 exposure_level = EXPOSE_REALMVIS;
431 exposure_level = EXPOSE_REALMANN;
434 exposure_level = EXPOSE_NETVIS;
437 exposure_level = EXPOSE_NETANN;
440 syslog(LOG_ERR,"broken location state %s/%d",
441 loc->user->string, (int) loc->exposure);
442 exposure_level = EXPOSE_OPSTAFF;
445 retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
446 loc->user->string, exposure_level, myname,
447 "", lyst, NUM_FIELDS);
448 if (retval != ZERR_NONE) {
449 syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
457 * Add the user to the internal table of locations.
461 ulogin_add_user(ZNotice_t *notice,
462 Exposure_type exposure,
463 struct sockaddr_in *who)
465 Location *loc, *oldlocs, newloc;
468 loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
470 /* Update the time, tty, and exposure on the existing location. */
471 loc->exposure = exposure;
472 if (ulogin_parse(notice, &newloc) == 0) {
473 free_string(loc->tty);
474 loc->tty = dup_string(newloc.tty);
476 loc->time = strsave(newloc.time);
484 locations = (Location *) malloc((num_locs + 1) * sizeof(Location));
486 syslog(LOG_ERR, "zloc mem alloc");
491 if (num_locs == 0) { /* first one */
492 if (ulogin_setup(notice, locations, exposure, who)) {
501 /* not the first one, insert him */
503 if (ulogin_setup(notice, &newloc, exposure, who)) {
512 while ((i < num_locs-1) &&
513 (comp_string(oldlocs[i].user,newloc.user) < 0)) {
514 locations[i] = oldlocs[i];
518 /* add him in here */
519 locations[i++] = newloc;
522 while (i < num_locs) {
523 locations[i] = oldlocs[i - 1];
534 * Set up the location locs with the information in the notice.
538 ulogin_setup(ZNotice_t *notice,
540 Exposure_type exposure,
541 struct sockaddr_in *who)
543 if (ulogin_parse(notice, locs))
546 locs->exposure = exposure;
547 locs->addr.sin_family = AF_INET;
548 locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
549 locs->addr.sin_port = notice->z_port;
554 * Parse the location information in the notice, and fill it into *locs
558 ulogin_parse(ZNotice_t *notice,
564 if (!notice->z_message_len) {
565 syslog(LOG_ERR, "short ulogin");
569 base = notice->z_message;
570 for (cp = base; cp < base + notice->z_message_len; cp++) {
575 syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
576 notice->z_sender, nulls);
580 locs->user = make_string(notice->z_class_inst,0);
583 locs->machine = make_string(cp,0);
585 cp += (strlen(cp) + 1);
586 locs->time = strsave(cp);
588 /* This field might not be null-terminated */
589 cp += (strlen(cp) + 1);
590 locs->tty = make_string(cp, 0);
597 ulogin_find(char *user,
598 struct in_addr *host,
604 /* Find the first location for this user. */
605 loc = ulogin_find_user(user);
609 /* Look for a location which matches the host and port. */
610 str = make_string(user, 0);
611 while (loc < locations + num_locs && loc->user == str) {
612 if (loc->addr.sin_addr.s_addr == host->s_addr
613 && loc->addr.sin_port == port) {
625 * Return a pointer to the first instance of this user@realm in the
630 ulogin_find_user(char *user)
639 str = make_string(user, 0);
641 /* i is the current midpoint location, rlo is the lowest we will
642 * still check, and rhi is the highest we will still check. */
648 while ((compar = comp_string(locations[i].user, str)) != 0) {
660 /* Back up to the first location for this user. */
661 while (i > 0 && locations[i - 1].user == str)
664 return &locations[i];
668 * remove the user specified in notice from the internal table
672 ulogin_remove_user(ZNotice_t *notice,
673 struct sockaddr_in *who,
676 Location *new_locs, *loc;
681 loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
687 quiet = loc->exposure;
689 if (--num_locs == 0) { /* last one */
696 new_locs = (Location *) malloc(num_locs * sizeof(Location));
698 syslog(LOG_CRIT, "ul_rem alloc");
702 /* copy old entries */
703 while (i < num_locs && &locations[i] < loc) {
704 new_locs[i] = locations[i];
708 /* free up this one */
709 free_loc(&locations[i]);
710 i++; /* skip over this one */
713 while (i <= num_locs) {
714 new_locs[i - 1] = locations[i];
720 locations = new_locs;
727 * remove all locs of the user specified in notice from the internal table
731 ulogin_flush_user(ZNotice_t *notice)
733 Location *loc, *loc2;
734 int i, j, num_match, num_left;
736 i = num_match = num_left = 0;
738 if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
741 /* compute # locations left in the list, after loc2 (inclusive) */
742 num_left = num_locs - (loc2 - locations);
745 !strcasecmp(loc2[num_match].user->string,
746 notice->z_class_inst)) {
747 /* as long as we keep matching, march up the list */
751 if (num_locs == num_match) { /* no other locations left */
752 for (j = 0; j < num_match; j++)
753 free_loc(&locations[j]); /* free storage */
760 loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
762 syslog(LOG_CRIT, "ul_rem alloc");
766 /* copy old entries */
767 while (i < num_locs && &locations[i] < loc2) {
768 loc[i] = locations[i];
772 for(j = 0; j < num_match; j++) {
773 free_loc(&locations[i]);
778 while (i < num_locs) {
779 loc[i - num_match] = locations[i];
786 num_locs -= num_match;
792 for (i = 0; i < num_locs; i++) {
793 syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
794 (int) locations[i].exposure);
802 ulogin_locate(ZNotice_t *notice,
803 struct sockaddr_in *who,
809 struct sockaddr_in send_to_who;
811 answer = ulogin_marshal_locs(notice, &found, auth);
814 send_to_who.sin_port = notice->z_port;
816 retval = ZSetDestAddr(&send_to_who);
817 if (retval != ZERR_NONE) {
818 syslog(LOG_WARNING, "ulogin_locate set addr: %s",
819 error_message(retval));
825 notice->z_kind = ACKED;
827 /* use xmit_frag() to send each piece of the notice */
829 retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
830 if (retval != ZERR_NONE)
831 syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
837 * Locate the user and collect the locations into an array. Return the # of
838 * locations in *found.
842 ulogin_marshal_locs(ZNotice_t *notice,
846 Location **matches = (Location **) 0;
851 int local = (auth && realm_sender_in_realm(ZGetRealm(), notice->z_sender));
853 *found = 0; /* # of matches */
855 loc = ulogin_find_user(notice->z_class_inst);
861 inst = make_string(notice->z_class_inst,0);
862 while (i < num_locs && (inst == locations[i].user)) {
863 /* these locations match */
864 switch (locations[i].exposure) {
880 matches = (Location **) malloc(sizeof(Location *));
882 syslog(LOG_ERR, "ulog_loc: no mem");
883 break; /* from the while */
885 matches[0] = &locations[i];
888 matches = (Location **) realloc(matches,
889 ++(*found) * sizeof(Location *));
891 syslog(LOG_ERR, "ulog_loc: realloc no mem");
893 break; /* from the while */
895 matches[*found - 1] = &locations[i];
901 /* OK, now we have a list of user@host's to return to the client
907 for (i = 0; i < *found ; i++)
908 zdbug((LOG_DEBUG,"found %s",
909 matches[i]->user->string));
913 /* coalesce the location information into a list of char *'s */
914 answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
916 syslog(LOG_ERR, "zloc no mem(answer)");
919 for (i = 0; i < *found ; i++) {
920 answer[i * NUM_FIELDS] = matches[i]->machine->string;
921 answer[i * NUM_FIELDS + 1] = matches[i]->time;
922 answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
931 uloc_dump_locs(FILE *fp)
935 for (i = 0; i < num_locs; i++) {
937 dump_quote(locations[i].user->string, fp);
939 dump_quote(locations[i].machine->string, fp);
941 dump_quote(locations[i].time, fp);
943 dump_quote(locations[i].tty->string, fp);
945 switch (locations[i].exposure) {
947 fputs("OPSTAFF", fp);
950 fputs("RLM_VIS", fp);
953 fputs("RLM_ANN", fp);
956 fputs("NET_VIS", fp);
959 fputs("NET_ANN", fp);
962 fprintf(fp, "? %d ?", locations[i].exposure);
965 fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
966 ntohs(locations[i].addr.sin_port));
971 free_loc(Location *loc)
973 free_string(loc->user);
974 free_string(loc->machine);
975 free_string(loc->tty);
981 ulogin_locate_forward(ZNotice_t *notice,
982 struct sockaddr_in *who,
988 lnotice.z_opcode = REALM_REQ_LOCATE;
990 realm_handoff(&lnotice, 1, who, realm, 0);
994 ulogin_realm_locate(ZNotice_t *notice,
995 struct sockaddr_in *who,
1007 zdbug((LOG_DEBUG, "ulogin_realm_locate"));
1010 answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
1013 lnotice.z_opcode = REALM_ANS_LOCATE;
1015 if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
1016 syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
1017 error_message(retval));
1026 if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
1027 syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1028 error_message(retval));
1033 realm_handoff(&lnotice, 1, who, realm, 0);
1040 ulogin_relay_locate(ZNotice_t *notice,
1041 struct sockaddr_in *who)
1045 struct sockaddr_in newwho;
1049 newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
1050 newwho.sin_port = notice->z_port;
1051 newwho.sin_family = AF_INET;
1053 if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
1054 syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
1055 error_message(retval));
1060 lnotice.z_opcode = LOCATE_LOCATE;
1061 lnotice.z_kind = ACKED;
1063 lnotice.z_authent_len = 0;
1064 lnotice.z_ascii_authent = "";
1065 lnotice.z_checksum = 0;
1066 lnotice.z_ascii_checksum = "";
1068 if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
1069 syslog(LOG_WARNING, "ulog_relay_loc format: %s",
1070 error_message(retval));
1074 if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
1075 syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
1076 error_message(retval));