Imported Upstream version 2.4.5
[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.2.2.2 2004/04/14 13:24:17 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_PREP: return "P_PREP";
144     case P_ACK: return "P_ACK";
145     case P_NAK: return "P_NAK";
146     default:
147         ap_snprintf(str, sizeof(str), "<bad pktype %d>", s);
148         return str;
149     }
150 }
151
152 #endif
153
154
155 void proto_init(socket, startseq, handles)
156 int socket, startseq, handles;
157 {
158     int i;
159
160 #ifdef PROTO_DEBUG
161     dbprintf(("%s: proto_init(socket %d, startseq %d, handles %d)\n",
162               debug_prefix_time(": protocol"),
163               socket,
164               startseq,
165               handles));
166 #endif
167     if(socket < 0 || socket >= FD_SETSIZE) {
168         error("proto_init: socket %d out of range (0 .. %d)\n",
169               socket, FD_SETSIZE-1);
170     }
171     proto_socket = socket;
172     proto_global_seq = startseq;
173     proto_handles = handles;
174
175     proto_handle_table = alloc(proto_handles * sizeof(proto_t *));
176     malloc_mark(proto_handle_table);
177     proto_next_handle = proto_handle_table;
178     for(i = 0; i < proto_handles; i++)
179         proto_handle_table[i] = NULL;
180     proto_init_time = time(0);
181 }
182
183
184 static void pending_enqueue(newp)
185 proto_t *newp;
186 {
187     proto_t *curp;
188
189     /* common case shortcut: check if adding to end of list */
190
191     if(pending_tail && pending_tail->timeout <= newp->timeout)
192         curp = NULL;
193     else {
194         /* scan list for insert-sort */
195         curp = pending_head;
196         while(curp && curp->timeout <= newp->timeout)
197             curp = curp->next;
198     }
199
200     newp->next = curp;
201     if(curp) {
202         newp->prev = curp->prev;
203         curp->prev = newp;
204     }
205     else {
206         newp->prev = pending_tail;
207         pending_tail = newp;
208     }
209
210     if(newp->prev) newp->prev->next = newp;
211     else pending_head = newp;
212
213     pending_qlength++;
214 }
215
216 static proto_t *pending_dequeue()
217 {
218     proto_t *p;
219
220     p = pending_head;
221     if(p) {
222         pending_head = p->next;
223         p->next = NULL;
224         if(pending_head) 
225             pending_head->prev = NULL;
226         else
227             pending_tail = NULL;
228         pending_qlength--;
229     }
230
231     return p;
232 }
233
234 static void pending_remove(p)
235 proto_t *p;
236 {
237     if(p->next) p->next->prev = p->prev;
238     else pending_tail = p->prev;
239
240     if(p->prev) p->prev->next = p->next;
241     else pending_head = p->next;
242
243     p->prev = p->next = NULL;
244     pending_qlength--;
245 }
246
247 /* -------- */
248
249 #define PTR_CHARS    sizeof(proto_t *)              /* chars in a pointer */
250 #define CHAR_DIGITS  2                              /* hex digits in a char */
251 #define HANDLE_CHARS (OFS_DIGITS+1+PTR_CHARS*CHAR_DIGITS) /* "xxx-yyyyyyyy" */
252 union handle_u {
253     unsigned char c[PTR_CHARS];
254     proto_t *p;
255 } hu;
256
257 static void alloc_handle(p)
258 proto_t *p;
259 {
260     int i;
261     proto_t **hp;
262
263     hp = proto_next_handle;
264     for(i = 0; i < proto_handles; i++) {
265         if(*hp == NULL) break;
266         hp++;
267         if(hp >= proto_handle_table + proto_handles)
268             hp = proto_handle_table;
269     }
270     if(i == proto_handles)
271         error("protocol out of handles");
272     p->handleofs = hp-proto_handle_table;
273     *hp = p;
274 }
275
276 static void free_handle(p)
277 proto_t *p;
278 {
279     if(proto_handle_table[p->handleofs] == p)
280         proto_handle_table[p->handleofs] = NULL;
281     p->handleofs = -1;
282 }
283
284 static void hex(str, digits, v)
285 char *str;
286 int digits;
287 unsigned int v;
288 {
289     str = str + digits - 1;
290
291     while(digits--) {
292         *str-- = "0123456789ABCDEF"[v % 16];
293         v /= 16;
294     }
295 }
296
297 static int unhex(str, digits)
298 char *str;
299 int digits;
300 {
301     int d, v = 0;
302
303     while(*str && digits--) {
304         d = *str >= 'A'? *str - 'A' + 10 : *str - '0';
305         v = v * 16 + d;
306         str++;
307     }
308     return v;
309 }
310
311
312 static proto_t *handle2ptr(str)
313 char *str;
314 {
315     int ofs, i;
316
317     if(strlen(str) != HANDLE_CHARS)
318         return NULL;
319
320     ofs = unhex(str, OFS_DIGITS);
321     str += OFS_DIGITS;
322     if(ofs < 0 || ofs >= proto_handles)
323         return NULL;
324
325     if(*str++ != '-')
326         return NULL;
327
328     for(i=0; i < PTR_CHARS; i++) {
329         hu.c[i] = unhex(str, CHAR_DIGITS);
330         str += CHAR_DIGITS;
331     }
332
333     if(proto_handle_table[ofs] != hu.p)
334         return NULL;
335
336     return hu.p;
337 }
338
339
340 static char *ptr2handle(p)
341 proto_t *p;
342 {
343     int i;
344     char *s;
345     static char hstr[HANDLE_CHARS+1];
346
347     assert(p->handleofs != -1 && proto_handle_table[p->handleofs] == p);
348
349     hu.p = p;
350
351     hex(hstr, OFS_DIGITS, p->handleofs);
352     s = &hstr[OFS_DIGITS];
353     *s++ = '-';
354
355     for(i=0;i<PTR_CHARS;i++) {
356         hex(s, CHAR_DIGITS, hu.c[i]);
357         s += CHAR_DIGITS;
358     }
359     *s = '\0';
360     return hstr;
361 }
362
363 /* -------- */
364
365 jmp_buf parse_failed;
366 char *parse_errmsg = NULL;
367
368 static void eat_string(msg, str)
369 dgram_t *msg;
370 char *str;
371 {
372     char *saved_str, *saved_msg;
373
374     /* eat leading whitespace */
375     while(isspace((int)(*msg->cur))) msg->cur++;
376
377     saved_msg = msg->cur;
378     saved_str = str;
379
380     /* eat any characters that match str */
381     while(*str && *msg->cur++ == *str++);
382
383     /* if we didn't eat all of str, we've failed */
384     if(*str) {
385         int len = strlen(saved_str);
386         char *tmp = NULL;
387         
388         tmp = alloc(len+1);
389         strncpy(tmp, saved_msg, len);
390         tmp[len] = '\0';
391         parse_errmsg = newvstralloc(parse_errmsg,
392                                     "expected \"", saved_str, "\",",
393                                     " got \"", tmp, "\"",
394                                     NULL);
395         amfree(tmp);
396         longjmp(parse_failed,1);
397     }
398 }
399
400 static int parse_integer(msg)
401 dgram_t *msg;
402 {
403     int i = 0;
404     int sign = 1;
405
406     /* eat leading whitespace */
407     while(isspace((int)(*msg->cur))) msg->cur++;
408
409     /* handle negative values */
410     if(*msg->cur == '-') {
411         sign = -1;
412         msg->cur++;
413     }
414
415     /* must have at least one digit */
416     if(*msg->cur < '0' || *msg->cur > '9') {
417         char non_digit[2];
418
419         non_digit[0] = *msg->cur;
420         non_digit[1] = '\0';
421         parse_errmsg = newvstralloc(parse_errmsg,
422                                     "expected digit, got \"", non_digit, "\"",
423                                     NULL);
424         longjmp(parse_failed,1);
425     }
426
427     while(*msg->cur >= '0' && *msg->cur <= '9') {
428         i = i * 10 + (*msg->cur - '0');
429         msg->cur++;
430     }
431     return sign * i;
432 }
433
434 static char *parse_string(msg)
435 dgram_t *msg;
436 {
437     char *str;
438
439     /* eat leading whitespace */
440     while(isspace((int)(*msg->cur))) msg->cur++;
441
442     /* mark start of string */
443     str = msg->cur;
444
445     /* stop at whitespace (including newlines) or end-of-packet */
446     while(*msg->cur && !isspace((int)(*msg->cur))) msg->cur++;
447
448     /* empty fields not allowed */
449     if(msg->cur == str) {
450         parse_errmsg = newstralloc(parse_errmsg,
451                                    "expected string, got empty field");
452         longjmp(parse_failed,1);
453     }
454
455     /* mark end of string in the packet, but don't fall off the end of it */
456     if(*msg->cur) *msg->cur++ = '\0';
457
458     return str;
459 }
460
461 static char *parse_line(msg)
462 dgram_t *msg;
463 {
464     char *str;
465
466     /* eat leading whitespace */
467     while(isspace((int)(*msg->cur))) msg->cur++;
468
469     /* mark start of string */
470     str = msg->cur;
471
472     /* stop at end of line or end-of-packet */
473     while(*msg->cur && *msg->cur != '\n') msg->cur++;
474
475     /* empty fields not allowed */
476     if(msg->cur == str) {
477         parse_errmsg = newstralloc(parse_errmsg,
478                                    "expected string, got empty field");
479         longjmp(parse_failed,1);
480     }
481
482     /* mark end of string in the packet, but don't fall off the end of it */
483     if(*msg->cur) *msg->cur++ = '\0';
484
485     return str;
486 }
487
488 void parse_pkt_header(pkt)
489 pkt_t *pkt;
490 {
491     dgram_t *msg;
492     char *typestr;
493
494     if(setjmp(parse_failed)) {
495 /*      dbprintf(("%s: leftover:\n----\n%s----\n\n", errmsg, msg->cur)); */
496         pkt->type = P_BOGUS;
497         return;
498     }
499
500     msg = &pkt->dgram;
501
502 #ifdef PROTO_DEBUG
503     dbprintf(("%s: parsing packet:\n-------\n%s-------\n\n",
504               debug_prefix_time(": protocol"),
505               msg->cur));
506 #endif
507
508     eat_string(msg, "Amanda");      pkt->version_major = parse_integer(msg);
509     eat_string(msg, ".");           pkt->version_minor = parse_integer(msg);
510     typestr = parse_string(msg);
511
512     if(strcmp(typestr, "REQ") == 0) pkt->type = P_REQ;
513     else if(strcmp(typestr, "REP") == 0) pkt->type = P_REP;
514     else if(strcmp(typestr, "PREP") == 0) pkt->type = P_PREP;
515     else if(strcmp(typestr, "ACK") == 0) pkt->type = P_ACK;
516     else if(strcmp(typestr, "NAK") == 0) pkt->type = P_NAK;
517     else pkt->type = P_BOGUS;
518
519     eat_string(msg, "HANDLE");      pkt->handle = parse_string(msg);
520     eat_string(msg, "SEQ");         pkt->sequence = parse_integer(msg);
521
522     eat_string(msg, "");
523 #define sc "SECURITY "
524     if(strncmp(msg->cur, sc, sizeof(sc)-1) == 0) {
525         /* got security tag */
526         eat_string(msg, sc);
527 #undef sc
528         pkt->security = parse_line(msg);
529     }
530     else pkt->security = NULL;
531
532     if(pkt->type == P_REQ) {
533
534 #ifdef KRB4_SECURITY
535         eat_string(msg, "");
536         pkt->cksum = kerberos_cksum(msg->cur);
537 #ifdef PROTO_DEBUG
538         dbprintf(("%s: parse_pkt/cksum %ld over \'%s\'\n\n",
539                   debug_prefix_time(": protocol"),
540                   pkt->cksum,
541                   msg->cur)); 
542 #endif
543         fflush(stdout);
544 #endif
545         eat_string(msg, "SERVICE");     pkt->service = parse_string(msg);
546     }
547
548     eat_string(msg, "");
549     pkt->body = msg->cur;
550 }
551
552 static void setup_dgram(p, msg, security, typestr)
553 proto_t *p;
554 dgram_t *msg;
555 char *security, *typestr;
556 {
557     char *linebuf = NULL;
558     char major_str[NUM_STR_SIZE];
559     char minor_str[NUM_STR_SIZE];
560     char seq_str[NUM_STR_SIZE];
561
562     ap_snprintf(major_str, sizeof(major_str), "%d", VERSION_MAJOR);
563     ap_snprintf(minor_str, sizeof(minor_str), "%d", VERSION_MINOR);
564     ap_snprintf(seq_str, sizeof(seq_str), "%d", p->curseq);
565
566     dgram_zero(msg);
567     dgram_socket(msg,proto_socket);
568     linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
569                         " ", typestr,
570                         " HANDLE ", ptr2handle(p),
571                         " SEQ ", seq_str,
572                         "\n",
573                         security ? security : "",
574                         security ? "\n" : "",
575                         NULL);
576     dgram_cat(msg, linebuf);
577     amfree(linebuf);
578 }
579
580 static void send_req(p)
581 proto_t *p;
582 {
583     dgram_t outmsg;
584
585     setup_dgram(p, &outmsg, p->security, "REQ");
586     dgram_cat(&outmsg, p->req);
587
588 #ifdef PROTO_DEBUG
589     dbprintf(("%s: send_req: len %d: packet:\n----\n%s----\n\n", 
590               debug_prefix_time(": protocol"),
591               outmsg.len,
592               outmsg.data));
593 #endif
594
595     if(dgram_send_addr(p->peer, &outmsg))
596         fprintf(stderr,"send req failed: %s\n", strerror(errno));
597 }
598
599 static void send_ack(p)
600 proto_t *p;
601 {
602     dgram_t outmsg;
603
604     setup_dgram(p, &outmsg, NULL, "ACK");
605
606 #ifdef PROTO_DEBUG
607     dbprintf(("%s: send_ack: len %d: packet:\n----\n%s----\n\n", 
608               debug_prefix_time(": protocol"),
609               outmsg.len,
610               outmsg.data));
611 #endif
612
613     if(dgram_send_addr(p->peer, &outmsg))
614         error("send ack failed: %s", strerror(errno));
615 }
616
617 static void send_ack_repl(pkt)
618 pkt_t *pkt;
619 {
620     dgram_t outmsg;
621     char *linebuf = NULL;
622     char major_str[NUM_STR_SIZE];
623     char minor_str[NUM_STR_SIZE];
624     char seq_str[NUM_STR_SIZE];
625
626     ap_snprintf(major_str, sizeof(major_str), "%d", VERSION_MAJOR);
627     ap_snprintf(minor_str, sizeof(minor_str), "%d", VERSION_MINOR);
628     ap_snprintf(seq_str, sizeof(seq_str), "%d", pkt->sequence);
629
630     dgram_zero(&outmsg);
631     dgram_socket(&outmsg,proto_socket);
632
633     linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
634                         " ACK HANDLE ", pkt->handle,
635                         " SEQ ", seq_str,
636                         "\n", NULL);
637
638     dgram_cat(&outmsg, linebuf);
639     amfree(linebuf);
640
641 #ifdef PROTO_DEBUG
642     dbprintf(("%s: send_ack_repl: len %d: packet:\n----\n%s----\n\n", 
643               debug_prefix_time(": protocol"),
644               outmsg.len,
645               outmsg.data));
646 #endif
647
648     if(dgram_send_addr(pkt->peer, &outmsg))
649         error("send ack failed: %s", strerror(errno));
650 }
651
652
653 static void state_machine(p, action, pkt)
654 proto_t *p;
655 action_t action;
656 pkt_t *pkt;
657 {
658
659 #ifdef PROTO_DEBUG
660     dbprintf(("%s: state_machine: p %X state %s action %s%s%s\n",
661               debug_prefix_time(": protocol"),
662               (int)p,
663               prnpstate(p->state),
664               prnaction(action),
665               pkt == NULL? "" : " pktype ",
666               pkt == NULL? "" : prnpktype(pkt->type)));
667 #endif
668
669     while(1) {
670         p->prevstate = p->state;
671         switch(p->state) {
672         case S_STARTUP: 
673             if(action != A_START) goto badaction;
674             p->origseq = p->curseq = proto_global_seq++;
675             p->reqtries = REQ_TRIES;
676             p->state = S_SENDREQ;
677             p->acktries = ACK_TRIES;
678             alloc_handle(p);
679             break;
680
681         case S_SENDREQ:
682             send_req(p);
683             p->curtime = CURTIME;
684             if(p->curseq == p->origseq) p->origtime = p->curtime;
685             p->timeout = time(0) + ACK_WAIT;
686             p->state = S_ACKWAIT;
687             pending_enqueue(p);
688             return;
689
690         case S_ACKWAIT:
691             if(action == A_TIMEOUT) {
692                 if(--p->acktries == 0) {
693                     p->state = S_FAILED;
694                     free_handle(p);
695                     p->continuation(p, NULL);
696                     amfree(p->req);
697                     amfree(p->security);
698                     amfree(p);
699                     return;
700                 }
701                 else {
702                     p->state = S_SENDREQ;
703                     break;
704                 }
705             }
706             else if(action != A_RCVDATA)
707                 goto badaction;
708
709             /* got the packet with the right handle, now check it */
710
711 #ifdef PROTO_DEBUG
712             dbprintf((
713          "%s: RESPTIME p %X pkt %s (t %d s %d) orig (t %d s %d) cur (t %d s %d)\n",
714                     debug_prefix_time(": protocol"),
715                     (int)p, prnpktype(pkt->type), 
716                     (int)CURTIME, relseq(pkt->sequence),
717                     (int)p->origtime, relseq(p->origseq), 
718                     (int)p->curtime, relseq(p->curseq)));
719 #endif
720
721             if(pkt->type == P_ACK) {
722                 if(pkt->sequence != p->origseq)
723                     p->reqtries--;
724                 p->state = S_REPWAIT;
725                 p->timeout = time(0) + p->repwait;
726                 pending_enqueue(p);
727                 return;
728             }
729             else if(pkt->type == P_NAK) {
730                 p->state = S_FAILED;
731                 free_handle(p);
732                 p->continuation(p, pkt);
733                 amfree(p->req);
734                 amfree(p->security);
735                 amfree(p);
736                 return;
737             }
738             else if(pkt->type == P_REP) {
739                 /* no ack, just rep */
740                 p->state = S_REPWAIT;
741                 break;
742             }
743             else if(pkt->type == P_PREP) {
744                 /* no ack, just rep */
745                 p->state = S_REPWAIT;
746                 break;
747             }
748             /* else unexpected packet, put back on queue */
749             pending_enqueue(p);
750             return;
751
752         case S_REPWAIT:
753             if(action == A_TIMEOUT) {
754                 if(p->reqtries == 0 || 
755                    (CURTIME - p->origtime > DROP_DEAD_TIME)) {
756                     p->state = S_FAILED;
757                     free_handle(p);
758                     p->continuation(p, NULL);
759                     amfree(p->req);
760                     amfree(p->security);
761                     amfree(p);
762                     return;
763                 }
764                 else {
765                     p->reqtries--;
766                     p->state = S_SENDREQ;
767                     p->acktries = ACK_TRIES;
768                     break;
769                 }
770             }
771             else if(action != A_RCVDATA)
772                 goto badaction;
773             /* got the packet with the right handle, now check it */
774             if(pkt->type != P_REP && pkt->type != P_PREP) {
775                 pending_enqueue(p);
776                 return;
777             }
778             if(pkt->type == P_REP) {
779                 send_ack(p);
780                 p->state = S_SUCCEEDED;
781                 free_handle(p);
782                 p->continuation(p, pkt);
783                 amfree(p->req);
784                 amfree(p->security);
785                 amfree(p);
786                 return;
787             }
788             else if(pkt->type == P_PREP) {
789                 p->state = S_REPWAIT;
790                 p->continuation(p, pkt);
791                 pending_enqueue(p);
792                 return;
793             }
794
795         default:
796         badaction:
797             error("protocol error: no handler for state %s action %s\n",
798                   prnpstate(p->state), prnaction(action));
799         }
800     }
801 }
802
803 static void add_bsd_security(p)
804 proto_t *p;
805 {
806     p->security = get_bsd_security();
807 }
808
809 int make_request(hostname, port, req, datap, repwait, continuation)
810 char *hostname;
811 int port;
812 char *req;
813 void *datap;
814 time_t repwait;
815 void (*continuation) P((proto_t *p, pkt_t *pkt));
816 {
817     proto_t *p;
818     struct hostent *hp;
819
820
821     p = alloc(sizeof(proto_t));
822     p->state = S_STARTUP;
823     p->prevstate = S_STARTUP;
824     p->continuation = continuation;
825     p->req = req;
826     p->repwait = repwait;
827     p->datap = datap;
828
829 #ifdef PROTO_DEBUG
830     dbprintf(("%s: make_request: host %s -> p %X\n", 
831               debug_prefix_time(": protocol"),
832               hostname,
833               (int)p));
834 #endif
835
836     if((hp = gethostbyname(hostname)) == 0) return -1;
837     memcpy(&p->peer.sin_addr, hp->h_addr, hp->h_length);
838     p->peer.sin_family = AF_INET;
839     p->peer.sin_port = htons(port);
840
841     add_bsd_security(p);
842
843     state_machine(p, A_START, NULL);
844     return 0;
845 }
846
847 #ifdef KRB4_SECURITY
848
849 static int add_krb_security P((proto_t *p, char *host_inst, char *realm));
850
851 static int add_krb_security(p, host_inst, realm)
852 proto_t *p;
853 char *host_inst, *realm;
854 {
855     p->security = get_krb_security(p->req, host_inst, realm, &p->auth_cksum);
856
857 #ifdef PROTO_DEBUG
858     dbprintf(("%s: add_krb_security() cksum: %lu: \'%s\'\n",
859               debug_prefix_time(": protocol"),
860               p->auth_cksum,
861               p->req));
862 #endif
863
864     return p->security == NULL;
865 }
866
867 int make_krb_request(hostname, port, req, datap, repwait, continuation)
868 char *hostname;
869 int port;
870 char *req;
871 void *datap;
872 time_t repwait;
873 void (*continuation) P((proto_t *p, pkt_t *pkt));
874 {
875     proto_t *p;
876     struct hostent *hp;
877     char inst[256], realm[256];
878     int rc;
879
880     p = alloc(sizeof(proto_t));
881     p->state = S_STARTUP;
882     p->prevstate = S_STARTUP;
883     p->continuation = continuation;
884     p->req = req;
885     p->repwait = repwait;
886     p->datap = datap;
887
888 #ifdef PROTO_DEBUG
889     dbprintf(("%s: make_krb_request: host %s -> p %X\n", 
890               debug_prefix_time(": protocol"),
891               hostname,
892               req, (int)p));
893 #endif
894
895     if((hp = host2krbname(hostname, inst, realm)) == 0)
896         return -1;
897     memcpy(&p->peer.sin_addr, hp->h_addr, hp->h_length);
898     p->peer.sin_family = AF_INET;
899     p->peer.sin_port = htons(port);
900
901     if((rc = add_krb_security(p, inst, realm)))
902         return rc;
903
904     state_machine(p, A_START, NULL);
905     return 0;
906 }
907
908 #endif
909
910 static int select_til(waketime) 
911 time_t waketime;
912 {
913     fd_set ready;
914     struct timeval to;
915     time_t waittime;
916     int rc;
917
918     waittime = waketime - time(0);
919     if(waittime < 0) waittime = 0;      /* just poll */
920
921     FD_ZERO(&ready);
922     FD_SET(proto_socket, &ready);
923     to.tv_sec = waittime;
924     to.tv_usec = 0;
925
926     rc = select(proto_socket+1, (SELECT_ARG_TYPE *)&ready, NULL, NULL, &to);
927     if(rc == -1) {
928         error("protocol socket select: %s", strerror(errno));
929     }
930     return rc;
931 }
932
933 static int packet_arrived() 
934 {
935     return select_til(0);
936 }
937
938 static void handle_incoming_packet() 
939 {
940     pkt_t inpkt;
941     proto_t *p;
942
943     dgram_zero(&inpkt.dgram);
944     dgram_socket(&inpkt.dgram, proto_socket);
945     if(dgram_recv(&inpkt.dgram, 0, &inpkt.peer) == -1) {
946 #ifdef ECONNREFUSED
947         if(errno == ECONNREFUSED)
948             return;
949 #endif
950 #ifdef EAGAIN
951         if(errno == EAGAIN)
952             return;
953 #endif
954         fprintf(stderr,"protocol packet receive: %s\n", strerror(errno));
955     }
956
957 #ifdef PROTO_DEBUG
958     dbprintf(("%s: got packet:\n----\n%s----\n\n",
959               debug_prefix_time(": protocol"),
960               inpkt.dgram.data));
961 #endif
962
963     parse_pkt_header(&inpkt);
964     if(inpkt.type == P_BOGUS)
965         return;
966     if((p = handle2ptr(inpkt.handle)) == NULL) {
967         /* ack succeeded reps */
968         if(inpkt.type == P_REP)
969             send_ack_repl(&inpkt);
970         return;
971     }
972
973 #ifdef PROTO_DEBUG
974     dbprintf(("%s: handle %s p %X got packet type %s\n",
975               debug_prefix_time(": protocol"),
976               inpkt.handle,
977               (int)p,
978               prnpktype(inpkt.type)));
979 #endif
980
981     pending_remove(p);
982     state_machine(p, A_RCVDATA, &inpkt);
983 }
984
985
986
987 void check_protocol()
988 {
989     time_t curtime;
990     proto_t *p;
991
992     while(packet_arrived())
993         handle_incoming_packet();
994
995     curtime = time(0);
996     while(pending_head && curtime >= pending_head->timeout) {
997         p = pending_dequeue();
998         state_machine(p, A_TIMEOUT, NULL);
999     }
1000 }
1001
1002
1003 void run_protocol()
1004 {
1005     time_t wakeup_time;
1006     proto_t *p;
1007
1008     while(pending_head) {
1009         wakeup_time = pending_head->timeout;
1010
1011 #ifdef PROTO_DEBUG
1012         dbprintf(("%s: run_protocol: waiting %d secs for %d pending reqs\n",
1013                   debug_prefix_time(": protocol"),
1014                   (int)(wakeup_time - time(0)),
1015                   pending_qlength));
1016 #endif
1017
1018         if(select_til(wakeup_time))
1019             handle_incoming_packet();
1020         else {
1021             p = pending_dequeue();
1022             state_machine(p, A_TIMEOUT, NULL);
1023         }
1024     }
1025 }