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