]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/uloc.c
finalize -3
[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, Realm *realm));
110
111 static Location *locations = NULL; /* ptr to first in array */
112 static int num_locs = 0;        /* number in array */
113
114 /*
115  * Dispatch a LOGIN notice.
116  */
117
118 Code_t
119 ulogin_dispatch(notice, auth, who, server)
120     ZNotice_t *notice;
121     int auth;
122     struct sockaddr_in *who;
123     Server *server;
124 {
125     Exposure_type retval;
126     int err_ret;
127
128     if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
129         retval = ulogin_remove_user(notice, who, &err_ret);
130         switch (retval) {
131           case NONE:
132             if (err_ret == UNAUTH) {
133                 if (server == me_server)
134                     clt_ack(notice, who, AUTH_FAILED);
135                 return ZERR_NONE;
136             } else if (err_ret == NOLOC) {
137                 if (server == me_server)
138                     clt_ack(notice, who, NOT_FOUND);
139                 return ZERR_NONE;
140             } 
141             syslog(LOG_ERR,"bogus location exposure NONE, %s",
142                    notice->z_sender);
143             break;
144           case OPSTAFF_VIS:
145           case REALM_VIS:
146             /* he is not announced to people.  Silently ack */
147             if (server == me_server)
148                 ack(notice, who);
149             break;
150           case REALM_ANN:
151           case NET_VIS:
152             if (server == me_server)
153                 sendit(notice, 1, who, 0);
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         syslog(LOG_ERR, "unknown ulog opcode %s", notice->z_opcode);
245         if (server == me_server)
246             nack(notice, who);
247         return ZERR_NONE;
248     }
249     if (server == me_server)
250         server_forward(notice, auth, who);
251     return ZERR_NONE;
252 }
253
254 static void
255 login_sendit(notice, auth, who, external)
256     ZNotice_t *notice;
257     int auth;
258     struct sockaddr_in *who;
259     int external;
260 {
261     ZNotice_t log_notice;
262
263     /* we must copy the notice struct here because we need the original
264        for forwarding.  We needn't copy the private data of the notice,
265        since that isn't modified by sendit and its subroutines. */
266
267     log_notice = *notice;
268
269     log_notice.z_opcode = LOGIN_USER_LOGIN;
270     sendit(&log_notice, auth, who, external);
271 }
272
273
274 /*
275  * Dispatch a LOCATE notice.
276  */
277 Code_t
278 ulocate_dispatch(notice, auth, who, server)
279     ZNotice_t *notice;
280     int auth;
281     struct sockaddr_in *who;
282     Server *server;
283 {
284     char *cp;
285     Realm *realm;
286
287     if (!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
288         /* we are talking to a current-rev client; send an ack */
289         ack(notice, who);
290         cp = strchr(notice->z_class_inst, '@');
291         if (cp && (realm = realm_get_realm_by_name(cp + 1)))
292             ulogin_locate_forward(notice, who, realm);
293         else
294             ulogin_locate(notice, who, auth);
295         return ZERR_NONE;
296     } else {
297         syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
298         if (server == me_server)
299             nack(notice, who);
300         return ZERR_NONE;
301     }
302 }
303
304 /*
305  * Flush all locations at the address.
306  */
307
308 void
309 uloc_hflush(addr)
310     struct in_addr *addr;
311 {
312     Location *loc;
313     int i = 0, new_num = 0;
314
315     if (num_locs == 0)
316         return;                 /* none to flush */
317
318     /* slightly inefficient, assume the worst, and allocate enough space */
319     loc = (Location *) malloc(num_locs *sizeof(Location));
320     if (!loc) {
321         syslog(LOG_CRIT, "uloc_flush alloc");
322         abort();
323     }
324
325     /* copy entries which don't match */
326     while (i < num_locs) {
327         if (locations[i].addr.sin_addr.s_addr != addr->s_addr)
328             loc[new_num++] = locations[i];
329         else
330             free_loc(&locations[i]);
331         i++;
332     }
333
334     free(locations);
335     locations = NULL;
336
337     if (!new_num) {
338         free(loc);
339         loc = NULL;
340         num_locs = new_num;
341
342         return;
343     }
344     locations = loc;
345     num_locs = new_num;
346
347     /* all done */
348     return;
349 }
350
351 void
352 uloc_flush_client(sin)
353     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()
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             break;
445         }
446         retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
447                                      loc->user->string, exposure_level, myname,
448                                      "", lyst, NUM_FIELDS);
449         if (retval != ZERR_NONE) {
450             syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
451             return(retval);
452         }
453     }
454     return ZERR_NONE;
455 }
456
457 /*
458  * Add the user to the internal table of locations.
459  */
460
461 static int
462 ulogin_add_user(notice, exposure, who)
463     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(notice, locs, exposure, who)
541     ZNotice_t *notice;
542     Location *locs;
543     Exposure_type exposure;
544     struct sockaddr_in *who;
545 {
546     if (ulogin_parse(notice, locs))
547         return 1;
548
549     locs->exposure = exposure;
550     locs->addr.sin_family = AF_INET;
551     locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
552     locs->addr.sin_port = notice->z_port;
553     return(0);
554 }
555
556 /*
557  * Parse the location information in the notice, and fill it into *locs
558  */
559
560 static int
561 ulogin_parse(notice, locs)
562     ZNotice_t *notice;
563     Location *locs;
564 {
565     char *cp, *base;
566     int nulls = 0;
567
568     if (!notice->z_message_len) {
569         syslog(LOG_ERR, "short ulogin");
570         return 1;
571     }
572
573     base = notice->z_message;
574     for (cp = base; cp < base + notice->z_message_len; cp++) {
575         if (!*cp)
576             nulls++;
577     }
578     if (nulls < 3) {
579         syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
580                notice->z_sender, nulls);
581         return 1;
582     }
583
584     locs->user = make_string(notice->z_class_inst,0);
585
586     cp = base;
587     locs->machine = make_string(cp,0);
588
589     cp += (strlen(cp) + 1);
590     locs->time = strsave(cp);
591
592     /* This field might not be null-terminated */
593     cp += (strlen(cp) + 1);
594     locs->tty = make_string(cp, 0);
595
596     return 0;
597 }       
598
599
600 static Location *
601 ulogin_find(user, host, port)
602     char *user;
603     struct in_addr *host;
604     unsigned int port;
605 {
606     Location *loc;
607     String *str;
608
609     /* Find the first location for this user. */
610     loc = ulogin_find_user(user);
611     if (!loc)
612         return NULL;
613
614     /* Look for a location which matches the host and port. */
615     str = make_string(user, 0);
616     while (loc < locations + num_locs && loc->user == str) {
617         if (loc->addr.sin_addr.s_addr == host->s_addr
618             && loc->addr.sin_port == port) {
619             free_string(str);
620             return loc;
621         }
622         loc++;
623     }
624
625     free_string(str);
626     return NULL;
627 }
628
629 /*
630  * Return a pointer to the first instance of this user@realm in the
631  * table.
632  */
633
634 static Location *
635 ulogin_find_user(user)
636     char *user;
637 {
638     int i, rlo, rhi;
639     int compar;
640     String *str;
641
642     if (!locations)
643         return(NULL);
644
645     str = make_string(user, 0);
646
647     /* i is the current midpoint location, rlo is the lowest we will
648      * still check, and rhi is the highest we will still check. */
649
650     i = num_locs / 2;
651     rlo = 0;
652     rhi = num_locs - 1;
653
654     while ((compar = comp_string(locations[i].user, str)) != 0) {
655         if (compar < 0)
656             rlo = i + 1;
657         else
658             rhi = i - 1;
659         if (rhi - rlo < 0) {
660             free_string(str);
661             return NULL;
662         }
663         i = (rhi + rlo) / 2;
664     }
665
666     /* Back up to the first location for this user. */
667     while (i > 0 && locations[i - 1].user == str)
668         i--;
669     free_string(str);
670     return &locations[i];
671 }
672
673 static int
674 ul_equiv(l1, l2)
675     Location *l1, *l2;
676 {
677     if (l1->machine != l2->machine)
678         return 0;
679     if (l1->tty != l2->tty)
680         return 0;
681     return 1;
682 }
683
684 /*
685  * remove the user specified in notice from the internal table
686  */
687
688 static Exposure_type
689 ulogin_remove_user(notice, who, err_return)
690     ZNotice_t *notice;
691     struct sockaddr_in *who;
692     int *err_return;
693 {
694     Location *new_locs, *loc;
695     int i = 0;
696     Exposure_type quiet;
697
698     *err_return = 0;
699     loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
700     if (!loc) {
701         *err_return = NOLOC;
702         return NONE;
703     }
704
705     quiet = loc->exposure;
706
707     if (--num_locs == 0) {      /* last one */
708         free_loc(locations);
709         free(locations);
710         locations = NULL;
711         return quiet;
712     }
713
714     new_locs = (Location *) malloc(num_locs * sizeof(Location));
715     if (!new_locs) {
716         syslog(LOG_CRIT, "ul_rem alloc");
717         abort();
718     }
719
720     /* copy old entries */
721     while (i < num_locs && &locations[i] < loc) {
722         new_locs[i] = locations[i];
723         i++;
724     }
725
726     /* free up this one */
727     free_loc(&locations[i]);
728     i++;                        /* skip over this one */
729
730     /* copy the rest */
731     while (i <= num_locs) {
732         new_locs[i - 1] = locations[i];
733         i++;
734     }
735
736     free(locations);
737
738     locations = new_locs;
739
740     /* all done */
741     return quiet;
742 }
743
744 /*
745  * remove all locs of the user specified in notice from the internal table
746  */
747
748 static void
749 ulogin_flush_user(notice)
750     ZNotice_t *notice;
751 {
752     Location *loc, *loc2;
753     int i, j, num_match, num_left;
754
755     i = num_match = num_left = 0;
756
757     if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
758         return;
759
760     /* compute # locations left in the list, after loc2 (inclusive) */
761     num_left = num_locs - (loc2 - locations);
762
763     while (num_left &&
764            !strcasecmp(loc2[num_match].user->string,
765                        notice->z_class_inst)) {
766         /* as long as we keep matching, march up the list */
767         num_match++;
768         num_left--;
769     }
770     if (num_locs == num_match) { /* no other locations left */
771         for (j = 0; j < num_match; j++)
772             free_loc(&locations[j]); /* free storage */
773         free (locations);
774         locations = NULL;
775         num_locs = 0;
776         return;
777     }
778
779     loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
780     if (!loc) {
781         syslog(LOG_CRIT, "ul_rem alloc");
782         abort();
783     }
784
785     /* copy old entries */
786     while (i < num_locs && &locations[i] < loc2) {
787         loc[i] = locations[i];
788         i++;
789     }
790         
791     for(j = 0; j < num_match; j++) {
792         free_loc(&locations[i]);
793         i++;
794     }
795
796     /* copy the rest */
797     while (i < num_locs) {
798         loc[i - num_match] = locations[i];
799         i++;
800     }
801
802     free(locations);
803
804     locations = loc;
805     num_locs -= num_match;
806
807 #ifdef DEBUG
808     if (zdebug) {
809         int i;
810
811         for (i = 0; i < num_locs; i++) {
812             syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
813                    (int) locations[i].exposure);
814         }
815     }
816 #endif
817 }
818
819
820 static void
821 ulogin_locate(notice, who, auth)
822     ZNotice_t *notice;
823     struct sockaddr_in *who;
824     int auth;
825 {
826     char **answer;
827     int found;
828     Code_t retval;
829     struct sockaddr_in send_to_who;
830
831     answer = ulogin_marshal_locs(notice, &found, auth);
832
833     send_to_who = *who;
834     send_to_who.sin_port = notice->z_port;
835
836     retval = ZSetDestAddr(&send_to_who);
837     if (retval != ZERR_NONE) {
838         syslog(LOG_WARNING, "ulogin_locate set addr: %s",
839                error_message(retval));
840         if (answer)
841             free(answer);
842         return;
843     }
844
845     notice->z_kind = ACKED;
846
847     /* use xmit_frag() to send each piece of the notice */
848
849     retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
850     if (retval != ZERR_NONE)
851         syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
852     if (answer)
853         free(answer);
854 }
855
856 /*
857  * Locate the user and collect the locations into an array.  Return the # of
858  * locations in *found.
859  */
860
861 static char **
862 ulogin_marshal_locs(notice, found, auth)
863     ZNotice_t *notice;
864     int *found;
865     int auth;
866 {
867     Location **matches = (Location **) 0;
868     Location *loc;
869     char **answer;
870     int i = 0;
871     String *inst;
872     int local = (auth && realm_sender_in_realm(ZGetRealm(), notice->z_sender));
873
874     *found = 0;                 /* # of matches */
875
876     loc = ulogin_find_user(notice->z_class_inst);
877     if (!loc)
878         return(NULL);
879
880     i = loc - locations;
881
882     inst = make_string(notice->z_class_inst,0);
883     while (i < num_locs && (inst == locations[i].user)) {
884         /* these locations match */
885         switch (locations[i].exposure) {
886           case OPSTAFF_VIS:
887             i++;
888             continue;
889           case REALM_VIS:
890           case REALM_ANN:
891             if (!local) {
892                 i++;
893                 continue;
894             }
895           case NET_VIS:
896           case NET_ANN:
897           default:
898             break;
899         }
900         if (!*found) {
901             matches = (Location **) malloc(sizeof(Location *));
902             if (!matches) {
903                 syslog(LOG_ERR, "ulog_loc: no mem");
904                 break;  /* from the while */
905             }
906             matches[0] = &locations[i];
907             (*found)++;
908         } else {
909             matches = (Location **) realloc(matches,
910                                             ++(*found) * sizeof(Location *));
911             if (!matches) {
912                 syslog(LOG_ERR, "ulog_loc: realloc no mem");
913                 *found = 0;
914                 break;  /* from the while */
915             }
916             matches[*found - 1] = &locations[i];
917         }
918         i++;
919     }
920     free_string(inst);
921
922     /* OK, now we have a list of user@host's to return to the client
923        in matches */
924         
925         
926 #ifdef DEBUG
927     if (zdebug) {
928         for (i = 0; i < *found ; i++)
929             zdbug((LOG_DEBUG,"found %s",
930                    matches[i]->user->string));
931     }
932 #endif
933         
934     /* coalesce the location information into a list of char *'s */
935     answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
936     if (!answer) {
937         syslog(LOG_ERR, "zloc no mem(answer)");
938         *found = 0;
939     } else
940         for (i = 0; i < *found ; i++) {
941             answer[i * NUM_FIELDS] = matches[i]->machine->string;
942             answer[i * NUM_FIELDS + 1] = matches[i]->time;
943             answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
944         }
945         
946     if (matches)
947         free(matches);
948     return answer;
949 }
950
951 void
952 uloc_dump_locs(fp)
953     FILE *fp;
954 {
955     int i;
956
957     for (i = 0; i < num_locs; i++) {
958         fputs("'", fp);
959         dump_quote(locations[i].user->string, fp);
960         fputs("' '", fp);
961         dump_quote(locations[i].machine->string, fp);
962         fputs("' '", fp);
963         dump_quote(locations[i].time, fp);
964         fputs("' '", fp);
965         dump_quote(locations[i].tty->string, fp);
966         fputs("' ", fp);
967         switch (locations[i].exposure) {
968           case OPSTAFF_VIS:
969             fputs("OPSTAFF", fp);
970             break;
971           case REALM_VIS:
972             fputs("RLM_VIS", fp);
973             break;
974           case REALM_ANN:
975             fputs("RLM_ANN", fp);
976             break;
977           case NET_VIS:
978             fputs("NET_VIS", fp);
979             break;
980           case NET_ANN:
981             fputs("NET_ANN", fp);
982             break;
983           default:
984             fprintf(fp, "? %d ?", locations[i].exposure);
985             break;
986         }
987         fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
988                 ntohs(locations[i].addr.sin_port));
989     }
990 }
991
992 static void
993 free_loc(loc)
994     Location *loc;
995 {
996     free_string(loc->user);
997     free_string(loc->machine);
998     free_string(loc->tty);
999     free(loc->time);
1000     return;
1001 }
1002
1003 static void
1004 ulogin_locate_forward(notice, who, realm)
1005     ZNotice_t *notice;
1006     struct sockaddr_in *who;
1007     Realm *realm;
1008 {
1009     ZNotice_t lnotice;
1010
1011     lnotice = *notice;
1012     lnotice.z_opcode = REALM_REQ_LOCATE;
1013   
1014     realm_handoff(&lnotice, 1, who, realm, 0);
1015 }
1016
1017 void
1018 ulogin_realm_locate(notice, who, realm)
1019     ZNotice_t *notice;
1020     struct sockaddr_in *who;
1021     Realm *realm;
1022 {
1023   char **answer;
1024   int found;
1025   Code_t retval;
1026   ZNotice_t lnotice;
1027   char *pack;
1028   int packlen;
1029   
1030 #ifdef DEBUG
1031   if (zdebug)
1032     zdbug((LOG_DEBUG, "ulogin_realm_locate"));
1033 #endif
1034   
1035   answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
1036   
1037   lnotice = *notice;
1038   lnotice.z_opcode = REALM_ANS_LOCATE;
1039   
1040   if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
1041     syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
1042            error_message(retval));
1043     
1044     if (answer)
1045       free(answer);
1046     return;
1047   }
1048   if (answer)
1049     free(answer);
1050   
1051   if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
1052     syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1053            error_message(retval));
1054     free(pack);
1055     return;
1056   }
1057   
1058   realm_handoff(&lnotice, 1, who, realm, 0);
1059   free(pack);
1060   
1061   return;
1062 }
1063
1064 void
1065 ulogin_relay_locate(notice, who)
1066     ZNotice_t *notice;
1067     struct sockaddr_in *who;
1068 {
1069   ZNotice_t lnotice;
1070   Code_t retval;
1071   struct sockaddr_in newwho;
1072   char *pack;
1073   int packlen;
1074   
1075   newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
1076   newwho.sin_port = notice->z_port;
1077   newwho.sin_family = AF_INET;
1078   
1079   if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
1080     syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
1081            error_message(retval));
1082     return;
1083   }
1084   
1085   lnotice = *notice;
1086   lnotice.z_opcode = LOCATE_LOCATE;
1087   lnotice.z_kind = ACKED;
1088   
1089   if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
1090     syslog(LOG_WARNING, "ulog_relay_loc format: %s",
1091            error_message(retval));
1092     return;
1093   }
1094   
1095   if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
1096     syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
1097            error_message(retval));
1098   }
1099   free(pack);
1100 }
1101