Imported Upstream version 2.4.4p3
[debian/amanda] / common-src / protocol.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: protocol.c,v 1.27.2.1.6.3 2004/04/14 13:24:35 martinea Exp $
28  *
29  * implements amanda protocol
30  */
31 #include "amanda.h"
32 #include "protocol.h"
33 #include "version.h"
34 #ifdef KRB4_SECURITY
35 #  include "krb4-security.h"
36 #endif
37
38 #define ACK_WAIT        10       /* time (secs) to wait for ACK - keep short */
39 #define ACK_TRIES        3     /* # times we'll retry after ACK_WAIT timeout */
40 #define REQ_TRIES        2   /* # times client can start over (reboot/crash) */
41
42 #define DROP_DEAD_TIME  (60*60)    /* If no reply in an hour, just forget it */
43
44 #define MAX_HANDLES     4096
45 #define OFS_DIGITS         3    /* log2(MAX_HANDLES)/4 */
46
47 proto_t *pending_head = NULL;
48 proto_t *pending_tail = NULL;
49 int pending_qlength = 0;
50
51 int proto_socket = -1;
52 int proto_global_seq = 0;
53 #define relseq(s) (s-proto_global_seq)
54
55 proto_t **proto_handle_table;
56 proto_t **proto_next_handle;
57 int proto_handles;
58
59 time_t proto_init_time;
60 #define CURTIME (time(0)-proto_init_time)
61
62 /* local functions */
63 static char *prnpstate P((pstate_t s));
64 static char *prnaction P((action_t s));
65 #ifdef PROTO_DEBUG
66 static char *prnpktype P((pktype_t s));
67 #endif
68 static void pending_enqueue P((proto_t *newp));
69 static proto_t *pending_dequeue P((void));
70 static void pending_remove P((proto_t *p));
71 static void alloc_handle P((proto_t *p));
72 static void free_handle P((proto_t *p));
73 static void hex P((char *str, int digits, unsigned int v));
74 static int unhex P((char *str, int digits));
75 static proto_t *handle2ptr P((char *str));
76 static char *ptr2handle P((proto_t *p));
77 static void eat_string P((dgram_t *msg, char *str));
78 static int parse_integer P((dgram_t *msg));
79 static char *parse_string P((dgram_t *msg));
80 static char *parse_line P((dgram_t *msg));
81 void parse_pkt_header P((pkt_t *pkt));
82 static void setup_dgram P((proto_t *p, dgram_t *msg, 
83                            char *security, char *typestr));
84 static void send_req P((proto_t *p));
85 static void send_ack P((proto_t *p));
86 static void send_ack_repl P((pkt_t *pkt));
87 static void state_machine P((proto_t *p, action_t action, pkt_t *pkt));
88 static void add_bsd_security P((proto_t *p));
89 static int select_til P((time_t waketime));
90 static int packet_arrived P((void));
91 static void handle_incoming_packet P((void)); 
92
93
94 /* -------------- */
95
96
97 static char *prnpstate(s)
98 pstate_t s;
99 {
100     static char str[80];
101
102     switch(s) {
103     case S_BOGUS: return "S_BOGUS";
104     case S_STARTUP:  return "S_STARTUP";
105     case S_SENDREQ:  return "S_SENDREQ";
106     case S_ACKWAIT:  return "S_ACKWAIT";
107     case S_REPWAIT:  return "S_REPWAIT";
108     case S_SUCCEEDED:  return "S_SUCCEEDED";
109     case S_FAILED: return "S_FAILED";
110     default:
111         ap_snprintf(str, sizeof(str), "<bad state %d>", s);
112         return str;
113     }
114 }
115
116 static char *prnaction(s)
117 action_t s;
118 {
119     static char str[80];
120
121     switch(s) {
122     case A_BOGUS:  return "A_BOGUS";
123     case A_START:  return "A_START";
124     case A_TIMEOUT:  return "A_TIMEOUT";
125     case A_RCVDATA: return "A_RCVDATA";
126     default:
127         ap_snprintf(str, sizeof(str), "<bad action %d>", s);
128         return str;
129     }
130 }
131
132 #ifdef PROTO_DEBUG
133
134 static char *prnpktype(s)
135 pktype_t s;
136 {
137     static char str[80];
138
139     switch(s) {
140     case P_BOGUS: return "P_BOGUS";
141     case P_REQ: return "P_REQ";
142     case P_REP: return "P_REP";
143     case P_ACK: return "P_ACK";
144     case P_NAK: return "P_NAK";
145     default:
146         ap_snprintf(str, sizeof(str), "<bad pktype %d>", s);
147         return str;
148     }
149 }
150
151 #endif
152
153
154 void proto_init(socket, startseq, handles)
155 int socket, startseq, handles;
156 {
157     int i;
158
159 #ifdef PROTO_DEBUG
160     dbprintf(("%s: proto_init(socket %d, startseq %d, handles %d)\n",
161               debug_prefix_time(": protocol"),
162               socket,
163               startseq,
164               handles));
165 #endif
166     if(socket < 0 || socket >= FD_SETSIZE) {
167         error("proto_init: socket %d out of range (0 .. %d)\n",
168               socket, FD_SETSIZE-1);
169     }
170     proto_socket = socket;
171     proto_global_seq = startseq;
172     proto_handles = handles;
173
174     proto_handle_table = alloc(proto_handles * sizeof(proto_t *));
175     malloc_mark(proto_handle_table);
176     proto_next_handle = proto_handle_table;
177     for(i = 0; i < proto_handles; i++)
178         proto_handle_table[i] = NULL;
179     proto_init_time = time(0);
180 }
181
182
183 static void pending_enqueue(newp)
184 proto_t *newp;
185 {
186     proto_t *curp;
187
188     /* common case shortcut: check if adding to end of list */
189
190     if(pending_tail && pending_tail->timeout <= newp->timeout)
191         curp = NULL;
192     else {
193         /* scan list for insert-sort */
194         curp = pending_head;
195         while(curp && curp->timeout <= newp->timeout)
196             curp = curp->next;
197     }
198
199     newp->next = curp;
200     if(curp) {
201         newp->prev = curp->prev;
202         curp->prev = newp;
203     }
204     else {
205         newp->prev = pending_tail;
206         pending_tail = newp;
207     }
208
209     if(newp->prev) newp->prev->next = newp;
210     else pending_head = newp;
211
212     pending_qlength++;
213 }
214
215 static proto_t *pending_dequeue()
216 {
217     proto_t *p;
218
219     p = pending_head;
220     if(p) {
221         pending_head = p->next;
222         p->next = NULL;
223         if(pending_head) 
224             pending_head->prev = NULL;
225         else
226             pending_tail = NULL;
227         pending_qlength--;
228     }
229
230     return p;
231 }
232
233 static void pending_remove(p)
234 proto_t *p;
235 {
236     if(p->next) p->next->prev = p->prev;
237     else pending_tail = p->prev;
238
239     if(p->prev) p->prev->next = p->next;
240     else pending_head = p->next;
241
242     p->prev = p->next = NULL;
243     pending_qlength--;
244 }
245
246 /* -------- */
247
248 #define PTR_CHARS    sizeof(proto_t *)              /* chars in a pointer */
249 #define CHAR_DIGITS  2                              /* hex digits in a char */
250 #define HANDLE_CHARS (OFS_DIGITS+1+PTR_CHARS*CHAR_DIGITS) /* "xxx-yyyyyyyy" */
251 union handle_u {
252     unsigned char c[PTR_CHARS];
253     proto_t *p;
254 } hu;
255
256 static void alloc_handle(p)
257 proto_t *p;
258 {
259     int i;
260     proto_t **hp;
261
262     hp = proto_next_handle;
263     for(i = 0; i < proto_handles; i++) {
264         if(*hp == NULL) break;
265         hp++;
266         if(hp >= proto_handle_table + proto_handles)
267             hp = proto_handle_table;
268     }
269     if(i == proto_handles)
270         error("protocol out of handles");
271     p->handleofs = hp-proto_handle_table;
272     *hp = p;
273 }
274
275 static void free_handle(p)
276 proto_t *p;
277 {
278     if(proto_handle_table[p->handleofs] == p)
279         proto_handle_table[p->handleofs] = NULL;
280     p->handleofs = -1;
281 }
282
283 static void hex(str, digits, v)
284 char *str;
285 int digits;
286 unsigned int v;
287 {
288     str = str + digits - 1;
289
290     while(digits--) {
291         *str-- = "0123456789ABCDEF"[v % 16];
292         v /= 16;
293     }
294 }
295
296 static int unhex(str, digits)
297 char *str;
298 int digits;
299 {
300     int d, v = 0;
301
302     while(*str && digits--) {
303         d = *str >= 'A'? *str - 'A' + 10 : *str - '0';
304         v = v * 16 + d;
305         str++;
306     }
307     return v;
308 }
309
310
311 static proto_t *handle2ptr(str)
312 char *str;
313 {
314     int ofs, i;
315
316     if(strlen(str) != HANDLE_CHARS)
317         return NULL;
318
319     ofs = unhex(str, OFS_DIGITS);
320     str += OFS_DIGITS;
321     if(ofs < 0 || ofs >= proto_handles)
322         return NULL;
323
324     if(*str++ != '-')
325         return NULL;
326
327     for(i=0; i < PTR_CHARS; i++) {
328         hu.c[i] = unhex(str, CHAR_DIGITS);
329         str += CHAR_DIGITS;
330     }
331
332     if(proto_handle_table[ofs] != hu.p)
333         return NULL;
334
335     return hu.p;
336 }
337
338
339 static char *ptr2handle(p)
340 proto_t *p;
341 {
342     int i;
343     char *s;
344     static char hstr[HANDLE_CHARS+1];
345
346     assert(p->handleofs != -1 && proto_handle_table[p->handleofs] == p);
347
348     hu.p = p;
349
350     hex(hstr, OFS_DIGITS, p->handleofs);
351     s = &hstr[OFS_DIGITS];
352     *s++ = '-';
353
354     for(i=0;i<PTR_CHARS;i++) {
355         hex(s, CHAR_DIGITS, hu.c[i]);
356         s += CHAR_DIGITS;
357     }
358     *s = '\0';
359     return hstr;
360 }
361
362 /* -------- */
363
364 jmp_buf parse_failed;
365 char *parse_errmsg = NULL;
366
367 static void eat_string(msg, str)
368 dgram_t *msg;
369 char *str;
370 {
371     char *saved_str, *saved_msg;
372
373     /* eat leading whitespace */
374     while(isspace((int)(*msg->cur))) msg->cur++;
375
376     saved_msg = msg->cur;
377     saved_str = str;
378
379     /* eat any characters that match str */
380     while(*str && *msg->cur++ == *str++);
381
382     /* if we didn't eat all of str, we've failed */
383     if(*str) {
384         int len = strlen(saved_str);
385         char *tmp = NULL;
386         
387         tmp = alloc(len+1);
388         strncpy(tmp, saved_msg, len);
389         tmp[len] = '\0';
390         parse_errmsg = newvstralloc(parse_errmsg,
391                                     "expected \"", saved_str, "\",",
392                                     " got \"", tmp, "\"",
393                                     NULL);
394         amfree(tmp);
395         longjmp(parse_failed,1);
396     }
397 }
398
399 static int parse_integer(msg)
400 dgram_t *msg;
401 {
402     int i = 0;
403     int sign = 1;
404
405     /* eat leading whitespace */
406     while(isspace((int)(*msg->cur))) msg->cur++;
407
408     /* handle negative values */
409     if(*msg->cur == '-') {
410         sign = -1;
411         msg->cur++;
412     }
413
414     /* must have at least one digit */
415     if(*msg->cur < '0' || *msg->cur > '9') {
416         char non_digit[2];
417
418         non_digit[0] = *msg->cur;
419         non_digit[1] = '\0';
420         parse_errmsg = newvstralloc(parse_errmsg,
421                                     "expected digit, got \"", non_digit, "\"",
422                                     NULL);
423         longjmp(parse_failed,1);
424     }
425
426     while(*msg->cur >= '0' && *msg->cur <= '9') {
427         i = i * 10 + (*msg->cur - '0');
428         msg->cur++;
429     }
430     return sign * i;
431 }
432
433 static char *parse_string(msg)
434 dgram_t *msg;
435 {
436     char *str;
437
438     /* eat leading whitespace */
439     while(isspace((int)(*msg->cur))) msg->cur++;
440
441     /* mark start of string */
442     str = msg->cur;
443
444     /* stop at whitespace (including newlines) or end-of-packet */
445     while(*msg->cur && !isspace((int)(*msg->cur))) msg->cur++;
446
447     /* empty fields not allowed */
448     if(msg->cur == str) {
449         parse_errmsg = newstralloc(parse_errmsg,
450                                    "expected string, got empty field");
451         longjmp(parse_failed,1);
452     }
453
454     /* mark end of string in the packet, but don't fall off the end of it */
455     if(*msg->cur) *msg->cur++ = '\0';
456
457     return str;
458 }
459
460 static char *parse_line(msg)
461 dgram_t *msg;
462 {
463     char *str;
464
465     /* eat leading whitespace */
466     while(isspace((int)(*msg->cur))) msg->cur++;
467
468     /* mark start of string */
469     str = msg->cur;
470
471     /* stop at end of line or end-of-packet */
472     while(*msg->cur && *msg->cur != '\n') msg->cur++;
473
474     /* empty fields not allowed */
475     if(msg->cur == str) {
476         parse_errmsg = newstralloc(parse_errmsg,
477                                    "expected string, got empty field");
478         longjmp(parse_failed,1);
479     }
480
481     /* mark end of string in the packet, but don't fall off the end of it */
482     if(*msg->cur) *msg->cur++ = '\0';
483
484     return str;
485 }
486
487 void parse_pkt_header(pkt)
488 pkt_t *pkt;
489 {
490     dgram_t *msg;
491     char *typestr;
492
493     if(setjmp(parse_failed)) {
494 /*      dbprintf(("%s: leftover:\n----\n%s----\n\n", errmsg, msg->cur)); */
495         pkt->type = P_BOGUS;
496         return;
497     }
498
499     msg = &pkt->dgram;
500
501 #ifdef PROTO_DEBUG
502     dbprintf(("%s: parsing packet:\n-------\n%s-------\n\n",
503               debug_prefix_time(": protocol"),
504               msg->cur));
505 #endif
506
507     eat_string(msg, "Amanda");      pkt->version_major = parse_integer(msg);
508     eat_string(msg, ".");           pkt->version_minor = parse_integer(msg);
509     typestr = parse_string(msg);
510
511     if(strcmp(typestr, "REQ") == 0) pkt->type = P_REQ;
512     else if(strcmp(typestr, "REP") == 0) pkt->type = P_REP;
513     else if(strcmp(typestr, "ACK") == 0) pkt->type = P_ACK;
514     else if(strcmp(typestr, "NAK") == 0) pkt->type = P_NAK;
515     else pkt->type = P_BOGUS;
516
517     eat_string(msg, "HANDLE");      pkt->handle = parse_string(msg);
518     eat_string(msg, "SEQ");         pkt->sequence = parse_integer(msg);
519
520     eat_string(msg, "");
521 #define sc "SECURITY "
522     if(strncmp(msg->cur, sc, sizeof(sc)-1) == 0) {
523         /* got security tag */
524         eat_string(msg, sc);
525 #undef sc
526         pkt->security = parse_line(msg);
527     }
528     else pkt->security = NULL;
529
530     if(pkt->type == P_REQ) {
531
532 #ifdef KRB4_SECURITY
533         eat_string(msg, "");
534         pkt->cksum = kerberos_cksum(msg->cur);
535 #ifdef PROTO_DEBUG
536         dbprintf(("%s: parse_pkt/cksum %ld over \'%s\'\n\n",
537                   debug_prefix_time(": protocol"),
538                   pkt->cksum,
539                   msg->cur)); 
540 #endif
541         fflush(stdout);
542 #endif
543         eat_string(msg, "SERVICE");     pkt->service = parse_string(msg);
544     }
545
546     eat_string(msg, "");
547     pkt->body = msg->cur;
548 }
549
550 static void setup_dgram(p, msg, security, typestr)
551 proto_t *p;
552 dgram_t *msg;
553 char *security, *typestr;
554 {
555     char *linebuf = NULL;
556     char major_str[NUM_STR_SIZE];
557     char minor_str[NUM_STR_SIZE];
558     char seq_str[NUM_STR_SIZE];
559
560     ap_snprintf(major_str, sizeof(major_str), "%d", VERSION_MAJOR);
561     ap_snprintf(minor_str, sizeof(minor_str), "%d", VERSION_MINOR);
562     ap_snprintf(seq_str, sizeof(seq_str), "%d", p->curseq);
563
564     dgram_zero(msg);
565     dgram_socket(msg,proto_socket);
566     linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
567                         " ", typestr,
568                         " HANDLE ", ptr2handle(p),
569                         " SEQ ", seq_str,
570                         "\n",
571                         security ? security : "",
572                         security ? "\n" : "",
573                         NULL);
574     dgram_cat(msg, linebuf);
575     amfree(linebuf);
576 }
577
578 static void send_req(p)
579 proto_t *p;
580 {
581     dgram_t outmsg;
582
583     setup_dgram(p, &outmsg, p->security, "REQ");
584     dgram_cat(&outmsg, p->req);
585
586 #ifdef PROTO_DEBUG
587     dbprintf(("%s: send_req: len %d: packet:\n----\n%s----\n\n", 
588               debug_prefix_time(": protocol"),
589               outmsg.len,
590               outmsg.data));
591 #endif
592
593     if(dgram_send_addr(p->peer, &outmsg))
594         fprintf(stderr,"send req failed: %s\n", strerror(errno));
595 }
596
597 static void send_ack(p)
598 proto_t *p;
599 {
600     dgram_t outmsg;
601
602     setup_dgram(p, &outmsg, NULL, "ACK");
603
604 #ifdef PROTO_DEBUG
605     dbprintf(("%s: send_ack: len %d: packet:\n----\n%s----\n\n", 
606               debug_prefix_time(": protocol"),
607               outmsg.len,
608               outmsg.data));
609 #endif
610
611     if(dgram_send_addr(p->peer, &outmsg))
612         error("send ack failed: %s", strerror(errno));
613 }
614
615 static void send_ack_repl(pkt)
616 pkt_t *pkt;
617 {
618     dgram_t outmsg;
619     char *linebuf = NULL;
620     char major_str[NUM_STR_SIZE];
621     char minor_str[NUM_STR_SIZE];
622     char seq_str[NUM_STR_SIZE];
623
624     ap_snprintf(major_str, sizeof(major_str), "%d", VERSION_MAJOR);
625     ap_snprintf(minor_str, sizeof(minor_str), "%d", VERSION_MINOR);
626     ap_snprintf(seq_str, sizeof(seq_str), "%d", pkt->sequence);
627
628     dgram_zero(&outmsg);
629     dgram_socket(&outmsg,proto_socket);
630
631     linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
632                         " ACK HANDLE ", pkt->handle,
633                         " SEQ ", seq_str,
634                         "\n", NULL);
635
636     dgram_cat(&outmsg, linebuf);
637     amfree(linebuf);
638
639 #ifdef PROTO_DEBUG
640     dbprintf(("%s: send_ack_repl: len %d: packet:\n----\n%s----\n\n", 
641               debug_prefix_time(": protocol"),
642               outmsg.len,
643               outmsg.data));
644 #endif
645
646     if(dgram_send_addr(pkt->peer, &outmsg))
647         error("send ack failed: %s", strerror(errno));
648 }
649
650
651 static void state_machine(p, action, pkt)
652 proto_t *p;
653 action_t action;
654 pkt_t *pkt;
655 {
656
657 #ifdef PROTO_DEBUG
658     dbprintf(("%s: state_machine: p %X state %s action %s%s%s\n",
659               debug_prefix_time(": protocol"),
660               (int)p,
661               prnpstate(p->state),
662               prnaction(action),
663               pkt == NULL? "" : " pktype ",
664               pkt == NULL? "" : prnpktype(pkt->type)));
665 #endif
666
667     while(1) {
668         p->prevstate = p->state;
669         switch(p->state) {
670         case S_STARTUP: 
671             if(action != A_START) goto badaction;
672             p->origseq = p->curseq = proto_global_seq++;
673             p->reqtries = REQ_TRIES;
674             p->state = S_SENDREQ;
675             p->acktries = ACK_TRIES;
676             alloc_handle(p);
677             break;
678
679         case S_SENDREQ:
680             send_req(p);
681             p->curtime = CURTIME;
682             if(p->curseq == p->origseq) p->origtime = p->curtime;
683             p->timeout = time(0) + ACK_WAIT;
684             p->state = S_ACKWAIT;
685             pending_enqueue(p);
686             return;
687
688         case S_ACKWAIT:
689             if(action == A_TIMEOUT) {
690                 if(--p->acktries == 0) {
691                     p->state = S_FAILED;
692                     free_handle(p);
693                     p->continuation(p, NULL);
694                     amfree(p->req);
695                     amfree(p->security);
696                     amfree(p);
697                     return;
698                 }
699                 else {
700                     p->state = S_SENDREQ;
701                     break;
702                 }
703             }
704             else if(action != A_RCVDATA)
705                 goto badaction;
706
707             /* got the packet with the right handle, now check it */
708
709 #ifdef PROTO_DEBUG
710             dbprintf((
711          "%s: RESPTIME p %X pkt %s (t %d s %d) orig (t %d s %d) cur (t %d s %d)\n",
712                     debug_prefix_time(": protocol"),
713                     (int)p, prnpktype(pkt->type), 
714                     (int)CURTIME, relseq(pkt->sequence),
715                     (int)p->origtime, relseq(p->origseq), 
716                     (int)p->curtime, relseq(p->curseq)));
717 #endif
718
719             if(pkt->type == P_ACK) {
720                 if(pkt->sequence != p->origseq)
721                     p->reqtries--;
722                 p->state = S_REPWAIT;
723                 p->timeout = time(0) + p->repwait;
724                 pending_enqueue(p);
725                 return;
726             }
727             else if(pkt->type == P_NAK) {
728                 p->state = S_FAILED;
729                 free_handle(p);
730                 p->continuation(p, pkt);
731                 amfree(p->req);
732                 amfree(p->security);
733                 amfree(p);
734                 return;
735             }
736             else if(pkt->type == P_REP) {
737                 /* no ack, just rep */
738                 p->state = S_REPWAIT;
739                 break;
740             }
741             /* else unexpected packet, put back on queue */
742             pending_enqueue(p);
743             return;
744
745         case S_REPWAIT:
746             if(action == A_TIMEOUT) {
747                 if(p->reqtries == 0 || 
748                    (CURTIME - p->origtime > DROP_DEAD_TIME)) {
749                     p->state = S_FAILED;
750                     free_handle(p);
751                     p->continuation(p, NULL);
752                     amfree(p->req);
753                     amfree(p->security);
754                     amfree(p);
755                     return;
756                 }
757                 else {
758                     p->reqtries--;
759                     p->state = S_SENDREQ;
760                     p->acktries = ACK_TRIES;
761                     break;
762                 }
763             }
764             else if(action != A_RCVDATA)
765                 goto badaction;
766             /* got the packet with the right handle, now check it */
767             if(pkt->type != P_REP) {
768                 pending_enqueue(p);
769                 return;
770             }
771             send_ack(p);
772             p->state = S_SUCCEEDED;
773             free_handle(p);
774             p->continuation(p, pkt);
775             amfree(p->req);
776             amfree(p->security);
777             amfree(p);
778             return;
779
780         default:
781         badaction:
782             error("protocol error: no handler for state %s action %s\n",
783                   prnpstate(p->state), prnaction(action));
784         }
785     }
786 }
787
788 static void add_bsd_security(p)
789 proto_t *p;
790 {
791     p->security = get_bsd_security();
792 }
793
794 int make_request(hostname, port, req, datap, repwait, continuation)
795 char *hostname;
796 int port;
797 char *req;
798 void *datap;
799 time_t repwait;
800 void (*continuation) P((proto_t *p, pkt_t *pkt));
801 {
802     proto_t *p;
803     struct hostent *hp;
804
805
806     p = alloc(sizeof(proto_t));
807     p->state = S_STARTUP;
808     p->prevstate = S_STARTUP;
809     p->continuation = continuation;
810     p->req = req;
811     p->repwait = repwait;
812     p->datap = datap;
813
814 #ifdef PROTO_DEBUG
815     dbprintf(("%s: make_request: host %s -> p %X\n", 
816               debug_prefix_time(": protocol"),
817               hostname,
818               (int)p));
819 #endif
820
821     if((hp = gethostbyname(hostname)) == 0) return -1;
822     memcpy(&p->peer.sin_addr, hp->h_addr, hp->h_length);
823     p->peer.sin_family = AF_INET;
824     p->peer.sin_port = htons(port);
825
826     add_bsd_security(p);
827
828     state_machine(p, A_START, NULL);
829     return 0;
830 }
831
832 #ifdef KRB4_SECURITY
833
834 static int add_krb_security P((proto_t *p, char *host_inst, char *realm));
835
836 static int add_krb_security(p, host_inst, realm)
837 proto_t *p;
838 char *host_inst, *realm;
839 {
840     p->security = get_krb_security(p->req, host_inst, realm, &p->auth_cksum);
841
842 #ifdef PROTO_DEBUG
843     dbprintf(("%s: add_krb_security() cksum: %lu: \'%s\'\n",
844               debug_prefix_time(": protocol"),
845               p->auth_cksum,
846               p->req));
847 #endif
848
849     return p->security == NULL;
850 }
851
852 int make_krb_request(hostname, port, req, datap, repwait, continuation)
853 char *hostname;
854 int port;
855 char *req;
856 void *datap;
857 time_t repwait;
858 void (*continuation) P((proto_t *p, pkt_t *pkt));
859 {
860     proto_t *p;
861     struct hostent *hp;
862     char inst[256], realm[256];
863     int rc;
864
865     p = alloc(sizeof(proto_t));
866     p->state = S_STARTUP;
867     p->prevstate = S_STARTUP;
868     p->continuation = continuation;
869     p->req = req;
870     p->repwait = repwait;
871     p->datap = datap;
872
873 #ifdef PROTO_DEBUG
874     dbprintf(("%s: make_krb_request: host %s -> p %X\n", 
875               debug_prefix_time(": protocol"),
876               hostname,
877               req, (int)p));
878 #endif
879
880     if((hp = host2krbname(hostname, inst, realm)) == 0)
881         return -1;
882     memcpy(&p->peer.sin_addr, hp->h_addr, hp->h_length);
883     p->peer.sin_family = AF_INET;
884     p->peer.sin_port = htons(port);
885
886     if((rc = add_krb_security(p, inst, realm)))
887         return rc;
888
889     state_machine(p, A_START, NULL);
890     return 0;
891 }
892
893 #endif
894
895 static int select_til(waketime) 
896 time_t waketime;
897 {
898     fd_set ready;
899     struct timeval to;
900     time_t waittime;
901     int rc;
902
903     waittime = waketime - time(0);
904     if(waittime < 0) waittime = 0;      /* just poll */
905
906     FD_ZERO(&ready);
907     FD_SET(proto_socket, &ready);
908     to.tv_sec = waittime;
909     to.tv_usec = 0;
910
911     rc = select(proto_socket+1, (SELECT_ARG_TYPE *)&ready, NULL, NULL, &to);
912     if(rc == -1) {
913         error("protocol socket select: %s", strerror(errno));
914     }
915     return rc;
916 }
917
918 static int packet_arrived() 
919 {
920     return select_til(0);
921 }
922
923 static void handle_incoming_packet() 
924 {
925     pkt_t inpkt;
926     proto_t *p;
927
928     dgram_zero(&inpkt.dgram);
929     dgram_socket(&inpkt.dgram, proto_socket);
930     if(dgram_recv(&inpkt.dgram, 0, &inpkt.peer) == -1) {
931 #ifdef ECONNREFUSED
932         if(errno == ECONNREFUSED)
933             return;
934 #endif
935 #ifdef EAGAIN
936         if(errno == EAGAIN)
937             return;
938 #endif
939
940         fprintf(stderr,"protocol packet receive: %s\n", strerror(errno));
941     }
942
943 #ifdef PROTO_DEBUG
944     dbprintf(("%s: got packet:\n----\n%s----\n\n",
945               debug_prefix_time(": protocol"),
946               inpkt.dgram.data));
947 #endif
948
949     parse_pkt_header(&inpkt);
950     if(inpkt.type == P_BOGUS)
951         return;
952     if((p = handle2ptr(inpkt.handle)) == NULL) {
953         /* ack succeeded reps */
954         if(inpkt.type == P_REP)
955             send_ack_repl(&inpkt);
956         return;
957     }
958
959 #ifdef PROTO_DEBUG
960     dbprintf(("%s: handle %s p %X got packet type %s\n",
961               debug_prefix_time(": protocol"),
962               inpkt.handle,
963               (int)p,
964               prnpktype(inpkt.type)));
965 #endif
966
967     pending_remove(p);
968     state_machine(p, A_RCVDATA, &inpkt);
969 }
970
971
972
973 void check_protocol()
974 {
975     time_t curtime;
976     proto_t *p;
977
978     while(packet_arrived())
979         handle_incoming_packet();
980
981     curtime = time(0);
982     while(pending_head && curtime >= pending_head->timeout) {
983         p = pending_dequeue();
984         state_machine(p, A_TIMEOUT, NULL);
985     }
986 }
987
988
989 void run_protocol()
990 {
991     time_t wakeup_time;
992     proto_t *p;
993
994     while(pending_head) {
995         wakeup_time = pending_head->timeout;
996
997 #ifdef PROTO_DEBUG
998         dbprintf(("%s: run_protocol: waiting %d secs for %d pending reqs\n",
999                   debug_prefix_time(": protocol"),
1000                   (int)(wakeup_time - time(0)),
1001                   pending_qlength));
1002 #endif
1003
1004         if(select_til(wakeup_time))
1005             handle_incoming_packet();
1006         else {
1007             p = pending_dequeue();
1008             state_machine(p, A_TIMEOUT, NULL);
1009         }
1010     }
1011 }