]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/dispatch.c
a4ab079d08fbc59c203d4a5c756c0fd37b7dae32
[1ts-debian.git] / zephyr / server / dispatch.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for dispatching a notice.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Id: dispatch.c,v 1.60 2000/02/11 20:59:36 ghudson Exp $
7  *
8  *      Copyright (c) 1987, 1991 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_dispatch_c[] =
20 "$Id: dispatch.c,v 1.60 2000/02/11 20:59:36 ghudson Exp $";
21 #endif
22 #endif
23
24 #define NACKTAB_HASHSIZE                1023
25 #define NACKTAB_HASHVAL(sockaddr, uid)  (((sockaddr).sin_addr.s_addr ^ \
26                                           (sockaddr).sin_port ^ \
27                                           (uid).zuid_addr.s_addr ^ \
28                                           (uid).tv.tv_sec ^ \
29                                           (uid).tv.tv_usec) % NACKTAB_HASHSIZE)
30 #define HOSTS_SIZE_INIT                 256
31
32 #ifdef DEBUG
33 ZCONST char *ZNoticeKinds[9] = {"UNSAFE", "UNACKED", "ACKED", "HMACK",
34                                     "HMCTL", "SERVACK", "SERVNAK", "CLIENTACK",
35                                     "STAT"};
36 #endif
37 /*
38  *
39  * External Routines:
40  *
41  * void dispatch(notice, auth, who)
42  *      ZNotice_t *notice;
43  *      int auth;
44  *      struct sockaddr_in *who;
45  *
46  * void clt_ack(notice, who, sent)
47  *      ZNotice_t *notice;
48  *      struct sockaddr_in *who;
49  *      Sent_type sent;
50  *
51  * void nack_release(client)
52  *      Client *client;
53  *
54  * void sendit(notice, auth, who)
55  *      ZNotice_t *notice;
56  *      int auth;
57  *      struct sockaddr_in *who;
58  *
59  * void xmit(notice, dest, auth, client)
60  *      ZNotice_t *notice;
61  *      struct sockaddr_in *dest;
62  *      int auth;
63  *      Client *client;
64  */
65
66
67 String *class_control, *class_admin, *class_hm, *class_ulogin, *class_ulocate;
68
69 int rexmit_times[] = REXMIT_TIMES;
70
71 static void nack_cancel __P((ZNotice_t *, struct sockaddr_in *));
72 static void dispatch __P((ZNotice_t *, int, struct sockaddr_in *, int));
73 static int send_to_dest __P((ZNotice_t *, int, Destination *dest, int, int));
74 static void hostm_deathgram __P((struct sockaddr_in *, Server *));
75 static char *hm_recipient __P((void));
76
77 Statistic realm_notices = {0, "inter-realm notices"};
78 Statistic interserver_notices = {0, "inter-server notices"};
79 Statistic hm_packets = {0, "hostmanager packets"};
80 Statistic control_notices = {0, "client control notices"};
81 Statistic message_notices = {0, "message notices"};
82 Statistic login_notices = {0, "login notices"};
83 Statistic i_s_ctls = {0, "inter-server control notices"};
84 Statistic i_s_logins = {0, "inter-server login notices"};
85 Statistic i_s_admins = {0, "inter-server admin notices"};
86 Statistic i_s_locates = {0, "inter-server locate notices"};
87 Statistic locate_notices = {0, "locate notices"};
88 Statistic admin_notices = {0, "admin notices"};
89
90 static Unacked *nacktab[NACKTAB_HASHSIZE];
91 static struct in_addr *hosts;
92 static int hosts_size = 0, num_hosts = 0;
93
94 static void
95 dump_stats (arg)
96     void *arg;
97 {
98     syslog(LOG_INFO, "stats: %s: %d", hm_packets.str, hm_packets.val);
99     syslog(LOG_INFO, "stats: %s: %d", control_notices.str,
100            control_notices.val);
101     syslog(LOG_INFO, "stats: %s: %d", message_notices.str,
102            message_notices.val);
103     syslog(LOG_INFO, "stats: %s: %d", login_notices.str, login_notices.val);
104     syslog(LOG_INFO, "stats: %s: %d", locate_notices.str, locate_notices.val);
105     syslog(LOG_INFO, "stats: %s: %d", admin_notices.str, admin_notices.val);
106     syslog(LOG_INFO, "stats: %s: %d", realm_notices.str, realm_notices.val);
107     syslog(LOG_INFO, "stats: %s: %d", interserver_notices.str,
108            interserver_notices.val);
109     syslog(LOG_INFO, "stats: %s: %d", i_s_ctls.str, i_s_ctls.val);
110     syslog(LOG_INFO, "stats: %s: %d", i_s_logins.str, i_s_logins.val);
111     syslog(LOG_INFO, "stats: %s: %d", i_s_admins.str, i_s_admins.val);
112     syslog(LOG_INFO, "stats: %s: %d", i_s_locates.str, i_s_locates.val);
113
114     /* log stuff once an hour */
115     timer_set_rel ((long) 6*60*60, dump_stats, arg);
116 }
117
118 /*
119  * Handle an input packet.
120  * Warning: this function may be called from within a brain dump.
121  */
122
123 void
124 handle_packet()
125 {
126     Code_t status;
127     ZPacket_t input_packet;     /* from the network */
128     ZNotice_t new_notice;       /* parsed from input_packet */
129     int input_len;              /* len of packet */
130     struct sockaddr_in input_sin; /* Zconstructed for authent */
131     struct sockaddr_in whoisit; /* for holding peer's address */
132     int authentic;              /* authentic flag */
133     Pending *pending;           /* pending packet */
134     int from_server;            /* packet is from another server */
135     Realm *realm;               /* foreign realm ptr */
136 #ifdef DEBUG
137     static int first_time = 1;
138 #endif
139
140 #ifdef DEBUG
141     /* Dump statistics five minutes after startup */
142     if (first_time) {
143         first_time = 0;
144         timer_set_rel(5*60, dump_stats, NULL);
145     }
146 #endif
147     /* handle traffic */
148
149     if (otherservers[me_server_idx].queue) {
150         /* something here for me; take care of it */
151 #if 1
152         zdbug((LOG_DEBUG, "internal queue process"));
153 #endif
154
155         pending = server_dequeue(me_server);
156
157         status = ZParseNotice(pending->packet, pending->len, &new_notice);
158         if (status != ZERR_NONE) {
159             syslog(LOG_ERR, "bad notice parse (%s): %s",
160                    inet_ntoa(pending->who.sin_addr), error_message(status));
161         } else {
162             dispatch(&new_notice, pending->auth, &pending->who, 1);
163         }
164         server_pending_free(pending);
165         return;
166     }
167
168     /* 
169      * nothing in internal queue, go to the external library
170      * queue/socket
171      */
172     status = ZReceivePacket(input_packet, &input_len, &whoisit);
173     if (status != ZERR_NONE) {
174         syslog(LOG_ERR, "bad packet receive: %s from %s",
175                error_message(status), inet_ntoa(whoisit.sin_addr));
176         return;
177     }
178     npackets++;
179     status = ZParseNotice(input_packet, input_len, &new_notice);
180     if (status != ZERR_NONE) {
181         syslog(LOG_ERR, "bad notice parse (%s): %s",
182                inet_ntoa(whoisit.sin_addr), error_message(status));
183         return;
184     }
185     if (server_which_server(&whoisit)) {
186         /* we need to parse twice--once to get
187            the source addr, second to check
188            authentication */
189         memset(&input_sin, 0, sizeof(input_sin));
190         input_sin.sin_addr.s_addr = new_notice.z_sender_addr.s_addr;
191         input_sin.sin_port = new_notice.z_port;
192         input_sin.sin_family = AF_INET;
193         realm = realm_which_realm(&input_sin);
194         if (realm) {
195             authentic = ZCheckRealmAuthentication(&new_notice, &input_sin,
196                                                   realm->name);
197         } else {
198             authentic = ZCheckAuthentication(&new_notice, &input_sin);
199         }
200         from_server = 1;
201     } else {
202         from_server = 0;
203         realm = realm_which_realm(&whoisit);
204         if (realm) {
205             authentic = ZCheckRealmAuthentication(&new_notice, &whoisit,
206                                                   realm->name);
207         } else {
208             authentic = ZCheckAuthentication(&new_notice, &whoisit);
209         }
210     }
211
212     if (whoisit.sin_port != hm_port && whoisit.sin_port != hm_srv_port &&
213         strcasecmp(new_notice.z_class, ZEPHYR_ADMIN_CLASS) != 0 &&
214         whoisit.sin_port != srv_addr.sin_port &&
215         new_notice.z_kind != CLIENTACK) {
216         syslog(LOG_ERR, "bad port %s/%d", inet_ntoa(whoisit.sin_addr),
217                ntohs(whoisit.sin_port));
218         return;
219     }
220
221     message_notices.val++;
222     dispatch(&new_notice, authentic, &whoisit, from_server);
223     return;
224 }
225 /*
226  * Dispatch a notice.
227  */
228
229 static void
230 dispatch(notice, auth, who, from_server)
231     ZNotice_t *notice;
232     int auth;
233     struct sockaddr_in *who;
234     int from_server;
235 {
236     Code_t status;
237     String *notice_class;
238     struct sockaddr_in who2;
239     int authflag;
240     Realm *realm;
241     char *cp;
242 #ifdef DEBUG
243     char dbg_buf[BUFSIZ];
244 #endif
245
246     authflag = (auth == ZAUTH_YES);
247
248     if ((int) notice->z_kind < (int) UNSAFE ||
249         (int) notice->z_kind > (int) CLIENTACK) {
250         syslog(LOG_NOTICE, "bad notice kind 0x%x from %s", notice->z_kind,
251                inet_ntoa(who->sin_addr));
252         return;
253     }
254 #if 0
255     if (zdebug) {
256         sprintf(dbg_buf,
257                 "disp:%s '%s' '%s' '%s' notice to '%s' from '%s' %s/%d/%d",
258                 ZNoticeKinds[(int) notice->z_kind], notice->z_class,
259                 notice->z_class_inst, notice->z_opcode, notice->z_recipient,
260                 notice->z_sender, inet_ntoa(who->sin_addr),
261                 ntohs(who->sin_port), ntohs(notice->z_port));
262         syslog(LOG_DEBUG, "%s", dbg_buf);
263     }
264 #endif
265
266     if (notice->z_kind == CLIENTACK) {
267         nack_cancel(notice, who);
268         return;
269     }
270
271     who2 = *who;
272 #if 0
273     if (0 && from_server) {
274         /* incorporate server_dispatch here */
275     }
276 #endif
277     notice_class = make_string(notice->z_class,1);
278
279     if (from_server) {
280         interserver_notices.val++;
281         status = server_dispatch(notice, authflag, who);
282     } else if (class_is_hm(notice_class)) {
283         hm_packets.val++;
284         status = hostm_dispatch(notice, authflag, who, me_server);
285     } else if (realm_which_realm(who) && !(class_is_admin(notice_class))) {
286         realm_notices.val++;
287         status = realm_dispatch(notice, authflag, who, me_server);
288     } else if (class_is_control(notice_class)) {
289         control_notices.val++;
290         status = control_dispatch(notice, authflag, who, me_server);
291     } else if (class_is_ulogin(notice_class)) {
292         login_notices.val++;
293         status = ulogin_dispatch(notice, authflag, who, me_server);
294     } else if (class_is_ulocate(notice_class)) {
295         locate_notices.val++;
296         status = ulocate_dispatch(notice, authflag, who, me_server);
297     } else if (class_is_admin(notice_class)) {
298         admin_notices.val++;
299         status = server_adispatch(notice, authflag, who, me_server);
300     } else {
301         if (!bound_for_local_realm(notice)) {
302             cp = strchr(notice->z_recipient, '@');
303             if (!cp ||
304                 !(realm = realm_get_realm_by_name(realm_expand_realm(cp + 1))))
305                 sendit(notice, authflag, who, 0);
306             else
307                 realm_handoff(notice, authflag, who, realm, 1);
308         } else {
309             if (notice->z_recipient[0] == '@')
310                 notice->z_recipient = "";
311             sendit(notice, authflag, who, 1);
312         }
313         free_string(notice_class);
314         return;
315     }
316
317     if (status == ZSRV_REQUEUE)
318         server_self_queue(notice, authflag, who);
319     free_string(notice_class);
320 }
321
322 /*
323  * Send a notice off to those clients who have subscribed to it.
324  */
325
326 void
327 sendit(notice, auth, who, external)
328     ZNotice_t *notice;
329     int auth;
330     struct sockaddr_in *who;
331     int external;
332 {
333     static int send_counter = 0;
334     char recipbuf[ANAME_SZ + INST_SZ + REALM_SZ + 3], *recipp;
335     int any = 0;
336     Acl *acl;
337     Destination dest;
338     String *class;
339
340     class = make_string(notice->z_class, 1);
341     acl = class_get_acl(class);
342     if (acl != NULL) {
343         /* if controlled and not auth, fail */
344         if (!auth) {
345             syslog(LOG_WARNING, "sendit unauthentic %s from %s",
346                    notice->z_class, notice->z_sender);
347             clt_ack(notice, who, AUTH_FAILED);
348             free_string(class);
349             return;
350         }
351         /* if not auth to transmit, fail */
352         if (!access_check(notice->z_sender, acl, TRANSMIT)) {
353             syslog(LOG_WARNING, "sendit unauthorized %s from %s",
354                    notice->z_class, notice->z_sender);
355             clt_ack(notice, who, AUTH_FAILED);
356             free_string(class);
357             return;
358         }
359         /* sender != inst and not auth to send to others --> fail */
360         if (strcmp(notice->z_sender, notice->z_class_inst) != 0 &&
361             !access_check(notice->z_sender, acl, INSTUID)) {
362             syslog(LOG_WARNING, "sendit unauth uid %s %s.%s", notice->z_sender,
363                    notice->z_class, notice->z_class_inst);
364             clt_ack(notice, who, AUTH_FAILED);
365             free_string(class);
366             return;
367         }
368     }
369     if (!realm_which_realm(who)) {
370         if (memcmp(&notice->z_sender_addr.s_addr, &who->sin_addr.s_addr,
371                    sizeof(notice->z_sender_addr.s_addr))) {
372             /* someone is playing games... */
373             /* inet_ntoa returns pointer to static area */
374             /* max size is 255.255.255.255 */
375             char buffer[16];
376             strcpy(buffer, inet_ntoa(who->sin_addr));
377             if (!auth) {
378                 syslog(LOG_WARNING,
379                        "sendit unauthentic fake packet: claimed %s, real %s",
380                        inet_ntoa(notice->z_sender_addr), buffer);
381                 clt_ack(notice, who, AUTH_FAILED);
382                 free_string(class);
383                 return;
384             }
385             if (ntohl(notice->z_sender_addr.s_addr) != 0) {
386                 syslog(LOG_WARNING,
387                        "sendit invalid address: claimed %s, real %s",
388                        inet_ntoa(notice->z_sender_addr), buffer);
389                 clt_ack(notice, who, AUTH_FAILED);
390                 free_string(class);
391                 return;
392             }
393             syslog(LOG_WARNING, "sendit addr mismatch: claimed %s, real %s",
394                    inet_ntoa(notice->z_sender_addr), buffer);
395         }
396     }
397
398     /* Increment the send counter, used to prevent duplicate sends to
399      * clients.  On the off-chance that we wrap around to 0, skip over
400      * it to prevent missing clients which have never had a packet
401      * sent to them. */
402     send_counter++;
403     if (send_counter == 0)
404         send_counter = 1;
405
406     /* Send to clients subscribed to the triplet itself. */
407     dest.classname = class;
408     dest.inst = make_string(notice->z_class_inst, 1);
409     if (bound_for_local_realm(notice) && *notice->z_recipient == '@') {
410         dest.recip = make_string("", 0);
411     } else {
412         strncpy(recipbuf, notice->z_recipient, sizeof(recipbuf));
413         recipp = strrchr(recipbuf, '@');
414         if (recipp)
415             sprintf(recipp + 1, "%s", realm_expand_realm(recipp + 1));
416         dest.recip = make_string(recipbuf, 0);
417     }
418     if (send_to_dest(notice, auth, &dest, send_counter, external))
419         any = 1;
420
421     /* Send to clients subscribed to the triplet with the instance
422      * substituted with the wildcard instance. */
423     free_string(dest.inst);
424     dest.inst = wildcard_instance;
425     if (send_to_dest(notice, auth, &dest, send_counter, external))
426         any = 1;
427
428     free_string(class);
429     free_string(dest.recip);
430     if (any)
431         ack(notice, who);
432     else
433         nack(notice, who);
434 }
435
436 /*
437  * Send to each client in the list.  Avoid duplicates by setting
438  * last_send on each client to send_counter, a nonce which is updated
439  * by sendit() above.
440  */
441
442 static int
443 send_to_dest(notice, auth, dest, send_counter, external)
444     ZNotice_t *notice;
445     int auth;
446     Destination *dest;
447     int send_counter;
448     int external;
449 {
450     Client **clientp;
451     int any = 0;
452
453     clientp = triplet_lookup(dest);
454     if (!clientp)
455         return 0;
456
457     for (; *clientp; clientp++) {
458         if ((*clientp)->last_send == send_counter)
459             continue;
460         (*clientp)->last_send = send_counter;
461         if ((*clientp)->realm && external)
462             realm_handoff(notice, auth, &clientp[0]->addr, clientp[0]->realm,
463                           1);
464         else
465             xmit(notice, &((*clientp)->addr), auth, *clientp);
466         any = 1;
467     }
468
469     return any;
470 }
471
472 /*
473  * Release anything destined for the client in the not-yet-acked table.
474  */
475
476 void
477 nack_release(client)
478     Client *client;
479 {
480     int i;
481     Unacked *nacked, *next;
482
483     for (i = 0; i < NACKTAB_HASHSIZE; i++) {
484         for (nacked = nacktab[i]; nacked; nacked = next) {
485             next = nacked->next;
486             if (nacked->client == client) {
487                 timer_reset(nacked->timer);
488                 LIST_DELETE(nacked);
489                 free(nacked->packet);
490                 free(nacked);
491             }
492         }
493     }
494 }
495
496 /*
497  * Send one packet of a fragmented message to a client.  After transmitting,
498  * put it onto the not ack'ed list.
499  */
500
501 /* the arguments must be the same as the arguments to Z_XmitFragment */
502 /*ARGSUSED*/
503 Code_t
504 xmit_frag(notice, buf, len, waitforack)
505     ZNotice_t *notice;
506     char *buf;
507     int len;
508     int waitforack;
509 {
510     struct sockaddr_in sin;
511     char *savebuf;
512     Unacked *nacked;
513     Code_t retval;
514     int hashval, sendfail = 0;
515
516     retval = ZSendPacket(buf, len, 0);
517     if (retval != ZERR_NONE) {
518         syslog(LOG_WARNING, "xmit_frag send: %s", error_message(retval));
519         if (retval != EAGAIN && retval != ENOBUFS)
520             return retval;
521         sendfail = 1;
522     }
523
524     /* now we've sent it, mark it as not ack'ed */
525     nacked = (Unacked *) malloc(sizeof(Unacked));
526     if (!nacked) {
527         /* no space: just punt */
528         syslog(LOG_WARNING, "xmit_frag nack malloc");
529         return ENOMEM;
530     }
531
532     savebuf = (char *) malloc(len);
533     if (!savebuf) {
534         /* no space: just punt */
535         syslog(LOG_WARNING, "xmit_frag pack malloc");
536         free(nacked);
537         return ENOMEM;
538     }
539
540     memcpy(savebuf, buf, len);
541
542     sin = ZGetDestAddr();
543     nacked->client = NULL;
544     nacked->rexmits = (sendfail) ? -1 : 0;
545     nacked->packet = savebuf;
546     nacked->dest.addr = sin;
547     nacked->packsz = len;
548     nacked->uid = notice->z_uid;
549     nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
550     LIST_INSERT(&nacktab[NACKTAB_HASHVAL(sin, nacked->uid)], nacked);
551     return(ZERR_NONE);
552 }
553
554 /*
555  * Send the notice to the client.  After transmitting, put it onto the
556  * not ack'ed list.
557  */
558
559 void
560 xmit(notice, dest, auth, client)
561     ZNotice_t *notice;
562     struct sockaddr_in *dest;
563     int auth;
564     Client *client;
565 {
566     char *noticepack;
567     Unacked *nacked;
568     int packlen, sendfail = 0;
569     Code_t retval;
570
571 #if 0
572     zdbug((LOG_DEBUG,"xmit"));
573 #endif
574
575     noticepack = (char *) malloc(sizeof(ZPacket_t));
576     if (!noticepack) {
577         syslog(LOG_ERR, "xmit malloc");
578         return;                 /* DON'T put on nack list */
579     }
580         
581     packlen = sizeof(ZPacket_t);
582
583     if (auth && client) {       /*
584                                    we are distributing authentic and
585                                    we have a pointer to auth info
586                                    */
587 #ifdef HAVE_KRB4
588         retval = ZFormatAuthenticNotice(notice, noticepack, packlen, &packlen,
589                                         client->session_key);
590         if (retval != ZERR_NONE) {
591             syslog(LOG_ERR, "xmit auth format: %s", error_message(retval));
592             free(noticepack);
593             return;
594         }
595 #else /* !HAVE_KRB4 */
596         notice->z_auth = 1;
597         retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
598         if (retval != ZERR_NONE) {
599             syslog(LOG_ERR, "xmit auth/raw format: %s", error_message(retval));
600             free(noticepack);
601             return;
602         }
603 #endif /* HAVE_KRB4 */
604     } else {
605         notice->z_auth = 0;
606         notice->z_authent_len = 0;
607         notice->z_ascii_authent = (char *)"";
608         retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
609         if (retval != ZERR_NONE) {
610             syslog(LOG_ERR, "xmit format: %s", error_message(retval));
611             free(noticepack);
612             return;                     /* DON'T put on nack list */
613         }
614     }
615 #if 0
616     zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(dest->sin_addr),
617            ntohs(dest->sin_port)));
618 #endif
619     retval = ZSetDestAddr(dest);
620     if (retval != ZERR_NONE) {
621         syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
622         free(noticepack);
623         return;
624     }
625     retval = ZSendPacket(noticepack, packlen, 0);
626     if (retval != ZERR_NONE) {
627         syslog(LOG_WARNING, "xmit xmit: (%s/%d) %s", inet_ntoa(dest->sin_addr),
628                ntohs(dest->sin_port), error_message(retval));
629         if (retval != EAGAIN && retval != ENOBUFS) {
630             free(noticepack);
631             return;
632         }
633         sendfail = 1;
634     }
635
636     /* now we've sent it, mark it as not ack'ed */
637
638     nacked = (Unacked *) malloc(sizeof(Unacked));
639     if (!nacked) {
640         /* no space: just punt */
641         syslog(LOG_WARNING, "xmit nack malloc");
642         free(noticepack);
643         return;
644     }
645
646     nacked->client = client;
647     nacked->rexmits = (sendfail) ? -1 : 0;
648     nacked->packet = noticepack;
649     nacked->dest.addr = *dest;
650     nacked->packsz = packlen;
651     nacked->uid = notice->z_uid;
652     nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
653     LIST_INSERT(&nacktab[NACKTAB_HASHVAL(*dest, nacked->uid)], nacked);
654 }
655
656 /*
657  * Retransmit the packet specified.  If we have timed out or retransmitted
658  * too many times, punt the packet and initiate the host recovery algorithm
659  * Else, increment the count and re-send the notice packet.
660  */
661
662 void
663 rexmit(arg)
664     void *arg;
665 {
666     Unacked *nacked = (Unacked *) arg;
667     int retval;
668
669 #if 1
670     syslog(LOG_DEBUG, "rexmit %s/%d #%d time %d",
671            inet_ntoa(nacked->dest.addr.sin_addr),
672            ntohs(nacked->dest.addr.sin_port), nacked->rexmits + 1, NOW);
673 #endif
674
675     nacked->rexmits++;
676     if (rexmit_times[nacked->rexmits] == -1) {
677         if (!nacked->client
678             || NOW - nacked->client->last_ack >= CLIENT_GIVEUP_MIN) {
679             /* The client (if there was one) has been unresponsive.
680              * Give up sending this packet, and kill the client if
681              * there was one.  (Make sure to remove nacked from the
682              * nack list before calling client_deregister(), which
683              * scans the nack list.)
684              */
685             LIST_DELETE(nacked);
686             if (nacked->client) {
687                 server_kill_clt(nacked->client);
688                 client_deregister(nacked->client, 1);
689             }
690             free(nacked->packet);
691             free(nacked);
692             return;
693         } else {
694             /* The client has sent us an ack recently.  Retry with the maximum
695              * retransmit time. */
696             nacked->rexmits--;
697         }
698     }
699
700     /* retransmit the packet */
701 #if 0
702     zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(nacked->dest.addr.sin_addr),
703            ntohs(nacked->dest.addr.sin_port)));
704 #endif
705     retval = ZSetDestAddr(&nacked->dest.addr);
706     if (retval != ZERR_NONE) {
707         syslog(LOG_WARNING, "rexmit set addr: %s", error_message(retval));
708     } else {
709         retval = ZSendPacket(nacked->packet, nacked->packsz, 0);
710         if (retval != ZERR_NONE)
711             syslog(LOG_WARNING, "rexmit xmit: %s", error_message(retval));
712         if (retval == EAGAIN || retval == ENOBUFS)
713             nacked->rexmits--;
714     }
715
716     /* reset the timer */
717     nacked->timer = timer_set_rel(rexmit_times[nacked->rexmits], rexmit,
718                                   nacked);
719     return;
720 }
721
722 /*
723  * Send an acknowledgement to the sending client, by sending back the
724  * header from the original notice with the z_kind field changed to either
725  * SERVACK or SERVNAK, and the contents of the message either SENT or
726  * NOT_SENT, depending on the value of the sent argument.
727  */
728
729 void
730 clt_ack(notice, who, sent)
731     ZNotice_t *notice;
732     struct sockaddr_in *who;
733     Sent_type sent;
734 {
735     ZNotice_t acknotice;
736     ZPacket_t ackpack;
737     int packlen;
738     int notme = 0;
739     char *sent_name;
740     Code_t retval;
741
742     if (bdumping) {             /* don't ack while dumping */
743 #if 1
744         zdbug((LOG_DEBUG,"bdumping, no ack"));
745 #endif
746         return;
747     }
748
749     acknotice = *notice;
750
751     acknotice.z_kind = SERVACK;
752     switch (sent) {
753       case SENT:
754         acknotice.z_message = ZSRVACK_SENT;
755         sent_name = "sent";
756         break;
757       case NOT_FOUND:
758         acknotice.z_message = ZSRVACK_FAIL;
759         acknotice.z_kind = SERVNAK;
760         sent_name = "fail";
761         break;
762       case AUTH_FAILED:
763         acknotice.z_kind = SERVNAK;
764         acknotice.z_message = ZSRVACK_NOTSENT;
765         sent_name = "nak/not_sent";
766         break;
767       case NOT_SENT:
768         acknotice.z_message = ZSRVACK_NOTSENT;
769         sent_name = "not_sent";
770         break;
771       default:
772         abort ();
773     }
774
775 #if 0
776     zdbug((LOG_DEBUG,"clt_ack type %s for %d to %s/%d", sent_name,
777            ntohs(notice->z_port), inet_ntoa(who->sin_addr),
778            ntohs(who->sin_port)));
779 #endif
780
781     acknotice.z_multinotice = "";
782
783     /* leave room for the trailing null */
784     acknotice.z_message_len = strlen(acknotice.z_message) + 1;
785
786     packlen = sizeof(ackpack);
787
788     retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
789     if (retval != ZERR_NONE) {
790         syslog(LOG_ERR, "clt_ack format: %s", error_message(retval));
791         return;
792     }
793     retval = ZSetDestAddr(who);
794     if (retval != ZERR_NONE) {
795         syslog(LOG_WARNING, "clt_ack set addr: %s", error_message(retval));
796         return;
797     }
798     retval = ZSendPacket(ackpack, packlen, 0);
799     if (retval != ZERR_NONE) {
800         syslog(LOG_WARNING, "clt_ack xmit: %s", error_message(retval));
801         return;
802     } else {
803         zdbug((LOG_DEBUG, "packet sent"));
804     }
805     return;
806 }
807
808 /*
809  * An ack has arrived.
810  * remove the packet matching this notice from the not-yet-acked queue
811  */
812
813 static void
814 nack_cancel(notice, who)
815     ZNotice_t *notice;
816     struct sockaddr_in *who;
817 {
818     Unacked *nacked;
819     int hashval;
820
821     /* search the not-yet-acked table for this packet, and flush it. */
822 #if 0
823     zdbug((LOG_DEBUG, "nack_cancel: %s:%08X,%08X",
824            inet_ntoa(notice->z_uid.zuid_addr),
825            notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
826 #endif
827     hashval = NACKTAB_HASHVAL(*who, notice->z_uid);
828     for (nacked = nacktab[hashval]; nacked; nacked = nacked->next) {
829         if (nacked->dest.addr.sin_addr.s_addr == who->sin_addr.s_addr
830             && nacked->dest.addr.sin_port == who->sin_port
831             && ZCompareUID(&nacked->uid, &notice->z_uid)) {
832             if (nacked->client)
833                 nacked->client->last_ack = NOW;
834             timer_reset(nacked->timer);
835             free(nacked->packet);
836             LIST_DELETE(nacked);
837             free(nacked);
838             return;
839         }
840     }
841
842 #if 1
843     zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X",
844            inet_ntoa (notice->z_uid.zuid_addr),
845            notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
846 #endif
847 }
848
849 /* for compatibility when sending subscription information to old clients */
850 #ifdef OLD_COMPAT
851 #define OLD_ZEPHYR_VERSION      "ZEPH0.0"
852 #endif /* OLD_COMPAT */
853
854 /* Dispatch an HM_CTL notice. */
855
856 Code_t
857 hostm_dispatch(notice, auth, who, server)
858     ZNotice_t *notice;
859     int auth;
860     struct sockaddr_in *who;
861     Server *server;
862 {
863     Server *owner;
864     char *opcode = notice->z_opcode;
865     Code_t retval;
866     int i, add = 0, remove = 0;
867
868 #if 0
869     zdbug((LOG_DEBUG,"hm_disp"));
870 #endif
871
872     if (notice->z_kind == HMACK) {
873         /* Ignore. */
874         ;
875     } else if (notice->z_kind != HMCTL) {
876 #if 0
877         zdbug((LOG_DEBUG, "bogus HM packet"));
878 #endif
879         clt_ack(notice, who, AUTH_FAILED);
880     } else if (strcmp(opcode, HM_FLUSH) == 0) {
881         client_flush_host(&who->sin_addr);
882         if (server == me_server)
883             server_forward(notice, auth, who);
884     } else if (strcmp(opcode, HM_BOOT) == 0) {
885         client_flush_host(&who->sin_addr);
886         if (server == me_server) {
887             server_forward(notice, auth, who);
888             ack(notice, who);
889             add = 1;
890         }
891     } else if (strcmp(opcode, HM_ATTACH) == 0) {
892         if (server == me_server) {
893             server_forward(notice, auth, who);
894             ack(notice, who);
895             add = 1;
896         } else {
897             remove = 1;
898         }
899     } else if (strcmp(opcode, HM_DETACH) == 0) {
900         remove = 1;
901     } else {
902         syslog(LOG_WARNING, "hm_dispatch: unknown opcode %s", opcode);
903     }
904
905     if (add) {
906         for (i = 0; i < num_hosts; i++) {
907             if (hosts[i].s_addr == who->sin_addr.s_addr)
908                 break;
909         }
910         if (i == num_hosts) {
911             if (hosts_size == 0) {
912                 hosts = (struct in_addr *) malloc(HOSTS_SIZE_INIT *
913                                                   sizeof(struct in_addr));
914                 if (!hosts)
915                     return ENOMEM;
916                 hosts_size = HOSTS_SIZE_INIT;
917             } else if (num_hosts == hosts_size) {
918                 hosts = (struct in_addr *) realloc(hosts, hosts_size * 2 *
919                                                    sizeof(struct in_addr));
920                 if (!hosts)
921                     return ENOMEM;
922                 hosts_size *= 2;
923             }
924             hosts[num_hosts++] = who->sin_addr;
925         }
926     } else if (remove) {
927         for (i = 0; i < num_hosts; i++) {
928             if (hosts[i].s_addr == who->sin_addr.s_addr) {
929                 memmove(&hosts[i], &hosts[i + 1], num_hosts - (i + 1));
930                 num_hosts--;
931                 break;
932             }
933         }
934     }
935     return ZERR_NONE;
936 }
937
938 /*
939  * Dispatch a ZEPHYR_CTL notice.
940  */
941
942 Code_t
943 control_dispatch(notice, auth, who, server)
944     ZNotice_t *notice;
945     int auth;
946     struct sockaddr_in *who;
947     Server *server;
948 {
949     char *opcode = notice->z_opcode;
950     Client *client;
951     Code_t retval;
952     int wantdefs;
953     Realm *realm;
954     struct sockaddr_in newwho;
955
956     /*
957      * ZEPHYR_CTL Opcodes expected are:
958      *  BOOT (inst HM): host has booted; flush data.
959      *  CLIENT_SUBSCRIBE: process with the subscription mananger.
960      *  CLIENT_UNSUBSCRIBE: ""
961      *  CLIENT_CANCELSUB:   ""
962      */
963
964     zdbug((LOG_DEBUG, "ctl_disp: opc=%s", opcode));
965
966     newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
967     newwho.sin_port = notice->z_port;
968     realm = realm_which_realm(&newwho);
969     if (realm)
970         return(realm_control_dispatch(notice, auth, who, server, realm));
971
972     if (strcasecmp(notice->z_class_inst, ZEPHYR_CTL_HM) == 0) {
973         return hostm_dispatch(notice, auth, who, server);
974     } else if (strcmp(opcode, CLIENT_GIMMESUBS) == 0 ||
975                strcmp(opcode, CLIENT_GIMMEDEFS) == 0) {
976         /* this special case is before the auth check so that
977            someone who has no subscriptions does NOT get a SERVNAK
978            but rather an empty list.  Note we must therefore
979            check authentication inside subscr_sendlist */
980 #ifdef OLD_COMPAT
981         /* only acknowledge if *not* old version; the old version
982            acknowledges the packet with the reply */
983         if (strcmp(notice->z_version, OLD_ZEPHYR_VERSION) != 0)
984             ack(notice, who);
985 #else /* !OLD_COMPAT */
986         ack(notice, who);
987 #endif /* OLD_COMPAT */
988         subscr_sendlist(notice, auth, who);
989         return ZERR_NONE;
990     } else if (!auth) {
991 #if 0
992         zdbug((LOG_DEBUG,"unauth ctrl_disp"));
993 #endif
994         if (server == me_server)
995             clt_ack(notice, who, AUTH_FAILED);
996         return ZERR_NONE;
997     }
998
999     wantdefs = strcmp(opcode, CLIENT_SUBSCRIBE_NODEFS);
1000     if (!wantdefs || strcmp(opcode, CLIENT_SUBSCRIBE) == 0) {
1001         /* subscription notice */
1002         retval = client_register(notice, &who->sin_addr, &client, wantdefs);
1003         if (retval != ZERR_NONE) {
1004             syslog(LOG_NOTICE, "subscr %s/%s/%d failed: %s",
1005                    notice->z_sender, inet_ntoa(who->sin_addr),
1006                    ntohs(notice->z_port), error_message(retval));
1007             if (server == me_server) {
1008                 if (retval == ZSRV_BADSUBPORT)
1009                     clt_ack(notice, who, AUTH_FAILED);
1010                 else
1011                     nack(notice, who);
1012             }
1013             return(ZERR_NONE);
1014         }
1015         if (strcmp(client->principal->string, notice->z_sender) != 0) {
1016             /* you may only subscribe for your own clients */
1017             if (server == me_server)
1018                 clt_ack(notice, who, AUTH_FAILED);
1019             return ZERR_NONE;
1020         }
1021 #ifdef HAVE_KRB4
1022         /* in case it's changed */
1023         memcpy(client->session_key, ZGetSession(), sizeof(C_Block));
1024 #endif
1025         retval = subscr_subscribe(client, notice);
1026         if (retval != ZERR_NONE) {
1027             syslog(LOG_WARNING, "subscr failed: %s", error_message(retval));
1028             if (server == me_server)
1029                 nack(notice, who);
1030             return ZERR_NONE;
1031         }
1032     } else if (strcmp(opcode, CLIENT_UNSUBSCRIBE) == 0) {
1033         client = client_find(&who->sin_addr, notice->z_port);
1034         if (client != NULL) {
1035             if (strcmp(client->principal->string, notice->z_sender) != 0) {
1036                 /* you may only cancel for your own clients */
1037                 if (server == me_server)
1038                     clt_ack(notice, who, AUTH_FAILED);
1039                 return ZERR_NONE;
1040             }
1041 #if 0
1042             if (zdebug) {
1043                 if (server == me_server) {
1044                     syslog(LOG_DEBUG, "subscription cancel for %s/%d\n",
1045                            inet_ntoa(who->sin_addr), ntohs(who->sin_port));
1046                 } else {
1047                     syslog(LOG_DEBUG,
1048                            "subscription cancel for %s/%d from %s\n",
1049                            inet_ntoa(who->sin_addr), ntohs(who->sin_port),
1050                            server->addr_str);
1051                 }
1052             }
1053 #endif
1054             subscr_cancel(who, notice);
1055         } else {
1056             nack(notice, who);
1057             return ZERR_NONE;
1058         }
1059     } else if (strcmp(opcode, CLIENT_CANCELSUB) == 0) {
1060         /* canceling subscriptions implies I can punt info about this client */
1061         client = client_find(&who->sin_addr, notice->z_port);
1062         if (client == NULL) {
1063 #if 0
1064             zdbug((LOG_DEBUG,"can_sub not found client"));
1065 #endif
1066             if (server == me_server)
1067                 nack(notice, who);
1068             return ZERR_NONE;
1069         }
1070         if (strcmp(client->principal->string, notice->z_sender) != 0) {
1071             /* you may only cancel for your own clients */
1072             if (server == me_server)
1073                 clt_ack(notice, who, AUTH_FAILED);
1074             return ZERR_NONE;
1075         }
1076         /* don't flush locations here, let him do it explicitly */
1077 #if 0
1078         zdbug((LOG_DEBUG, "cancelsub clt_dereg %s/%d",
1079                inet_ntoa(who->sin_addr), ntohs(who->sin_port)));
1080 #endif
1081         client_deregister(client, 0);
1082     } else {
1083         syslog(LOG_WARNING, "unknown ctl opcode %s", opcode);
1084         if (server == me_server)
1085             nack(notice, who);
1086         return ZERR_NONE;
1087     }
1088
1089     if (server == me_server) {
1090         ack(notice, who);
1091         server_forward(notice, auth, who);
1092     }
1093     return ZERR_NONE;
1094 }
1095
1096 void
1097 hostm_shutdown()
1098 {
1099     int i, s, newserver;
1100     struct sockaddr_in sin;
1101
1102     for (i = 0; i < nservers; i++) {
1103         if (i != me_server_idx && otherservers[i].state == SERV_UP)
1104             break;
1105     }
1106     newserver = (i < nservers);
1107     for (i = 0; i < num_hosts; i++) {
1108         sin.sin_addr = hosts[i];
1109         sin.sin_port = hm_port;
1110         if (newserver) {
1111             while (1) {
1112                 s = (random() % (nservers - 1)) + 1;
1113                 if (otherservers[s].state == SERV_UP)
1114                     break;
1115             }
1116             hostm_deathgram(&sin, &otherservers[s]);
1117         } else {
1118             hostm_deathgram(&sin, NULL);
1119         }
1120     }
1121 }
1122
1123 static void
1124 hostm_deathgram(sin, server)
1125     struct sockaddr_in *sin;
1126     Server *server;
1127 {
1128     Code_t retval;
1129     int shutlen;
1130     ZNotice_t shutnotice;
1131     char *shutpack;
1132
1133     shutnotice.z_kind = HMCTL;
1134     shutnotice.z_port = sin->sin_port; /* we are sending it */
1135     shutnotice.z_class = HM_CTL_CLASS;
1136     shutnotice.z_class_inst = HM_CTL_SERVER;
1137     shutnotice.z_opcode = SERVER_SHUTDOWN;
1138     shutnotice.z_sender = HM_CTL_SERVER;
1139     shutnotice.z_recipient = hm_recipient();
1140     shutnotice.z_default_format = "";
1141     shutnotice.z_num_other_fields = 0;
1142     shutnotice.z_message = (server) ? server->addr_str : NULL;
1143     shutnotice.z_message_len = (server) ? strlen(server->addr_str) + 1 : 0;
1144
1145     retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH);
1146     if (retval != ZERR_NONE) {
1147         syslog(LOG_ERR, "hm_shut format: %s",error_message(retval));
1148         return;
1149     }
1150     retval = ZSetDestAddr(sin);
1151     if (retval != ZERR_NONE) {
1152         syslog(LOG_WARNING, "hm_shut set addr: %s", error_message(retval));
1153         free(shutpack);
1154         return;
1155     }
1156     retval = ZSendPacket(shutpack, shutlen, 0);
1157     if (retval != ZERR_NONE)
1158         syslog(LOG_WARNING, "hm_shut xmit: %s", error_message(retval));
1159     free(shutpack);
1160 }
1161
1162 static char *
1163 hm_recipient()
1164 {
1165     static char *recipient;
1166     char *realm;
1167
1168     if (recipient)
1169         return recipient;
1170
1171     realm = ZGetRealm();
1172     if (!realm)
1173         realm = "???";
1174     recipient = (char *) malloc(strlen(realm) + 4);
1175     strcpy (recipient, "hm@");
1176     strcat (recipient, realm);
1177     return recipient;
1178 }
1179