]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/uloc.c
66071cf3bbac1d1cdcb686b712d4ba55868467b4
[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         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             exposure_level = EXPOSE_OPSTAFF;
449             break;
450         }
451         retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
452                                      loc->user->string, exposure_level, myname,
453                                      "", lyst, NUM_FIELDS);
454         if (retval != ZERR_NONE) {
455             syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
456             return(retval);
457         }
458     }
459     return ZERR_NONE;
460 }
461
462 /*
463  * Add the user to the internal table of locations.
464  */
465
466 static int
467 ulogin_add_user(notice, exposure, who)
468     ZNotice_t *notice;
469     Exposure_type exposure;
470     struct sockaddr_in *who;
471 {
472     Location *loc, *oldlocs, newloc;
473     int i;
474
475     loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
476     if (loc) {
477         /* Update the time, tty, and exposure on the existing location. */
478         loc->exposure = exposure;
479         if (ulogin_parse(notice, &newloc) == 0) {
480             free_string(loc->tty);
481             loc->tty = dup_string(newloc.tty);
482             free(loc->time);
483             loc->time = strsave(newloc.time);
484             free_loc(&newloc);
485         }
486         return 0;
487     }
488
489     oldlocs = locations;
490
491     locations = (Location *) malloc((num_locs + 1) * sizeof(Location));
492     if (!locations) {
493         syslog(LOG_ERR, "zloc mem alloc");
494         locations = oldlocs;
495         return 1;
496     }
497
498     if (num_locs == 0) {        /* first one */
499         if (ulogin_setup(notice, locations, exposure, who)) {
500             free(locations);
501             locations = NULL;
502             return 1;
503         }
504         num_locs = 1;
505         goto dprnt;
506     }
507
508     /* not the first one, insert him */
509
510     if (ulogin_setup(notice, &newloc, exposure, who)) {
511         free(locations);
512         locations = oldlocs;
513         return 1;
514     }
515     num_locs++;
516
517     /* copy old locs */
518     i = 0;
519     while ((i < num_locs-1) &&
520            (comp_string(oldlocs[i].user,newloc.user) < 0)) {
521         locations[i] = oldlocs[i];
522         i++;
523     }
524
525     /* add him in here */
526     locations[i++] = newloc;
527
528     /* copy the rest */
529     while (i < num_locs) {
530         locations[i] = oldlocs[i - 1];
531         i++;
532     }
533     if (oldlocs)
534         free(oldlocs);
535
536   dprnt:
537     return 0;
538 }
539
540 /*
541  * Set up the location locs with the information in the notice.
542  */ 
543
544 static int
545 ulogin_setup(notice, locs, exposure, who)
546     ZNotice_t *notice;
547     Location *locs;
548     Exposure_type exposure;
549     struct sockaddr_in *who;
550 {
551     if (ulogin_parse(notice, locs))
552         return 1;
553
554     locs->exposure = exposure;
555     locs->addr.sin_family = AF_INET;
556     locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
557     locs->addr.sin_port = notice->z_port;
558     return(0);
559 }
560
561 /*
562  * Parse the location information in the notice, and fill it into *locs
563  */
564
565 static int
566 ulogin_parse(notice, locs)
567     ZNotice_t *notice;
568     Location *locs;
569 {
570     char *cp, *base;
571     int nulls = 0;
572
573     if (!notice->z_message_len) {
574         syslog(LOG_ERR, "short ulogin");
575         return 1;
576     }
577
578     base = notice->z_message;
579     for (cp = base; cp < base + notice->z_message_len; cp++) {
580         if (!*cp)
581             nulls++;
582     }
583     if (nulls < 3) {
584         syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
585                notice->z_sender, nulls);
586         return 1;
587     }
588
589     locs->user = make_string(notice->z_class_inst,0);
590
591     cp = base;
592     locs->machine = make_string(cp,0);
593
594     cp += (strlen(cp) + 1);
595     locs->time = strsave(cp);
596
597     /* This field might not be null-terminated */
598     cp += (strlen(cp) + 1);
599     locs->tty = make_string(cp, 0);
600
601     return 0;
602 }       
603
604
605 static Location *
606 ulogin_find(user, host, port)
607     char *user;
608     struct in_addr *host;
609     unsigned int port;
610 {
611     Location *loc;
612     String *str;
613
614     /* Find the first location for this user. */
615     loc = ulogin_find_user(user);
616     if (!loc)
617         return NULL;
618
619     /* Look for a location which matches the host and port. */
620     str = make_string(user, 0);
621     while (loc < locations + num_locs && loc->user == str) {
622         if (loc->addr.sin_addr.s_addr == host->s_addr
623             && loc->addr.sin_port == port) {
624             free_string(str);
625             return loc;
626         }
627         loc++;
628     }
629
630     free_string(str);
631     return NULL;
632 }
633
634 /*
635  * Return a pointer to the first instance of this user@realm in the
636  * table.
637  */
638
639 static Location *
640 ulogin_find_user(user)
641     char *user;
642 {
643     int i, rlo, rhi;
644     int compar;
645     String *str;
646
647     if (!locations)
648         return(NULL);
649
650     str = make_string(user, 0);
651
652     /* i is the current midpoint location, rlo is the lowest we will
653      * still check, and rhi is the highest we will still check. */
654
655     i = num_locs / 2;
656     rlo = 0;
657     rhi = num_locs - 1;
658
659     while ((compar = comp_string(locations[i].user, str)) != 0) {
660         if (compar < 0)
661             rlo = i + 1;
662         else
663             rhi = i - 1;
664         if (rhi - rlo < 0) {
665             free_string(str);
666             return NULL;
667         }
668         i = (rhi + rlo) / 2;
669     }
670
671     /* Back up to the first location for this user. */
672     while (i > 0 && locations[i - 1].user == str)
673         i--;
674     free_string(str);
675     return &locations[i];
676 }
677
678 static int
679 ul_equiv(l1, l2)
680     Location *l1, *l2;
681 {
682     if (l1->machine != l2->machine)
683         return 0;
684     if (l1->tty != l2->tty)
685         return 0;
686     return 1;
687 }
688
689 /*
690  * remove the user specified in notice from the internal table
691  */
692
693 static Exposure_type
694 ulogin_remove_user(notice, who, err_return)
695     ZNotice_t *notice;
696     struct sockaddr_in *who;
697     int *err_return;
698 {
699     Location *new_locs, *loc;
700     int i = 0;
701     Exposure_type quiet;
702
703     *err_return = 0;
704     loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
705     if (!loc) {
706         *err_return = NOLOC;
707         return NONE;
708     }
709
710     quiet = loc->exposure;
711
712     if (--num_locs == 0) {      /* last one */
713         free_loc(locations);
714         free(locations);
715         locations = NULL;
716         return quiet;
717     }
718
719     new_locs = (Location *) malloc(num_locs * sizeof(Location));
720     if (!new_locs) {
721         syslog(LOG_CRIT, "ul_rem alloc");
722         abort();
723     }
724
725     /* copy old entries */
726     while (i < num_locs && &locations[i] < loc) {
727         new_locs[i] = locations[i];
728         i++;
729     }
730
731     /* free up this one */
732     free_loc(&locations[i]);
733     i++;                        /* skip over this one */
734
735     /* copy the rest */
736     while (i <= num_locs) {
737         new_locs[i - 1] = locations[i];
738         i++;
739     }
740
741     free(locations);
742
743     locations = new_locs;
744
745     /* all done */
746     return quiet;
747 }
748
749 /*
750  * remove all locs of the user specified in notice from the internal table
751  */
752
753 static void
754 ulogin_flush_user(notice)
755     ZNotice_t *notice;
756 {
757     Location *loc, *loc2;
758     int i, j, num_match, num_left;
759
760     i = num_match = num_left = 0;
761
762     if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
763         return;
764
765     /* compute # locations left in the list, after loc2 (inclusive) */
766     num_left = num_locs - (loc2 - locations);
767
768     while (num_left &&
769            !strcasecmp(loc2[num_match].user->string,
770                        notice->z_class_inst)) {
771         /* as long as we keep matching, march up the list */
772         num_match++;
773         num_left--;
774     }
775     if (num_locs == num_match) { /* no other locations left */
776         for (j = 0; j < num_match; j++)
777             free_loc(&locations[j]); /* free storage */
778         free (locations);
779         locations = NULL;
780         num_locs = 0;
781         return;
782     }
783
784     loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
785     if (!loc) {
786         syslog(LOG_CRIT, "ul_rem alloc");
787         abort();
788     }
789
790     /* copy old entries */
791     while (i < num_locs && &locations[i] < loc2) {
792         loc[i] = locations[i];
793         i++;
794     }
795         
796     for(j = 0; j < num_match; j++) {
797         free_loc(&locations[i]);
798         i++;
799     }
800
801     /* copy the rest */
802     while (i < num_locs) {
803         loc[i - num_match] = locations[i];
804         i++;
805     }
806
807     free(locations);
808
809     locations = loc;
810     num_locs -= num_match;
811
812 #ifdef DEBUG
813     if (zdebug) {
814         int i;
815
816         for (i = 0; i < num_locs; i++) {
817             syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
818                    (int) locations[i].exposure);
819         }
820     }
821 #endif
822 }
823
824
825 static void
826 ulogin_locate(notice, who, auth)
827     ZNotice_t *notice;
828     struct sockaddr_in *who;
829     int auth;
830 {
831     char **answer;
832     int found;
833     Code_t retval;
834     struct sockaddr_in send_to_who;
835
836     answer = ulogin_marshal_locs(notice, &found, auth);
837
838     send_to_who = *who;
839     send_to_who.sin_port = notice->z_port;
840
841     retval = ZSetDestAddr(&send_to_who);
842     if (retval != ZERR_NONE) {
843         syslog(LOG_WARNING, "ulogin_locate set addr: %s",
844                error_message(retval));
845         if (answer)
846             free(answer);
847         return;
848     }
849
850     notice->z_kind = ACKED;
851
852     /* use xmit_frag() to send each piece of the notice */
853
854     retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
855     if (retval != ZERR_NONE)
856         syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
857     if (answer)
858         free(answer);
859 }
860
861 /*
862  * Locate the user and collect the locations into an array.  Return the # of
863  * locations in *found.
864  */
865
866 static char **
867 ulogin_marshal_locs(notice, found, auth)
868     ZNotice_t *notice;
869     int *found;
870     int auth;
871 {
872     Location **matches = (Location **) 0;
873     Location *loc;
874     char **answer;
875     int i = 0;
876     String *inst;
877     int local = (auth && realm_sender_in_realm(ZGetRealm(), notice->z_sender));
878
879     *found = 0;                 /* # of matches */
880
881     loc = ulogin_find_user(notice->z_class_inst);
882     if (!loc)
883         return(NULL);
884
885     i = loc - locations;
886
887     inst = make_string(notice->z_class_inst,0);
888     while (i < num_locs && (inst == locations[i].user)) {
889         /* these locations match */
890         switch (locations[i].exposure) {
891           case OPSTAFF_VIS:
892             i++;
893             continue;
894           case REALM_VIS:
895           case REALM_ANN:
896             if (!local) {
897                 i++;
898                 continue;
899             }
900           case NET_VIS:
901           case NET_ANN:
902           default:
903             break;
904         }
905         if (!*found) {
906             matches = (Location **) malloc(sizeof(Location *));
907             if (!matches) {
908                 syslog(LOG_ERR, "ulog_loc: no mem");
909                 break;  /* from the while */
910             }
911             matches[0] = &locations[i];
912             (*found)++;
913         } else {
914             matches = (Location **) realloc(matches,
915                                             ++(*found) * sizeof(Location *));
916             if (!matches) {
917                 syslog(LOG_ERR, "ulog_loc: realloc no mem");
918                 *found = 0;
919                 break;  /* from the while */
920             }
921             matches[*found - 1] = &locations[i];
922         }
923         i++;
924     }
925     free_string(inst);
926
927     /* OK, now we have a list of user@host's to return to the client
928        in matches */
929         
930         
931 #ifdef DEBUG
932     if (zdebug) {
933         for (i = 0; i < *found ; i++)
934             zdbug((LOG_DEBUG,"found %s",
935                    matches[i]->user->string));
936     }
937 #endif
938         
939     /* coalesce the location information into a list of char *'s */
940     answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
941     if (!answer) {
942         syslog(LOG_ERR, "zloc no mem(answer)");
943         *found = 0;
944     } else
945         for (i = 0; i < *found ; i++) {
946             answer[i * NUM_FIELDS] = matches[i]->machine->string;
947             answer[i * NUM_FIELDS + 1] = matches[i]->time;
948             answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
949         }
950         
951     if (matches)
952         free(matches);
953     return answer;
954 }
955
956 void
957 uloc_dump_locs(fp)
958     FILE *fp;
959 {
960     int i;
961
962     for (i = 0; i < num_locs; i++) {
963         fputs("'", fp);
964         dump_quote(locations[i].user->string, fp);
965         fputs("' '", fp);
966         dump_quote(locations[i].machine->string, fp);
967         fputs("' '", fp);
968         dump_quote(locations[i].time, fp);
969         fputs("' '", fp);
970         dump_quote(locations[i].tty->string, fp);
971         fputs("' ", fp);
972         switch (locations[i].exposure) {
973           case OPSTAFF_VIS:
974             fputs("OPSTAFF", fp);
975             break;
976           case REALM_VIS:
977             fputs("RLM_VIS", fp);
978             break;
979           case REALM_ANN:
980             fputs("RLM_ANN", fp);
981             break;
982           case NET_VIS:
983             fputs("NET_VIS", fp);
984             break;
985           case NET_ANN:
986             fputs("NET_ANN", fp);
987             break;
988           default:
989             fprintf(fp, "? %d ?", locations[i].exposure);
990             break;
991         }
992         fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
993                 ntohs(locations[i].addr.sin_port));
994     }
995 }
996
997 static void
998 free_loc(loc)
999     Location *loc;
1000 {
1001     free_string(loc->user);
1002     free_string(loc->machine);
1003     free_string(loc->tty);
1004     free(loc->time);
1005     return;
1006 }
1007
1008 static void
1009 ulogin_locate_forward(notice, who, realm)
1010     ZNotice_t *notice;
1011     struct sockaddr_in *who;
1012     ZRealm *realm;
1013 {
1014     ZNotice_t lnotice;
1015
1016     lnotice = *notice;
1017     lnotice.z_opcode = REALM_REQ_LOCATE;
1018   
1019     realm_handoff(&lnotice, 1, who, realm, 0);
1020 }
1021
1022 void
1023 ulogin_realm_locate(notice, who, realm)
1024     ZNotice_t *notice;
1025     struct sockaddr_in *who;
1026     ZRealm *realm;
1027 {
1028   char **answer;
1029   int found;
1030   Code_t retval;
1031   ZNotice_t lnotice;
1032   char *pack;
1033   int packlen;
1034   
1035 #ifdef DEBUG
1036   if (zdebug)
1037     zdbug((LOG_DEBUG, "ulogin_realm_locate"));
1038 #endif
1039   
1040   answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
1041   
1042   lnotice = *notice;
1043   lnotice.z_opcode = REALM_ANS_LOCATE;
1044   
1045   if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
1046     syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
1047            error_message(retval));
1048     
1049     if (answer)
1050       free(answer);
1051     return;
1052   }
1053   if (answer)
1054     free(answer);
1055   
1056   if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
1057     syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1058            error_message(retval));
1059     free(pack);
1060     return;
1061   }
1062   
1063   realm_handoff(&lnotice, 1, who, realm, 0);
1064   free(pack);
1065   
1066   return;
1067 }
1068
1069 void
1070 ulogin_relay_locate(notice, who)
1071     ZNotice_t *notice;
1072     struct sockaddr_in *who;
1073 {
1074   ZNotice_t lnotice;
1075   Code_t retval;
1076   struct sockaddr_in newwho;
1077   char *pack;
1078   int packlen;
1079   
1080   newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
1081   newwho.sin_port = notice->z_port;
1082   newwho.sin_family = AF_INET;
1083   
1084   if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
1085     syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
1086            error_message(retval));
1087     return;
1088   }
1089   
1090   lnotice = *notice;
1091   lnotice.z_opcode = LOCATE_LOCATE;
1092   lnotice.z_kind = ACKED;
1093   
1094   if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
1095     syslog(LOG_WARNING, "ulog_relay_loc format: %s",
1096            error_message(retval));
1097     return;
1098   }
1099   
1100   if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
1101     syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
1102            error_message(retval));
1103   }
1104   free(pack);
1105 }
1106