]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/uloc.c
This commit was generated by cvs2svn to compensate for changes in r127,
[1ts-debian.git] / zephyr / server / uloc.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for the User Locator service.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Id: uloc.c,v 1.60 2001/02/27 04:59:03 zacheiss Exp $
7  *
8  *      Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
9  *      For copying and distribution information, see the file
10  *      "mit-copyright.h". 
11  */
12
13 #include <zephyr/mit-copyright.h>
14 #include "zserver.h"
15 #include <sys/socket.h>
16
17 #ifndef lint
18 #ifndef SABER
19 static const char rcsid_uloc_c[] =
20 "$Id: uloc.c,v 1.60 2001/02/27 04:59:03 zacheiss Exp $";
21 #endif /* SABER */
22 #endif /* lint */
23
24 /*
25  * The user locator functions.
26  *
27  * External functions:
28  *
29  * void ulocate_dispatch(notice, auth, who, server)
30  *      ZNotice_t *notice;
31  *      int auth;
32  *      struct sockaddr_in *who;
33  *      Server *server;
34  *
35  * void ulogin_dispatch(notice, auth, who, server)
36  *      ZNotice_t *notice;
37  *      int auth;
38  *      struct sockaddr_in *who;
39  *      Server *server;
40  *
41  * void uloc_hflush(addr)
42  *      struct in_addr *addr;
43  *
44  * void uloc_flush_client(sin)
45  *      struct sockaddr_in *sin;
46  *
47  * Code_t uloc_send_locations()
48  *
49  * void uloc_dump_locs(fp)
50  *      FILE *fp;
51  */
52
53 /*
54  * The user locator.
55  * We maintain an array of Location sorted by user (so we can do
56  * binary searches), growing and shrinking it as necessary.
57  */
58
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() */  
62 #define NUM_FIELDS      3
63
64 typedef enum _Exposure_type {
65     NONE,
66     OPSTAFF_VIS,
67     REALM_VIS,
68     REALM_ANN,
69     NET_VIS,
70     NET_ANN
71 } Exposure_type;
72
73 typedef struct _Location {
74     String *user;
75     String *machine;
76     char *time;                 /* in ctime format */
77     String *tty;
78     struct sockaddr_in addr;    /* IP address and port of location */
79     Exposure_type exposure;
80 } Location;
81
82 #define NOLOC           1
83 #define QUIET           -1
84 #define UNAUTH          -2
85
86 static void ulogin_locate __P((ZNotice_t *notice, struct sockaddr_in *who,
87                                int auth)),
88 ulogin_flush_user __P((ZNotice_t *notice));
89 static Location *ulogin_find __P((char *user, struct in_addr *host,
90                                   unsigned int port));
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,
99                                              int *err_return));
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,
103                                        int auth));
104
105 static int ul_equiv __P((Location *l1, Location *l2));
106
107 static void free_loc __P((Location *loc));
108 static void ulogin_locate_forward __P((ZNotice_t *notice,
109                                        struct sockaddr_in *who, Realm *realm));
110
111 static Location *locations = NULL; /* ptr to first in array */
112 static int num_locs = 0;        /* number in array */
113
114 /*
115  * Dispatch a LOGIN notice.
116  */
117
118 Code_t
119 ulogin_dispatch(notice, auth, who, server)
120     ZNotice_t *notice;
121     int auth;
122     struct sockaddr_in *who;
123     Server *server;
124 {
125     Exposure_type retval;
126     int err_ret;
127
128     if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
129         retval = ulogin_remove_user(notice, who, &err_ret);
130         switch (retval) {
131           case NONE:
132             if (err_ret == UNAUTH) {
133                 if (server == me_server)
134                     clt_ack(notice, who, AUTH_FAILED);
135                 return ZERR_NONE;
136             } else if (err_ret == NOLOC) {
137                 if (server == me_server)
138                     clt_ack(notice, who, NOT_FOUND);
139                 return ZERR_NONE;
140             } 
141             syslog(LOG_ERR,"bogus location exposure NONE, %s",
142                    notice->z_sender);
143             break;
144           case OPSTAFF_VIS:
145           case REALM_VIS:
146             /* he is not announced to people.  Silently ack */
147             if (server == me_server)
148                 ack(notice, who);
149             break;
150           case REALM_ANN:
151           case NET_VIS:
152             if (server == me_server)
153                 sendit(notice, 1, who, 0, 1);
154             break;
155           case NET_ANN:
156             /* currently no distinction between these.
157                just announce */
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
161                ip addrs */
162             if (server == me_server)
163                 sendit(notice, 1, who, 1, 1);
164             break;
165           default:
166             syslog(LOG_ERR,"bogus location exposure %d/%s",
167                    (int) retval, notice->z_sender);
168             break;
169         }
170         if (server == me_server) /* tell the other servers */
171             server_forward(notice, auth, who);
172         return ZERR_NONE;
173     }
174     if (!bdumping && 
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);
180         return ZERR_NONE;
181     }
182     if (strcmp(notice->z_opcode, LOGIN_USER_FLUSH) == 0) {
183         ulogin_flush_user(notice);
184         if (server == me_server)
185             ack(notice, who);
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);
191             return ZERR_NONE;
192         } else if (err_ret == NOLOC) {
193             if (server == me_server)
194                 clt_ack(notice, who, NOT_FOUND);
195             return ZERR_NONE;
196         }
197         if (server == me_server) {
198             ack(notice, who);
199             server_forward(notice, auth, who);
200         }
201         return ZERR_NONE;
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) {
205             if (err_ret)
206                 nack(notice, who);
207             else
208                 ack(notice, who);
209         }
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,
213                                       so we ack it here */
214             if (err_ret)
215                 nack(notice, who);
216             else
217                 ack(notice, who);
218         }
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 */
222             if (err_ret)
223                 nack(notice, who);
224             else
225                 login_sendit(notice, auth, who, 0);
226         }
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 */
230             if (err_ret)
231                 nack(notice, who);
232             else
233                 login_sendit(notice, auth, who, 0);
234         }
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 */
238             if (err_ret)
239                 nack(notice, who);
240             else
241                 login_sendit(notice, auth, who, 1);
242         }
243     } else {
244         syslog(LOG_ERR, "unknown ulog opcode %s", notice->z_opcode);
245         if (server == me_server)
246             nack(notice, who);
247         return ZERR_NONE;
248     }
249     if (server == me_server)
250         server_forward(notice, auth, who);
251     return ZERR_NONE;
252 }
253
254 static void
255 login_sendit(notice, auth, who, external)
256     ZNotice_t *notice;
257     int auth;
258     struct sockaddr_in *who;
259     int external;
260 {
261     ZNotice_t log_notice;
262
263     /* we must copy the notice struct here because we need the original
264        for forwarding.  We needn't copy the private data of the notice,
265        since that isn't modified by sendit and its subroutines. */
266
267     log_notice = *notice;
268
269     log_notice.z_opcode = LOGIN_USER_LOGIN;
270     sendit(&log_notice, auth, who, external, 1);
271 }
272
273
274 /*
275  * Dispatch a LOCATE notice.
276  */
277 Code_t
278 ulocate_dispatch(notice, auth, who, server)
279     ZNotice_t *notice;
280     int auth;
281     struct sockaddr_in *who;
282     Server *server;
283 {
284     char *cp;
285     Realm *realm;
286
287     if (!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
288         /* we are talking to a current-rev client; send an ack */
289         ack(notice, who);
290         ulogin_locate(notice, who, auth);
291         return ZERR_NONE;
292     } else {
293         syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
294         if (server == me_server)
295             nack(notice, who);
296         return ZERR_NONE;
297     }
298 }
299
300 /*
301  * Flush all locations at the address.
302  */
303
304 void
305 uloc_hflush(addr)
306     struct in_addr *addr;
307 {
308     Location *loc;
309     int i = 0, new_num = 0;
310
311     if (num_locs == 0)
312         return;                 /* none to flush */
313
314     /* slightly inefficient, assume the worst, and allocate enough space */
315     loc = (Location *) malloc(num_locs *sizeof(Location));
316     if (!loc) {
317         syslog(LOG_CRIT, "uloc_flush alloc");
318         abort();
319     }
320
321     /* copy entries which don't match */
322     while (i < num_locs) {
323         if (locations[i].addr.sin_addr.s_addr != addr->s_addr)
324             loc[new_num++] = locations[i];
325         else
326             free_loc(&locations[i]);
327         i++;
328     }
329
330     free(locations);
331     locations = NULL;
332
333     if (!new_num) {
334         free(loc);
335         loc = NULL;
336         num_locs = new_num;
337
338         return;
339     }
340     locations = loc;
341     num_locs = new_num;
342
343     /* all done */
344     return;
345 }
346
347 void
348 uloc_flush_client(sin)
349     struct sockaddr_in *sin;
350 {
351     Location *loc;
352     int i = 0, new_num = 0;
353
354     if (num_locs == 0)
355         return;                 /* none to flush */
356
357     /* slightly inefficient, assume the worst, and allocate enough space */
358     loc = (Location *) malloc(num_locs *sizeof(Location));
359     if (!loc) {
360         syslog(LOG_CRIT, "uloc_flush_clt alloc");
361         abort();
362     }
363
364     /* copy entries which don't match */
365     while (i < num_locs) {
366         if ((locations[i].addr.sin_addr.s_addr != sin->sin_addr.s_addr)
367             || (locations[i].addr.sin_port != sin->sin_port)) {
368             loc[new_num++] = locations[i];
369         } else {
370             free_loc(&locations[i]);
371         }
372         i++;
373     }
374
375     free(locations);
376     locations = NULL;
377
378     if (!new_num) {
379         free(loc);
380         loc = NULL;
381         num_locs = new_num;
382
383         return;
384     }
385     locations = loc;
386     num_locs = new_num;
387
388 #ifdef DEBUG
389     if (zdebug) {
390         int i;
391
392         for (i = 0; i < num_locs; i++) {
393             syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
394                    (int) locations[i].exposure);
395         }
396     }
397 #endif
398     /* all done */
399     return;
400 }
401
402 /*
403  * Send the locations for host for a brain dump
404  */
405
406 /*ARGSUSED*/
407 Code_t
408 uloc_send_locations()
409 {
410     Location *loc;
411     int i;
412     char *lyst[NUM_FIELDS];
413     char *exposure_level;
414     Code_t retval;
415
416     for (i = 0, loc = locations; i < num_locs; i++, loc++) {
417         lyst[0] = (char *) loc->machine->string;
418         lyst[1] = (char *) loc->time;
419         lyst[2] = (char *) loc->tty->string;
420
421         switch (loc->exposure) {
422           case OPSTAFF_VIS:
423             exposure_level = EXPOSE_OPSTAFF;
424             break;
425           case REALM_VIS:
426             exposure_level = EXPOSE_REALMVIS;
427             break;
428           case REALM_ANN:
429             exposure_level = EXPOSE_REALMANN;
430             break;
431           case NET_VIS:
432             exposure_level = EXPOSE_NETVIS;
433             break;
434           case NET_ANN:
435             exposure_level = EXPOSE_NETANN;
436             break;
437           default:
438             syslog(LOG_ERR,"broken location state %s/%d",
439                    loc->user->string, (int) loc->exposure);
440             break;
441         }
442         retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
443                                      loc->user->string, exposure_level, myname,
444                                      "", lyst, NUM_FIELDS);
445         if (retval != ZERR_NONE) {
446             syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
447             return(retval);
448         }
449     }
450     return ZERR_NONE;
451 }
452
453 /*
454  * Add the user to the internal table of locations.
455  */
456
457 static int
458 ulogin_add_user(notice, exposure, who)
459     ZNotice_t *notice;
460     Exposure_type exposure;
461     struct sockaddr_in *who;
462 {
463     Location *loc, *oldlocs, newloc;
464     int i;
465
466     loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
467     if (loc) {
468         /* Update the time, tty, and exposure on the existing location. */
469         loc->exposure = exposure;
470         if (ulogin_parse(notice, &newloc) == 0) {
471             free_string(loc->tty);
472             loc->tty = dup_string(newloc.tty);
473             free(loc->time);
474             loc->time = strsave(newloc.time);
475             free_loc(&newloc);
476         }
477         return 0;
478     }
479
480     oldlocs = locations;
481
482     locations = (Location *) malloc((num_locs + 1) * sizeof(Location));
483     if (!locations) {
484         syslog(LOG_ERR, "zloc mem alloc");
485         locations = oldlocs;
486         return 1;
487     }
488
489     if (num_locs == 0) {        /* first one */
490         if (ulogin_setup(notice, locations, exposure, who)) {
491             free(locations);
492             locations = NULL;
493             return 1;
494         }
495         num_locs = 1;
496         goto dprnt;
497     }
498
499     /* not the first one, insert him */
500
501     if (ulogin_setup(notice, &newloc, exposure, who)) {
502         free(locations);
503         locations = oldlocs;
504         return 1;
505     }
506     num_locs++;
507
508     /* copy old locs */
509     i = 0;
510     while ((i < num_locs-1) &&
511            (comp_string(oldlocs[i].user,newloc.user) < 0)) {
512         locations[i] = oldlocs[i];
513         i++;
514     }
515
516     /* add him in here */
517     locations[i++] = newloc;
518
519     /* copy the rest */
520     while (i < num_locs) {
521         locations[i] = oldlocs[i - 1];
522         i++;
523     }
524     if (oldlocs)
525         free(oldlocs);
526
527   dprnt:
528     return 0;
529 }
530
531 /*
532  * Set up the location locs with the information in the notice.
533  */ 
534
535 static int
536 ulogin_setup(notice, locs, exposure, who)
537     ZNotice_t *notice;
538     Location *locs;
539     Exposure_type exposure;
540     struct sockaddr_in *who;
541 {
542     if (ulogin_parse(notice, locs))
543         return 1;
544
545     locs->exposure = exposure;
546     locs->addr.sin_family = AF_INET;
547     locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
548     locs->addr.sin_port = notice->z_port;
549     return(0);
550 }
551
552 /*
553  * Parse the location information in the notice, and fill it into *locs
554  */
555
556 static int
557 ulogin_parse(notice, locs)
558     ZNotice_t *notice;
559     Location *locs;
560 {
561     char *cp, *base;
562     int nulls = 0;
563
564     if (!notice->z_message_len) {
565         syslog(LOG_ERR, "short ulogin");
566         return 1;
567     }
568
569     base = notice->z_message;
570     for (cp = base; cp < base + notice->z_message_len; cp++) {
571         if (!*cp)
572             nulls++;
573     }
574     if (nulls < 3) {
575         syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
576                notice->z_sender, nulls);
577         return 1;
578     }
579
580     locs->user = make_string(notice->z_class_inst,0);
581
582     cp = base;
583     locs->machine = make_string(cp,0);
584
585     cp += (strlen(cp) + 1);
586     locs->time = strsave(cp);
587
588     /* This field might not be null-terminated */
589     cp += (strlen(cp) + 1);
590     locs->tty = make_string(cp, 0);
591
592     return 0;
593 }       
594
595
596 static Location *
597 ulogin_find(user, host, port)
598     char *user;
599     struct in_addr *host;
600     unsigned int port;
601 {
602     Location *loc;
603     String *str;
604
605     /* Find the first location for this user. */
606     loc = ulogin_find_user(user);
607     if (!loc)
608         return NULL;
609
610     /* Look for a location which matches the host and port. */
611     str = make_string(user, 0);
612     while (loc < locations + num_locs && loc->user == str) {
613         if (loc->addr.sin_addr.s_addr == host->s_addr
614             && loc->addr.sin_port == port) {
615             free_string(str);
616             return loc;
617         }
618         loc++;
619     }
620
621     free_string(str);
622     return NULL;
623 }
624
625 /*
626  * Return a pointer to the first instance of this user@realm in the
627  * table.
628  */
629
630 static Location *
631 ulogin_find_user(user)
632     char *user;
633 {
634     int i, rlo, rhi;
635     int compar;
636     String *str;
637
638     if (!locations)
639         return(NULL);
640
641     str = make_string(user, 0);
642
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. */
645
646     i = num_locs / 2;
647     rlo = 0;
648     rhi = num_locs - 1;
649
650     while ((compar = comp_string(locations[i].user, str)) != 0) {
651         if (compar < 0)
652             rlo = i + 1;
653         else
654             rhi = i - 1;
655         if (rhi - rlo < 0) {
656             free_string(str);
657             return NULL;
658         }
659         i = (rhi + rlo) / 2;
660     }
661
662     /* Back up to the first location for this user. */
663     while (i > 0 && locations[i - 1].user == str)
664         i--;
665     free_string(str);
666     return &locations[i];
667 }
668
669 static int
670 ul_equiv(l1, l2)
671     Location *l1, *l2;
672 {
673     if (l1->machine != l2->machine)
674         return 0;
675     if (l1->tty != l2->tty)
676         return 0;
677     return 1;
678 }
679
680 /*
681  * remove the user specified in notice from the internal table
682  */
683
684 static Exposure_type
685 ulogin_remove_user(notice, who, err_return)
686     ZNotice_t *notice;
687     struct sockaddr_in *who;
688     int *err_return;
689 {
690     Location *new_locs, *loc;
691     int i = 0;
692     Exposure_type quiet;
693
694     *err_return = 0;
695     loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
696     if (!loc) {
697         *err_return = NOLOC;
698         return NONE;
699     }
700
701     quiet = loc->exposure;
702
703     if (--num_locs == 0) {      /* last one */
704         free_loc(locations);
705         free(locations);
706         locations = NULL;
707         return quiet;
708     }
709
710     new_locs = (Location *) malloc(num_locs * sizeof(Location));
711     if (!new_locs) {
712         syslog(LOG_CRIT, "ul_rem alloc");
713         abort();
714     }
715
716     /* copy old entries */
717     while (i < num_locs && &locations[i] < loc) {
718         new_locs[i] = locations[i];
719         i++;
720     }
721
722     /* free up this one */
723     free_loc(&locations[i]);
724     i++;                        /* skip over this one */
725
726     /* copy the rest */
727     while (i <= num_locs) {
728         new_locs[i - 1] = locations[i];
729         i++;
730     }
731
732     free(locations);
733
734     locations = new_locs;
735
736     /* all done */
737     return quiet;
738 }
739
740 /*
741  * remove all locs of the user specified in notice from the internal table
742  */
743
744 static void
745 ulogin_flush_user(notice)
746     ZNotice_t *notice;
747 {
748     Location *loc, *loc2;
749     int i, j, num_match, num_left;
750
751     i = num_match = num_left = 0;
752
753     if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
754         return;
755
756     /* compute # locations left in the list, after loc2 (inclusive) */
757     num_left = num_locs - (loc2 - locations);
758
759     while (num_left &&
760            !strcasecmp(loc2[num_match].user->string,
761                        notice->z_class_inst)) {
762         /* as long as we keep matching, march up the list */
763         num_match++;
764         num_left--;
765     }
766     if (num_locs == num_match) { /* no other locations left */
767         for (j = 0; j < num_match; j++)
768             free_loc(&locations[j]); /* free storage */
769         free (locations);
770         locations = NULL;
771         num_locs = 0;
772         return;
773     }
774
775     loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
776     if (!loc) {
777         syslog(LOG_CRIT, "ul_rem alloc");
778         abort();
779     }
780
781     /* copy old entries */
782     while (i < num_locs && &locations[i] < loc2) {
783         loc[i] = locations[i];
784         i++;
785     }
786         
787     for(j = 0; j < num_match; j++) {
788         free_loc(&locations[i]);
789         i++;
790     }
791
792     /* copy the rest */
793     while (i < num_locs) {
794         loc[i - num_match] = locations[i];
795         i++;
796     }
797
798     free(locations);
799
800     locations = loc;
801     num_locs -= num_match;
802
803 #ifdef DEBUG
804     if (zdebug) {
805         int i;
806
807         for (i = 0; i < num_locs; i++) {
808             syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
809                    (int) locations[i].exposure);
810         }
811     }
812 #endif
813 }
814
815
816 static void
817 ulogin_locate(notice, who, auth)
818     ZNotice_t *notice;
819     struct sockaddr_in *who;
820     int auth;
821 {
822     char **answer;
823     int found;
824     Code_t retval;
825     struct sockaddr_in send_to_who;
826     char *cp;
827     Realm *realm;
828
829     answer = ulogin_marshal_locs(notice, &found, auth);
830
831     /* XXX do more parsing, like in dispatch() */
832
833     if (found == 0) {
834         cp = strrchr(notice->z_class_inst, '@');
835         if (cp && (realm = realm_get_realm_by_name(cp + 1))) {
836             char *inlhsat;
837
838             /* XXX eeew. */
839             *cp = '\0';
840             inlhsat = strrchr(notice->z_class_inst, '@');
841
842             if (!inlhsat)
843                 *cp = '@';
844
845             ulogin_locate_forward(notice, who, realm);
846             return;
847         }
848     }
849
850     send_to_who = *who;
851     send_to_who.sin_port = notice->z_port;
852
853     retval = ZSetDestAddr(&send_to_who);
854     if (retval != ZERR_NONE) {
855         syslog(LOG_WARNING, "ulogin_locate set addr: %s",
856                error_message(retval));
857         if (answer)
858             free(answer);
859         return;
860     }
861
862     notice->z_kind = ACKED;
863
864     /* use xmit_frag() to send each piece of the notice */
865
866     retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
867     if (retval != ZERR_NONE)
868         syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
869     if (answer)
870         free(answer);
871 }
872
873 /*
874  * Locate the user and collect the locations into an array.  Return the # of
875  * locations in *found.
876  */
877
878 static char **
879 ulogin_marshal_locs(notice, found, auth)
880     ZNotice_t *notice;
881     int *found;
882     int auth;
883 {
884     Location **matches = (Location **) 0;
885     Location *loc;
886     char **answer;
887     int i = 0;
888     String *inst;
889     int local = (auth && realm_sender_in_realm(my_galaxy, notice->z_sender));
890
891     *found = 0;                 /* # of matches */
892
893     loc = ulogin_find_user(notice->z_class_inst);
894     if (!loc)
895         return(NULL);
896
897     i = loc - locations;
898
899     inst = make_string(notice->z_class_inst,0);
900     while (i < num_locs && (inst == locations[i].user)) {
901         /* these locations match */
902         switch (locations[i].exposure) {
903           case OPSTAFF_VIS:
904             i++;
905             continue;
906           case REALM_VIS:
907           case REALM_ANN:
908             if (!local) {
909                 i++;
910                 continue;
911             }
912           case NET_VIS:
913           case NET_ANN:
914           default:
915             break;
916         }
917         if (!*found) {
918             matches = (Location **) malloc(sizeof(Location *));
919             if (!matches) {
920                 syslog(LOG_ERR, "ulog_loc: no mem");
921                 break;  /* from the while */
922             }
923             matches[0] = &locations[i];
924             (*found)++;
925         } else {
926             matches = (Location **) realloc(matches,
927                                             ++(*found) * sizeof(Location *));
928             if (!matches) {
929                 syslog(LOG_ERR, "ulog_loc: realloc no mem");
930                 *found = 0;
931                 break;  /* from the while */
932             }
933             matches[*found - 1] = &locations[i];
934         }
935         i++;
936     }
937     free_string(inst);
938
939     /* OK, now we have a list of user@host's to return to the client
940        in matches */
941         
942 #ifdef DEBUG
943     if (zdebug) {
944         for (i = 0; i < *found ; i++)
945             zdbug((LOG_DEBUG,"found %s",
946                    matches[i]->user->string));
947     }
948 #endif
949         
950     /* coalesce the location information into a list of char *'s */
951     answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
952     if (!answer) {
953         syslog(LOG_ERR, "zloc no mem(answer)");
954         *found = 0;
955     } else
956         for (i = 0; i < *found ; i++) {
957             answer[i * NUM_FIELDS] = matches[i]->machine->string;
958             answer[i * NUM_FIELDS + 1] = matches[i]->time;
959             answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
960         }
961         
962     if (matches)
963         free(matches);
964     return answer;
965 }
966
967 void
968 uloc_dump_locs(fp)
969     FILE *fp;
970 {
971     int i;
972
973     for (i = 0; i < num_locs; i++) {
974         fputs("'", fp);
975         dump_quote(locations[i].user->string, fp);
976         fputs("' '", fp);
977         dump_quote(locations[i].machine->string, fp);
978         fputs("' '", fp);
979         dump_quote(locations[i].time, fp);
980         fputs("' '", fp);
981         dump_quote(locations[i].tty->string, fp);
982         fputs("' ", fp);
983         switch (locations[i].exposure) {
984           case OPSTAFF_VIS:
985             fputs("OPSTAFF", fp);
986             break;
987           case REALM_VIS:
988             fputs("RLM_VIS", fp);
989             break;
990           case REALM_ANN:
991             fputs("RLM_ANN", fp);
992             break;
993           case NET_VIS:
994             fputs("NET_VIS", fp);
995             break;
996           case NET_ANN:
997             fputs("NET_ANN", fp);
998             break;
999           default:
1000             fprintf(fp, "? %d ?", locations[i].exposure);
1001             break;
1002         }
1003         fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
1004                 ntohs(locations[i].addr.sin_port));
1005     }
1006 }
1007
1008 static void
1009 free_loc(loc)
1010     Location *loc;
1011 {
1012     free_string(loc->user);
1013     free_string(loc->machine);
1014     free_string(loc->tty);
1015     free(loc->time);
1016     return;
1017 }
1018
1019 static void
1020 ulogin_locate_forward(notice, who, realm)
1021     ZNotice_t *notice;
1022     struct sockaddr_in *who;
1023     Realm *realm;
1024 {
1025     ZNotice_t lnotice;
1026
1027     lnotice = *notice;
1028     lnotice.z_opcode = REALM_REQ_LOCATE;
1029   
1030     realm_handoff(&lnotice, 1, who, realm, 0);
1031 }
1032
1033 void
1034 ulogin_realm_locate(notice, who, realm)
1035     ZNotice_t *notice;
1036     struct sockaddr_in *who;
1037     Realm *realm;
1038 {
1039   char **answer;
1040   int found;
1041   Code_t retval;
1042   ZNotice_t lnotice;
1043   char *pack;
1044   int packlen;
1045   
1046 #ifdef DEBUG
1047   if (zdebug)
1048     zdbug((LOG_DEBUG, "ulogin_realm_locate"));
1049 #endif
1050   
1051   answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
1052   
1053   lnotice = *notice;
1054   lnotice.z_opcode = REALM_ANS_LOCATE;
1055   
1056   if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
1057     syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
1058            error_message(retval));
1059     
1060     if (answer)
1061       free(answer);
1062     return;
1063   }
1064   if (answer)
1065     free(answer);
1066   
1067   if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
1068     syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1069            error_message(retval));
1070     free(pack);
1071     return;
1072   }
1073   
1074   realm_handoff(&lnotice, 1, who, realm, 0);
1075   free(pack);
1076   
1077   return;
1078 }
1079
1080 void
1081 ulogin_relay_locate(notice, who)
1082     ZNotice_t *notice;
1083     struct sockaddr_in *who;
1084 {
1085   ZNotice_t lnotice;
1086   Code_t retval;
1087   struct sockaddr_in newwho;
1088   char *pack;
1089   int packlen;
1090   
1091   newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
1092   newwho.sin_port = notice->z_port;
1093   newwho.sin_family = AF_INET;
1094   
1095   if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
1096     syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
1097            error_message(retval));
1098     return;
1099   }
1100   
1101   lnotice = *notice;
1102   lnotice.z_opcode = LOCATE_LOCATE;
1103   lnotice.z_kind = ACKED;
1104   
1105   if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
1106     syslog(LOG_WARNING, "ulog_relay_loc format: %s",
1107            error_message(retval));
1108     return;
1109   }
1110   
1111   if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
1112     syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
1113            error_message(retval));
1114   }
1115   free(pack);
1116 }
1117