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