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(notice, auth, who, server)
122 struct sockaddr_in *who;
125 Exposure_type retval;
128 if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
129 retval = ulogin_remove_user(notice, who, &err_ret);
132 if (err_ret == UNAUTH) {
133 if (server == me_server)
134 clt_ack(notice, who, AUTH_FAILED);
136 } else if (err_ret == NOLOC) {
137 if (server == me_server)
138 clt_ack(notice, who, NOT_FOUND);
141 syslog(LOG_ERR,"bogus location exposure NONE, %s",
146 /* he is not announced to people. Silently ack */
147 if (server == me_server)
152 if (server == me_server)
153 sendit(notice, 1, who, 0);
156 /* currently no distinction between these.
158 /* we assume that if this user is at a certain
159 IP address, we can trust the logout to be
160 authentic. ulogin_remove_user checks the
162 if (server == me_server)
163 sendit(notice, 1, who, 1);
166 syslog(LOG_ERR,"bogus location exposure %d/%s",
167 (int) retval, notice->z_sender);
170 if (server == me_server) /* tell the other servers */
171 server_forward(notice, auth, who);
175 (!auth || strcmp(notice->z_sender, notice->z_class_inst) != 0)) {
176 zdbug((LOG_DEBUG,"unauthentic ulogin: %d %s %s", auth,
177 notice->z_sender, notice->z_class_inst));
178 if (server == me_server)
179 clt_ack(notice, who, AUTH_FAILED);
182 if (strcmp(notice->z_opcode, LOGIN_USER_FLUSH) == 0) {
183 ulogin_flush_user(notice);
184 if (server == me_server)
186 } else if (strcmp(notice->z_opcode, EXPOSE_NONE) == 0) {
187 ulogin_remove_user(notice, who, &err_ret);
188 if (err_ret == UNAUTH) {
189 if (server == me_server)
190 clt_ack(notice, who, AUTH_FAILED);
192 } else if (err_ret == NOLOC) {
193 if (server == me_server)
194 clt_ack(notice, who, NOT_FOUND);
197 if (server == me_server) {
199 server_forward(notice, auth, who);
202 } else if (strcmp(notice->z_opcode, EXPOSE_OPSTAFF) == 0) {
203 err_ret = ulogin_add_user(notice, OPSTAFF_VIS, who);
204 if (server == me_server) {
210 } else if (strcmp(notice->z_opcode, EXPOSE_REALMVIS) == 0) {
211 err_ret = ulogin_add_user(notice, REALM_VIS, who);
212 if (server == me_server) { /* realm vis is not broadcast,
219 } else if (!strcmp(notice->z_opcode, EXPOSE_REALMANN)) {
220 err_ret = ulogin_add_user(notice, REALM_ANN, who);
221 if (server == me_server) { /* announce to the realm */
225 login_sendit(notice, auth, who, 0);
227 } else if (!strcmp(notice->z_opcode, EXPOSE_NETVIS)) {
228 err_ret = ulogin_add_user(notice, NET_VIS, who);
229 if (server == me_server) { /* announce to the realm */
233 login_sendit(notice, auth, who, 0);
235 } else if (!strcmp(notice->z_opcode, EXPOSE_NETANN)) {
236 err_ret = ulogin_add_user(notice, NET_ANN, who);
237 if (server == me_server) { /* tell the world */
241 login_sendit(notice, auth, who, 1);
244 if (!strcmp(notice->z_opcode, LOGIN_USER_LOGIN))
245 zdbug((LOG_DEBUG, "ulog opcode from unknown foreign realm %s",
248 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(notice, auth, who, external)
262 struct sockaddr_in *who;
265 ZNotice_t log_notice;
267 /* we must copy the notice struct here because we need the original
268 for forwarding. We needn't copy the private data of the notice,
269 since that isn't modified by sendit and its subroutines. */
271 log_notice = *notice;
273 log_notice.z_opcode = LOGIN_USER_LOGIN;
274 sendit(&log_notice, auth, who, external);
279 * Dispatch a LOCATE notice.
282 ulocate_dispatch(notice, auth, who, server)
285 struct sockaddr_in *who;
291 if (!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
292 /* we are talking to a current-rev client; send an ack */
294 cp = strchr(notice->z_class_inst, '@');
295 if (cp && (realm = realm_get_realm_by_name(cp + 1)))
296 ulogin_locate_forward(notice, who, realm);
298 ulogin_locate(notice, who, auth);
301 syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
302 if (server == me_server)
309 * Flush all locations at the address.
314 struct in_addr *addr;
317 int i = 0, new_num = 0;
320 return; /* none to flush */
322 /* slightly inefficient, assume the worst, and allocate enough space */
323 loc = (Location *) malloc(num_locs *sizeof(Location));
325 syslog(LOG_CRIT, "uloc_flush alloc");
329 /* copy entries which don't match */
330 while (i < num_locs) {
331 if (locations[i].addr.sin_addr.s_addr != addr->s_addr)
332 loc[new_num++] = locations[i];
334 free_loc(&locations[i]);
356 uloc_flush_client(sin)
357 struct sockaddr_in *sin;
360 int i = 0, new_num = 0;
363 return; /* none to flush */
365 /* slightly inefficient, assume the worst, and allocate enough space */
366 loc = (Location *) malloc(num_locs *sizeof(Location));
368 syslog(LOG_CRIT, "uloc_flush_clt alloc");
372 /* copy entries which don't match */
373 while (i < num_locs) {
374 if ((locations[i].addr.sin_addr.s_addr != sin->sin_addr.s_addr)
375 || (locations[i].addr.sin_port != sin->sin_port)) {
376 loc[new_num++] = locations[i];
378 free_loc(&locations[i]);
400 for (i = 0; i < num_locs; i++) {
401 syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
402 (int) locations[i].exposure);
411 * Send the locations for host for a brain dump
416 uloc_send_locations()
420 char *lyst[NUM_FIELDS];
421 char *exposure_level;
424 for (i = 0, loc = locations; i < num_locs; i++, loc++) {
425 lyst[0] = (char *) loc->machine->string;
426 lyst[1] = (char *) loc->time;
427 lyst[2] = (char *) loc->tty->string;
429 switch (loc->exposure) {
431 exposure_level = EXPOSE_OPSTAFF;
434 exposure_level = EXPOSE_REALMVIS;
437 exposure_level = EXPOSE_REALMANN;
440 exposure_level = EXPOSE_NETVIS;
443 exposure_level = EXPOSE_NETANN;
446 syslog(LOG_ERR,"broken location state %s/%d",
447 loc->user->string, (int) loc->exposure);
448 exposure_level = EXPOSE_OPSTAFF;
451 retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
452 loc->user->string, exposure_level, myname,
453 "", lyst, NUM_FIELDS);
454 if (retval != ZERR_NONE) {
455 syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
463 * Add the user to the internal table of locations.
467 ulogin_add_user(notice, exposure, who)
469 Exposure_type exposure;
470 struct sockaddr_in *who;
472 Location *loc, *oldlocs, newloc;
475 loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
477 /* Update the time, tty, and exposure on the existing location. */
478 loc->exposure = exposure;
479 if (ulogin_parse(notice, &newloc) == 0) {
480 free_string(loc->tty);
481 loc->tty = dup_string(newloc.tty);
483 loc->time = strsave(newloc.time);
491 locations = (Location *) malloc((num_locs + 1) * sizeof(Location));
493 syslog(LOG_ERR, "zloc mem alloc");
498 if (num_locs == 0) { /* first one */
499 if (ulogin_setup(notice, locations, exposure, who)) {
508 /* not the first one, insert him */
510 if (ulogin_setup(notice, &newloc, exposure, who)) {
519 while ((i < num_locs-1) &&
520 (comp_string(oldlocs[i].user,newloc.user) < 0)) {
521 locations[i] = oldlocs[i];
525 /* add him in here */
526 locations[i++] = newloc;
529 while (i < num_locs) {
530 locations[i] = oldlocs[i - 1];
541 * Set up the location locs with the information in the notice.
545 ulogin_setup(notice, locs, exposure, who)
548 Exposure_type exposure;
549 struct sockaddr_in *who;
551 if (ulogin_parse(notice, locs))
554 locs->exposure = exposure;
555 locs->addr.sin_family = AF_INET;
556 locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
557 locs->addr.sin_port = notice->z_port;
562 * Parse the location information in the notice, and fill it into *locs
566 ulogin_parse(notice, locs)
573 if (!notice->z_message_len) {
574 syslog(LOG_ERR, "short ulogin");
578 base = notice->z_message;
579 for (cp = base; cp < base + notice->z_message_len; cp++) {
584 syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
585 notice->z_sender, nulls);
589 locs->user = make_string(notice->z_class_inst,0);
592 locs->machine = make_string(cp,0);
594 cp += (strlen(cp) + 1);
595 locs->time = strsave(cp);
597 /* This field might not be null-terminated */
598 cp += (strlen(cp) + 1);
599 locs->tty = make_string(cp, 0);
606 ulogin_find(user, host, port)
608 struct in_addr *host;
614 /* Find the first location for this user. */
615 loc = ulogin_find_user(user);
619 /* Look for a location which matches the host and port. */
620 str = make_string(user, 0);
621 while (loc < locations + num_locs && loc->user == str) {
622 if (loc->addr.sin_addr.s_addr == host->s_addr
623 && loc->addr.sin_port == port) {
635 * Return a pointer to the first instance of this user@realm in the
640 ulogin_find_user(user)
650 str = make_string(user, 0);
652 /* i is the current midpoint location, rlo is the lowest we will
653 * still check, and rhi is the highest we will still check. */
659 while ((compar = comp_string(locations[i].user, str)) != 0) {
671 /* Back up to the first location for this user. */
672 while (i > 0 && locations[i - 1].user == str)
675 return &locations[i];
682 if (l1->machine != l2->machine)
684 if (l1->tty != l2->tty)
690 * remove the user specified in notice from the internal table
694 ulogin_remove_user(notice, who, err_return)
696 struct sockaddr_in *who;
699 Location *new_locs, *loc;
704 loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
710 quiet = loc->exposure;
712 if (--num_locs == 0) { /* last one */
719 new_locs = (Location *) malloc(num_locs * sizeof(Location));
721 syslog(LOG_CRIT, "ul_rem alloc");
725 /* copy old entries */
726 while (i < num_locs && &locations[i] < loc) {
727 new_locs[i] = locations[i];
731 /* free up this one */
732 free_loc(&locations[i]);
733 i++; /* skip over this one */
736 while (i <= num_locs) {
737 new_locs[i - 1] = locations[i];
743 locations = new_locs;
750 * remove all locs of the user specified in notice from the internal table
754 ulogin_flush_user(notice)
757 Location *loc, *loc2;
758 int i, j, num_match, num_left;
760 i = num_match = num_left = 0;
762 if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
765 /* compute # locations left in the list, after loc2 (inclusive) */
766 num_left = num_locs - (loc2 - locations);
769 !strcasecmp(loc2[num_match].user->string,
770 notice->z_class_inst)) {
771 /* as long as we keep matching, march up the list */
775 if (num_locs == num_match) { /* no other locations left */
776 for (j = 0; j < num_match; j++)
777 free_loc(&locations[j]); /* free storage */
784 loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
786 syslog(LOG_CRIT, "ul_rem alloc");
790 /* copy old entries */
791 while (i < num_locs && &locations[i] < loc2) {
792 loc[i] = locations[i];
796 for(j = 0; j < num_match; j++) {
797 free_loc(&locations[i]);
802 while (i < num_locs) {
803 loc[i - num_match] = locations[i];
810 num_locs -= num_match;
816 for (i = 0; i < num_locs; i++) {
817 syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
818 (int) locations[i].exposure);
826 ulogin_locate(notice, who, auth)
828 struct sockaddr_in *who;
834 struct sockaddr_in send_to_who;
836 answer = ulogin_marshal_locs(notice, &found, auth);
839 send_to_who.sin_port = notice->z_port;
841 retval = ZSetDestAddr(&send_to_who);
842 if (retval != ZERR_NONE) {
843 syslog(LOG_WARNING, "ulogin_locate set addr: %s",
844 error_message(retval));
850 notice->z_kind = ACKED;
852 /* use xmit_frag() to send each piece of the notice */
854 retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
855 if (retval != ZERR_NONE)
856 syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
862 * Locate the user and collect the locations into an array. Return the # of
863 * locations in *found.
867 ulogin_marshal_locs(notice, found, auth)
872 Location **matches = (Location **) 0;
877 int local = (auth && realm_sender_in_realm(ZGetRealm(), notice->z_sender));
879 *found = 0; /* # of matches */
881 loc = ulogin_find_user(notice->z_class_inst);
887 inst = make_string(notice->z_class_inst,0);
888 while (i < num_locs && (inst == locations[i].user)) {
889 /* these locations match */
890 switch (locations[i].exposure) {
906 matches = (Location **) malloc(sizeof(Location *));
908 syslog(LOG_ERR, "ulog_loc: no mem");
909 break; /* from the while */
911 matches[0] = &locations[i];
914 matches = (Location **) realloc(matches,
915 ++(*found) * sizeof(Location *));
917 syslog(LOG_ERR, "ulog_loc: realloc no mem");
919 break; /* from the while */
921 matches[*found - 1] = &locations[i];
927 /* OK, now we have a list of user@host's to return to the client
933 for (i = 0; i < *found ; i++)
934 zdbug((LOG_DEBUG,"found %s",
935 matches[i]->user->string));
939 /* coalesce the location information into a list of char *'s */
940 answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
942 syslog(LOG_ERR, "zloc no mem(answer)");
945 for (i = 0; i < *found ; i++) {
946 answer[i * NUM_FIELDS] = matches[i]->machine->string;
947 answer[i * NUM_FIELDS + 1] = matches[i]->time;
948 answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
962 for (i = 0; i < num_locs; i++) {
964 dump_quote(locations[i].user->string, fp);
966 dump_quote(locations[i].machine->string, fp);
968 dump_quote(locations[i].time, fp);
970 dump_quote(locations[i].tty->string, fp);
972 switch (locations[i].exposure) {
974 fputs("OPSTAFF", fp);
977 fputs("RLM_VIS", fp);
980 fputs("RLM_ANN", fp);
983 fputs("NET_VIS", fp);
986 fputs("NET_ANN", fp);
989 fprintf(fp, "? %d ?", locations[i].exposure);
992 fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
993 ntohs(locations[i].addr.sin_port));
1001 free_string(loc->user);
1002 free_string(loc->machine);
1003 free_string(loc->tty);
1009 ulogin_locate_forward(notice, who, realm)
1011 struct sockaddr_in *who;
1017 lnotice.z_opcode = REALM_REQ_LOCATE;
1019 realm_handoff(&lnotice, 1, who, realm, 0);
1023 ulogin_realm_locate(notice, who, realm)
1025 struct sockaddr_in *who;
1037 zdbug((LOG_DEBUG, "ulogin_realm_locate"));
1040 answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
1043 lnotice.z_opcode = REALM_ANS_LOCATE;
1045 if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
1046 syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
1047 error_message(retval));
1056 if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
1057 syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1058 error_message(retval));
1063 realm_handoff(&lnotice, 1, who, realm, 0);
1070 ulogin_relay_locate(notice, who)
1072 struct sockaddr_in *who;
1076 struct sockaddr_in newwho;
1080 newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
1081 newwho.sin_port = notice->z_port;
1082 newwho.sin_family = AF_INET;
1084 if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
1085 syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
1086 error_message(retval));
1091 lnotice.z_opcode = LOCATE_LOCATE;
1092 lnotice.z_kind = ACKED;
1094 if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
1095 syslog(LOG_WARNING, "ulog_relay_loc format: %s",
1096 error_message(retval));
1100 if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
1101 syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
1102 error_message(retval));