]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/uloc.c
r4275@bucket (orig r265): kcr | 2008-01-21 02:57:32 -0500
[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$
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$";
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 void free_loc __P((Location *loc));
106 static void ulogin_locate_forward __P((ZNotice_t *notice,
107                                        struct sockaddr_in *who, ZRealm *realm));
108
109 static Location *locations = NULL; /* ptr to first in array */
110 static int num_locs = 0;        /* number in array */
111
112 /*
113  * Dispatch a LOGIN notice.
114  */
115
116 Code_t
117 ulogin_dispatch(ZNotice_t *notice,
118                 int auth,
119                 struct sockaddr_in *who,
120                 Server *server)
121 {
122     Exposure_type retval;
123     int err_ret;
124
125     if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
126         retval = ulogin_remove_user(notice, who, &err_ret);
127         switch (retval) {
128           case NONE:
129             if (err_ret == UNAUTH) {
130                 if (server == me_server)
131                     clt_ack(notice, who, AUTH_FAILED);
132                 return ZERR_NONE;
133             } else if (err_ret == NOLOC) {
134                 if (server == me_server)
135                     clt_ack(notice, who, NOT_FOUND);
136                 return ZERR_NONE;
137             } 
138             syslog(LOG_ERR,"bogus location exposure NONE, %s",
139                    notice->z_sender);
140             break;
141           case OPSTAFF_VIS:
142           case REALM_VIS:
143             /* he is not announced to people.  Silently ack */
144             if (server == me_server)
145                 ack(notice, who);
146             break;
147           case REALM_ANN:
148           case NET_VIS:
149             if (server == me_server)
150                 sendit(notice, 1, who, 0);
151             break;
152           case NET_ANN:
153             /* currently no distinction between these.
154                just announce */
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
158                ip addrs */
159             if (server == me_server)
160                 sendit(notice, 1, who, 1);
161             break;
162           default:
163             syslog(LOG_ERR,"bogus location exposure %d/%s",
164                    (int) retval, notice->z_sender);
165             break;
166         }
167         if (server == me_server) /* tell the other servers */
168             server_forward(notice, auth, who);
169         return ZERR_NONE;
170     }
171     if (!bdumping && 
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);
177         return ZERR_NONE;
178     }
179     if (strcmp(notice->z_opcode, LOGIN_USER_FLUSH) == 0) {
180         ulogin_flush_user(notice);
181         if (server == me_server)
182             ack(notice, who);
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);
188             return ZERR_NONE;
189         } else if (err_ret == NOLOC) {
190             if (server == me_server)
191                 clt_ack(notice, who, NOT_FOUND);
192             return ZERR_NONE;
193         }
194         if (server == me_server) {
195             ack(notice, who);
196             server_forward(notice, auth, who);
197         }
198         return ZERR_NONE;
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) {
202             if (err_ret)
203                 nack(notice, who);
204             else
205                 ack(notice, who);
206         }
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,
210                                       so we ack it here */
211             if (err_ret)
212                 nack(notice, who);
213             else
214                 ack(notice, who);
215         }
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 */
219             if (err_ret)
220                 nack(notice, who);
221             else
222                 login_sendit(notice, auth, who, 0);
223         }
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 */
227             if (err_ret)
228                 nack(notice, who);
229             else
230                 login_sendit(notice, auth, who, 0);
231         }
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 */
235             if (err_ret)
236                 nack(notice, who);
237             else
238                 login_sendit(notice, auth, who, 1);
239         }
240     } else {
241         if (!strcmp(notice->z_opcode, LOGIN_USER_LOGIN)) {
242             zdbug((LOG_DEBUG, "ulog opcode from unknown foreign realm %s", 
243                    notice->z_opcode));
244         } else {
245             syslog(LOG_ERR, "unknown ulog opcode %s", notice->z_opcode);
246         }
247         if (server == me_server)
248             nack(notice, who);
249         return ZERR_NONE;
250     }
251     if (server == me_server)
252         server_forward(notice, auth, who);
253     return ZERR_NONE;
254 }
255
256 static void
257 login_sendit(ZNotice_t *notice,
258              int auth,
259              struct sockaddr_in *who,
260              int external)
261 {
262     ZNotice_t log_notice;
263
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. */
267
268     log_notice = *notice;
269
270     log_notice.z_opcode = LOGIN_USER_LOGIN;
271     sendit(&log_notice, auth, who, external);
272 }
273
274
275 /*
276  * Dispatch a LOCATE notice.
277  */
278 Code_t
279 ulocate_dispatch(ZNotice_t *notice,
280                  int auth,
281                  struct sockaddr_in *who,
282                  Server *server)
283 {
284     char *cp;
285     ZRealm *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         cp = strchr(notice->z_class_inst, '@');
291         if (cp && (realm = realm_get_realm_by_name(cp + 1)))
292             ulogin_locate_forward(notice, who, realm);
293         else
294             ulogin_locate(notice, who, auth);
295         return ZERR_NONE;
296     } else {
297         syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
298         if (server == me_server)
299             nack(notice, who);
300         return ZERR_NONE;
301     }
302 }
303
304 /*
305  * Flush all locations at the address.
306  */
307
308 void
309 uloc_hflush(struct in_addr *addr)
310 {
311     Location *loc;
312     int i = 0, new_num = 0;
313
314     if (num_locs == 0)
315         return;                 /* none to flush */
316
317     /* slightly inefficient, assume the worst, and allocate enough space */
318     loc = (Location *) malloc(num_locs *sizeof(Location));
319     if (!loc) {
320         syslog(LOG_CRIT, "uloc_flush alloc");
321         abort();
322     }
323
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];
328         else
329             free_loc(&locations[i]);
330         i++;
331     }
332
333     free(locations);
334     locations = NULL;
335
336     if (!new_num) {
337         free(loc);
338         loc = NULL;
339         num_locs = new_num;
340
341         return;
342     }
343     locations = loc;
344     num_locs = new_num;
345
346     /* all done */
347     return;
348 }
349
350 void
351 uloc_flush_client(struct sockaddr_in *sin)
352 {
353     Location *loc;
354     int i = 0, new_num = 0;
355
356     if (num_locs == 0)
357         return;                 /* none to flush */
358
359     /* slightly inefficient, assume the worst, and allocate enough space */
360     loc = (Location *) malloc(num_locs *sizeof(Location));
361     if (!loc) {
362         syslog(LOG_CRIT, "uloc_flush_clt alloc");
363         abort();
364     }
365
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];
371         } else {
372             free_loc(&locations[i]);
373         }
374         i++;
375     }
376
377     free(locations);
378     locations = NULL;
379
380     if (!new_num) {
381         free(loc);
382         loc = NULL;
383         num_locs = new_num;
384
385         return;
386     }
387     locations = loc;
388     num_locs = new_num;
389
390 #ifdef DEBUG
391     if (zdebug) {
392         int i;
393
394         for (i = 0; i < num_locs; i++) {
395             syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
396                    (int) locations[i].exposure);
397         }
398     }
399 #endif
400     /* all done */
401     return;
402 }
403
404 /*
405  * Send the locations for host for a brain dump
406  */
407
408 /*ARGSUSED*/
409 Code_t
410 uloc_send_locations(void)
411 {
412     Location *loc;
413     int i;
414     char *lyst[NUM_FIELDS];
415     char *exposure_level;
416     Code_t retval;
417
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;
422
423         switch (loc->exposure) {
424           case OPSTAFF_VIS:
425             exposure_level = EXPOSE_OPSTAFF;
426             break;
427           case REALM_VIS:
428             exposure_level = EXPOSE_REALMVIS;
429             break;
430           case REALM_ANN:
431             exposure_level = EXPOSE_REALMANN;
432             break;
433           case NET_VIS:
434             exposure_level = EXPOSE_NETVIS;
435             break;
436           case NET_ANN:
437             exposure_level = EXPOSE_NETANN;
438             break;
439           default:
440             syslog(LOG_ERR,"broken location state %s/%d",
441                    loc->user->string, (int) loc->exposure);
442             exposure_level = EXPOSE_OPSTAFF;
443             break;
444         }
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));
450             return(retval);
451         }
452     }
453     return ZERR_NONE;
454 }
455
456 /*
457  * Add the user to the internal table of locations.
458  */
459
460 static int
461 ulogin_add_user(ZNotice_t *notice,
462                 Exposure_type exposure,
463                 struct sockaddr_in *who)
464 {
465     Location *loc, *oldlocs, newloc;
466     int i;
467
468     loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
469     if (loc) {
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);
475             free(loc->time);
476             loc->time = strsave(newloc.time);
477             free_loc(&newloc);
478         }
479         return 0;
480     }
481
482     oldlocs = locations;
483
484     locations = (Location *) malloc((num_locs + 1) * sizeof(Location));
485     if (!locations) {
486         syslog(LOG_ERR, "zloc mem alloc");
487         locations = oldlocs;
488         return 1;
489     }
490
491     if (num_locs == 0) {        /* first one */
492         if (ulogin_setup(notice, locations, exposure, who)) {
493             free(locations);
494             locations = NULL;
495             return 1;
496         }
497         num_locs = 1;
498         goto dprnt;
499     }
500
501     /* not the first one, insert him */
502
503     if (ulogin_setup(notice, &newloc, exposure, who)) {
504         free(locations);
505         locations = oldlocs;
506         return 1;
507     }
508     num_locs++;
509
510     /* copy old locs */
511     i = 0;
512     while ((i < num_locs-1) &&
513            (comp_string(oldlocs[i].user,newloc.user) < 0)) {
514         locations[i] = oldlocs[i];
515         i++;
516     }
517
518     /* add him in here */
519     locations[i++] = newloc;
520
521     /* copy the rest */
522     while (i < num_locs) {
523         locations[i] = oldlocs[i - 1];
524         i++;
525     }
526     if (oldlocs)
527         free(oldlocs);
528
529   dprnt:
530     return 0;
531 }
532
533 /*
534  * Set up the location locs with the information in the notice.
535  */ 
536
537 static int
538 ulogin_setup(ZNotice_t *notice,
539              Location *locs,
540              Exposure_type exposure,
541              struct sockaddr_in *who)
542 {
543     if (ulogin_parse(notice, locs))
544         return 1;
545
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;
550     return(0);
551 }
552
553 /*
554  * Parse the location information in the notice, and fill it into *locs
555  */
556
557 static int
558 ulogin_parse(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(char *user,
598             struct in_addr *host,
599             unsigned int port)
600 {
601     Location *loc;
602     String *str;
603
604     /* Find the first location for this user. */
605     loc = ulogin_find_user(user);
606     if (!loc)
607         return NULL;
608
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) {
614             free_string(str);
615             return loc;
616         }
617         loc++;
618     }
619
620     free_string(str);
621     return NULL;
622 }
623
624 /*
625  * Return a pointer to the first instance of this user@realm in the
626  * table.
627  */
628
629 static Location *
630 ulogin_find_user(char *user)
631 {
632     int i, rlo, rhi;
633     int compar;
634     String *str;
635
636     if (!locations)
637         return(NULL);
638
639     str = make_string(user, 0);
640
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. */
643
644     i = num_locs / 2;
645     rlo = 0;
646     rhi = num_locs - 1;
647
648     while ((compar = comp_string(locations[i].user, str)) != 0) {
649         if (compar < 0)
650             rlo = i + 1;
651         else
652             rhi = i - 1;
653         if (rhi - rlo < 0) {
654             free_string(str);
655             return NULL;
656         }
657         i = (rhi + rlo) / 2;
658     }
659
660     /* Back up to the first location for this user. */
661     while (i > 0 && locations[i - 1].user == str)
662         i--;
663     free_string(str);
664     return &locations[i];
665 }
666
667 /*
668  * remove the user specified in notice from the internal table
669  */
670
671 static Exposure_type
672 ulogin_remove_user(ZNotice_t *notice,
673                    struct sockaddr_in *who,
674                    int *err_return)
675 {
676     Location *new_locs, *loc;
677     int i = 0;
678     Exposure_type quiet;
679
680     *err_return = 0;
681     loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
682     if (!loc) {
683         *err_return = NOLOC;
684         return NONE;
685     }
686
687     quiet = loc->exposure;
688
689     if (--num_locs == 0) {      /* last one */
690         free_loc(locations);
691         free(locations);
692         locations = NULL;
693         return quiet;
694     }
695
696     new_locs = (Location *) malloc(num_locs * sizeof(Location));
697     if (!new_locs) {
698         syslog(LOG_CRIT, "ul_rem alloc");
699         abort();
700     }
701
702     /* copy old entries */
703     while (i < num_locs && &locations[i] < loc) {
704         new_locs[i] = locations[i];
705         i++;
706     }
707
708     /* free up this one */
709     free_loc(&locations[i]);
710     i++;                        /* skip over this one */
711
712     /* copy the rest */
713     while (i <= num_locs) {
714         new_locs[i - 1] = locations[i];
715         i++;
716     }
717
718     free(locations);
719
720     locations = new_locs;
721
722     /* all done */
723     return quiet;
724 }
725
726 /*
727  * remove all locs of the user specified in notice from the internal table
728  */
729
730 static void
731 ulogin_flush_user(ZNotice_t *notice)
732 {
733     Location *loc, *loc2;
734     int i, j, num_match, num_left;
735
736     i = num_match = num_left = 0;
737
738     if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
739         return;
740
741     /* compute # locations left in the list, after loc2 (inclusive) */
742     num_left = num_locs - (loc2 - locations);
743
744     while (num_left &&
745            !strcasecmp(loc2[num_match].user->string,
746                        notice->z_class_inst)) {
747         /* as long as we keep matching, march up the list */
748         num_match++;
749         num_left--;
750     }
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 */
754         free (locations);
755         locations = NULL;
756         num_locs = 0;
757         return;
758     }
759
760     loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
761     if (!loc) {
762         syslog(LOG_CRIT, "ul_rem alloc");
763         abort();
764     }
765
766     /* copy old entries */
767     while (i < num_locs && &locations[i] < loc2) {
768         loc[i] = locations[i];
769         i++;
770     }
771         
772     for(j = 0; j < num_match; j++) {
773         free_loc(&locations[i]);
774         i++;
775     }
776
777     /* copy the rest */
778     while (i < num_locs) {
779         loc[i - num_match] = locations[i];
780         i++;
781     }
782
783     free(locations);
784
785     locations = loc;
786     num_locs -= num_match;
787
788 #ifdef DEBUG
789     if (zdebug) {
790         int i;
791
792         for (i = 0; i < num_locs; i++) {
793             syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
794                    (int) locations[i].exposure);
795         }
796     }
797 #endif
798 }
799
800
801 static void
802 ulogin_locate(ZNotice_t *notice,
803               struct sockaddr_in *who,
804               int auth)
805 {
806     char **answer;
807     int found;
808     Code_t retval;
809     struct sockaddr_in send_to_who;
810
811     answer = ulogin_marshal_locs(notice, &found, auth);
812
813     send_to_who = *who;
814     send_to_who.sin_port = notice->z_port;
815
816     retval = ZSetDestAddr(&send_to_who);
817     if (retval != ZERR_NONE) {
818         syslog(LOG_WARNING, "ulogin_locate set addr: %s",
819                error_message(retval));
820         if (answer)
821             free(answer);
822         return;
823     }
824
825     notice->z_kind = ACKED;
826
827     /* use xmit_frag() to send each piece of the notice */
828
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));
832     if (answer)
833         free(answer);
834 }
835
836 /*
837  * Locate the user and collect the locations into an array.  Return the # of
838  * locations in *found.
839  */
840
841 static char **
842 ulogin_marshal_locs(ZNotice_t *notice,
843                     int *found,
844                     int auth) 
845 {
846     Location **matches = (Location **) 0;
847     Location *loc;
848     char **answer;
849     int i = 0;
850     String *inst;
851     int local = (auth && realm_sender_in_realm(ZGetRealm(), notice->z_sender));
852
853     *found = 0;                 /* # of matches */
854
855     loc = ulogin_find_user(notice->z_class_inst);
856     if (!loc)
857         return(NULL);
858
859     i = loc - locations;
860
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) {
865           case OPSTAFF_VIS:
866             i++;
867             continue;
868           case REALM_VIS:
869           case REALM_ANN:
870             if (!local) {
871                 i++;
872                 continue;
873             }
874           case NET_VIS:
875           case NET_ANN:
876           default:
877             break;
878         }
879         if (!*found) {
880             matches = (Location **) malloc(sizeof(Location *));
881             if (!matches) {
882                 syslog(LOG_ERR, "ulog_loc: no mem");
883                 break;  /* from the while */
884             }
885             matches[0] = &locations[i];
886             (*found)++;
887         } else {
888             matches = (Location **) realloc(matches,
889                                             ++(*found) * sizeof(Location *));
890             if (!matches) {
891                 syslog(LOG_ERR, "ulog_loc: realloc no mem");
892                 *found = 0;
893                 break;  /* from the while */
894             }
895             matches[*found - 1] = &locations[i];
896         }
897         i++;
898     }
899     free_string(inst);
900
901     /* OK, now we have a list of user@host's to return to the client
902        in matches */
903         
904         
905 #ifdef DEBUG
906     if (zdebug) {
907         for (i = 0; i < *found ; i++)
908             zdbug((LOG_DEBUG,"found %s",
909                    matches[i]->user->string));
910     }
911 #endif
912         
913     /* coalesce the location information into a list of char *'s */
914     answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
915     if (!answer) {
916         syslog(LOG_ERR, "zloc no mem(answer)");
917         *found = 0;
918     } else
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;
923         }
924         
925     if (matches)
926         free(matches);
927     return answer;
928 }
929
930 void
931 uloc_dump_locs(FILE *fp)
932 {
933     int i;
934
935     for (i = 0; i < num_locs; i++) {
936         fputs("'", fp);
937         dump_quote(locations[i].user->string, fp);
938         fputs("' '", fp);
939         dump_quote(locations[i].machine->string, fp);
940         fputs("' '", fp);
941         dump_quote(locations[i].time, fp);
942         fputs("' '", fp);
943         dump_quote(locations[i].tty->string, fp);
944         fputs("' ", fp);
945         switch (locations[i].exposure) {
946           case OPSTAFF_VIS:
947             fputs("OPSTAFF", fp);
948             break;
949           case REALM_VIS:
950             fputs("RLM_VIS", fp);
951             break;
952           case REALM_ANN:
953             fputs("RLM_ANN", fp);
954             break;
955           case NET_VIS:
956             fputs("NET_VIS", fp);
957             break;
958           case NET_ANN:
959             fputs("NET_ANN", fp);
960             break;
961           default:
962             fprintf(fp, "? %d ?", locations[i].exposure);
963             break;
964         }
965         fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
966                 ntohs(locations[i].addr.sin_port));
967     }
968 }
969
970 static void
971 free_loc(Location *loc)
972 {
973     free_string(loc->user);
974     free_string(loc->machine);
975     free_string(loc->tty);
976     free(loc->time);
977     return;
978 }
979
980 static void
981 ulogin_locate_forward(ZNotice_t *notice,
982                       struct sockaddr_in *who,
983                       ZRealm *realm)
984 {
985     ZNotice_t lnotice;
986
987     lnotice = *notice;
988     lnotice.z_opcode = REALM_REQ_LOCATE;
989   
990     realm_handoff(&lnotice, 1, who, realm, 0);
991 }
992
993 void
994 ulogin_realm_locate(ZNotice_t *notice,
995                     struct sockaddr_in *who,
996                     ZRealm *realm)
997 {
998   char **answer;
999   int found;
1000   Code_t retval;
1001   ZNotice_t lnotice;
1002   char *pack;
1003   int packlen;
1004   
1005 #ifdef DEBUG
1006   if (zdebug)
1007     zdbug((LOG_DEBUG, "ulogin_realm_locate"));
1008 #endif
1009   
1010   answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
1011   
1012   lnotice = *notice;
1013   lnotice.z_opcode = REALM_ANS_LOCATE;
1014   
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));
1018     
1019     if (answer)
1020       free(answer);
1021     return;
1022   }
1023   if (answer)
1024     free(answer);
1025   
1026   if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
1027     syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1028            error_message(retval));
1029     free(pack);
1030     return;
1031   }
1032   
1033   realm_handoff(&lnotice, 1, who, realm, 0);
1034   free(pack);
1035   
1036   return;
1037 }
1038
1039 void
1040 ulogin_relay_locate(ZNotice_t *notice,
1041                     struct sockaddr_in *who)
1042 {
1043   ZNotice_t lnotice;
1044   Code_t retval;
1045   struct sockaddr_in newwho;
1046   char *pack;
1047   int packlen;
1048   
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;
1052   
1053   if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
1054     syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
1055            error_message(retval));
1056     return;
1057   }
1058   
1059   lnotice = *notice;
1060   lnotice.z_opcode = LOCATE_LOCATE;
1061   lnotice.z_kind = ACKED;
1062   lnotice.z_auth = 0;
1063   lnotice.z_authent_len = 0;
1064   lnotice.z_ascii_authent = "";
1065   lnotice.z_checksum = 0;
1066   lnotice.z_ascii_checksum = "";
1067   
1068   if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
1069     syslog(LOG_WARNING, "ulog_relay_loc format: %s",
1070            error_message(retval));
1071     return;
1072   }
1073   
1074   if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
1075     syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
1076            error_message(retval));
1077   }
1078   free(pack);
1079 }
1080