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