2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: protocol.c,v 1.27.2.1.6.2.2.2 2004/04/14 13:24:17 martinea Exp $
29 * implements amanda protocol
35 # include "krb4-security.h"
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) */
42 #define DROP_DEAD_TIME (60*60) /* If no reply in an hour, just forget it */
44 #define MAX_HANDLES 4096
45 #define OFS_DIGITS 3 /* log2(MAX_HANDLES)/4 */
47 proto_t *pending_head = NULL;
48 proto_t *pending_tail = NULL;
49 int pending_qlength = 0;
51 int proto_socket = -1;
52 int proto_global_seq = 0;
53 #define relseq(s) (s-proto_global_seq)
55 proto_t **proto_handle_table;
56 proto_t **proto_next_handle;
59 time_t proto_init_time;
60 #define CURTIME (time(0)-proto_init_time)
63 static char *prnpstate P((pstate_t s));
64 static char *prnaction P((action_t s));
66 static char *prnpktype P((pktype_t s));
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));
97 static char *prnpstate(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";
111 ap_snprintf(str, sizeof(str), "<bad state %d>", s);
116 static char *prnaction(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";
127 ap_snprintf(str, sizeof(str), "<bad action %d>", s);
134 static char *prnpktype(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";
147 ap_snprintf(str, sizeof(str), "<bad pktype %d>", s);
155 void proto_init(socket, startseq, handles)
156 int socket, startseq, handles;
161 dbprintf(("%s: proto_init(socket %d, startseq %d, handles %d)\n",
162 debug_prefix_time(": protocol"),
167 if(socket < 0 || socket >= FD_SETSIZE) {
168 error("proto_init: socket %d out of range (0 .. %d)\n",
169 socket, FD_SETSIZE-1);
171 proto_socket = socket;
172 proto_global_seq = startseq;
173 proto_handles = handles;
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);
184 static void pending_enqueue(newp)
189 /* common case shortcut: check if adding to end of list */
191 if(pending_tail && pending_tail->timeout <= newp->timeout)
194 /* scan list for insert-sort */
196 while(curp && curp->timeout <= newp->timeout)
202 newp->prev = curp->prev;
206 newp->prev = pending_tail;
210 if(newp->prev) newp->prev->next = newp;
211 else pending_head = newp;
216 static proto_t *pending_dequeue()
222 pending_head = p->next;
225 pending_head->prev = NULL;
234 static void pending_remove(p)
237 if(p->next) p->next->prev = p->prev;
238 else pending_tail = p->prev;
240 if(p->prev) p->prev->next = p->next;
241 else pending_head = p->next;
243 p->prev = p->next = NULL;
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" */
253 unsigned char c[PTR_CHARS];
257 static void alloc_handle(p)
263 hp = proto_next_handle;
264 for(i = 0; i < proto_handles; i++) {
265 if(*hp == NULL) break;
267 if(hp >= proto_handle_table + proto_handles)
268 hp = proto_handle_table;
270 if(i == proto_handles)
271 error("protocol out of handles");
272 p->handleofs = hp-proto_handle_table;
276 static void free_handle(p)
279 if(proto_handle_table[p->handleofs] == p)
280 proto_handle_table[p->handleofs] = NULL;
284 static void hex(str, digits, v)
289 str = str + digits - 1;
292 *str-- = "0123456789ABCDEF"[v % 16];
297 static int unhex(str, digits)
303 while(*str && digits--) {
304 d = *str >= 'A'? *str - 'A' + 10 : *str - '0';
312 static proto_t *handle2ptr(str)
317 if(strlen(str) != HANDLE_CHARS)
320 ofs = unhex(str, OFS_DIGITS);
322 if(ofs < 0 || ofs >= proto_handles)
328 for(i=0; i < PTR_CHARS; i++) {
329 hu.c[i] = unhex(str, CHAR_DIGITS);
333 if(proto_handle_table[ofs] != hu.p)
340 static char *ptr2handle(p)
345 static char hstr[HANDLE_CHARS+1];
347 assert(p->handleofs != -1 && proto_handle_table[p->handleofs] == p);
351 hex(hstr, OFS_DIGITS, p->handleofs);
352 s = &hstr[OFS_DIGITS];
355 for(i=0;i<PTR_CHARS;i++) {
356 hex(s, CHAR_DIGITS, hu.c[i]);
365 jmp_buf parse_failed;
366 char *parse_errmsg = NULL;
368 static void eat_string(msg, str)
372 char *saved_str, *saved_msg;
374 /* eat leading whitespace */
375 while(isspace((int)(*msg->cur))) msg->cur++;
377 saved_msg = msg->cur;
380 /* eat any characters that match str */
381 while(*str && *msg->cur++ == *str++);
383 /* if we didn't eat all of str, we've failed */
385 int len = strlen(saved_str);
389 strncpy(tmp, saved_msg, len);
391 parse_errmsg = newvstralloc(parse_errmsg,
392 "expected \"", saved_str, "\",",
393 " got \"", tmp, "\"",
396 longjmp(parse_failed,1);
400 static int parse_integer(msg)
406 /* eat leading whitespace */
407 while(isspace((int)(*msg->cur))) msg->cur++;
409 /* handle negative values */
410 if(*msg->cur == '-') {
415 /* must have at least one digit */
416 if(*msg->cur < '0' || *msg->cur > '9') {
419 non_digit[0] = *msg->cur;
421 parse_errmsg = newvstralloc(parse_errmsg,
422 "expected digit, got \"", non_digit, "\"",
424 longjmp(parse_failed,1);
427 while(*msg->cur >= '0' && *msg->cur <= '9') {
428 i = i * 10 + (*msg->cur - '0');
434 static char *parse_string(msg)
439 /* eat leading whitespace */
440 while(isspace((int)(*msg->cur))) msg->cur++;
442 /* mark start of string */
445 /* stop at whitespace (including newlines) or end-of-packet */
446 while(*msg->cur && !isspace((int)(*msg->cur))) msg->cur++;
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);
455 /* mark end of string in the packet, but don't fall off the end of it */
456 if(*msg->cur) *msg->cur++ = '\0';
461 static char *parse_line(msg)
466 /* eat leading whitespace */
467 while(isspace((int)(*msg->cur))) msg->cur++;
469 /* mark start of string */
472 /* stop at end of line or end-of-packet */
473 while(*msg->cur && *msg->cur != '\n') msg->cur++;
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);
482 /* mark end of string in the packet, but don't fall off the end of it */
483 if(*msg->cur) *msg->cur++ = '\0';
488 void parse_pkt_header(pkt)
494 if(setjmp(parse_failed)) {
495 /* dbprintf(("%s: leftover:\n----\n%s----\n\n", errmsg, msg->cur)); */
503 dbprintf(("%s: parsing packet:\n-------\n%s-------\n\n",
504 debug_prefix_time(": protocol"),
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);
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;
519 eat_string(msg, "HANDLE"); pkt->handle = parse_string(msg);
520 eat_string(msg, "SEQ"); pkt->sequence = parse_integer(msg);
523 #define sc "SECURITY "
524 if(strncmp(msg->cur, sc, sizeof(sc)-1) == 0) {
525 /* got security tag */
528 pkt->security = parse_line(msg);
530 else pkt->security = NULL;
532 if(pkt->type == P_REQ) {
536 pkt->cksum = kerberos_cksum(msg->cur);
538 dbprintf(("%s: parse_pkt/cksum %ld over \'%s\'\n\n",
539 debug_prefix_time(": protocol"),
545 eat_string(msg, "SERVICE"); pkt->service = parse_string(msg);
549 pkt->body = msg->cur;
552 static void setup_dgram(p, msg, security, typestr)
555 char *security, *typestr;
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];
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);
567 dgram_socket(msg,proto_socket);
568 linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
570 " HANDLE ", ptr2handle(p),
573 security ? security : "",
574 security ? "\n" : "",
576 dgram_cat(msg, linebuf);
580 static void send_req(p)
585 setup_dgram(p, &outmsg, p->security, "REQ");
586 dgram_cat(&outmsg, p->req);
589 dbprintf(("%s: send_req: len %d: packet:\n----\n%s----\n\n",
590 debug_prefix_time(": protocol"),
595 if(dgram_send_addr(p->peer, &outmsg))
596 fprintf(stderr,"send req failed: %s\n", strerror(errno));
599 static void send_ack(p)
604 setup_dgram(p, &outmsg, NULL, "ACK");
607 dbprintf(("%s: send_ack: len %d: packet:\n----\n%s----\n\n",
608 debug_prefix_time(": protocol"),
613 if(dgram_send_addr(p->peer, &outmsg))
614 error("send ack failed: %s", strerror(errno));
617 static void send_ack_repl(pkt)
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];
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);
631 dgram_socket(&outmsg,proto_socket);
633 linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
634 " ACK HANDLE ", pkt->handle,
638 dgram_cat(&outmsg, linebuf);
642 dbprintf(("%s: send_ack_repl: len %d: packet:\n----\n%s----\n\n",
643 debug_prefix_time(": protocol"),
648 if(dgram_send_addr(pkt->peer, &outmsg))
649 error("send ack failed: %s", strerror(errno));
653 static void state_machine(p, action, pkt)
660 dbprintf(("%s: state_machine: p %X state %s action %s%s%s\n",
661 debug_prefix_time(": protocol"),
665 pkt == NULL? "" : " pktype ",
666 pkt == NULL? "" : prnpktype(pkt->type)));
670 p->prevstate = p->state;
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;
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;
691 if(action == A_TIMEOUT) {
692 if(--p->acktries == 0) {
695 p->continuation(p, NULL);
702 p->state = S_SENDREQ;
706 else if(action != A_RCVDATA)
709 /* got the packet with the right handle, now check it */
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)));
721 if(pkt->type == P_ACK) {
722 if(pkt->sequence != p->origseq)
724 p->state = S_REPWAIT;
725 p->timeout = time(0) + p->repwait;
729 else if(pkt->type == P_NAK) {
732 p->continuation(p, pkt);
738 else if(pkt->type == P_REP) {
739 /* no ack, just rep */
740 p->state = S_REPWAIT;
743 else if(pkt->type == P_PREP) {
744 /* no ack, just rep */
745 p->state = S_REPWAIT;
748 /* else unexpected packet, put back on queue */
753 if(action == A_TIMEOUT) {
754 if(p->reqtries == 0 ||
755 (CURTIME - p->origtime > DROP_DEAD_TIME)) {
758 p->continuation(p, NULL);
766 p->state = S_SENDREQ;
767 p->acktries = ACK_TRIES;
771 else if(action != A_RCVDATA)
773 /* got the packet with the right handle, now check it */
774 if(pkt->type != P_REP && pkt->type != P_PREP) {
778 if(pkt->type == P_REP) {
780 p->state = S_SUCCEEDED;
782 p->continuation(p, pkt);
788 else if(pkt->type == P_PREP) {
789 p->state = S_REPWAIT;
790 p->continuation(p, pkt);
797 error("protocol error: no handler for state %s action %s\n",
798 prnpstate(p->state), prnaction(action));
803 static void add_bsd_security(p)
806 p->security = get_bsd_security();
809 int make_request(hostname, port, req, datap, repwait, continuation)
815 void (*continuation) P((proto_t *p, pkt_t *pkt));
821 p = alloc(sizeof(proto_t));
822 p->state = S_STARTUP;
823 p->prevstate = S_STARTUP;
824 p->continuation = continuation;
826 p->repwait = repwait;
830 dbprintf(("%s: make_request: host %s -> p %X\n",
831 debug_prefix_time(": protocol"),
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);
843 state_machine(p, A_START, NULL);
849 static int add_krb_security P((proto_t *p, char *host_inst, char *realm));
851 static int add_krb_security(p, host_inst, realm)
853 char *host_inst, *realm;
855 p->security = get_krb_security(p->req, host_inst, realm, &p->auth_cksum);
858 dbprintf(("%s: add_krb_security() cksum: %lu: \'%s\'\n",
859 debug_prefix_time(": protocol"),
864 return p->security == NULL;
867 int make_krb_request(hostname, port, req, datap, repwait, continuation)
873 void (*continuation) P((proto_t *p, pkt_t *pkt));
877 char inst[256], realm[256];
880 p = alloc(sizeof(proto_t));
881 p->state = S_STARTUP;
882 p->prevstate = S_STARTUP;
883 p->continuation = continuation;
885 p->repwait = repwait;
889 dbprintf(("%s: make_krb_request: host %s -> p %X\n",
890 debug_prefix_time(": protocol"),
895 if((hp = host2krbname(hostname, inst, realm)) == 0)
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);
901 if((rc = add_krb_security(p, inst, realm)))
904 state_machine(p, A_START, NULL);
910 static int select_til(waketime)
918 waittime = waketime - time(0);
919 if(waittime < 0) waittime = 0; /* just poll */
922 FD_SET(proto_socket, &ready);
923 to.tv_sec = waittime;
926 rc = select(proto_socket+1, (SELECT_ARG_TYPE *)&ready, NULL, NULL, &to);
928 error("protocol socket select: %s", strerror(errno));
933 static int packet_arrived()
935 return select_til(0);
938 static void handle_incoming_packet()
943 dgram_zero(&inpkt.dgram);
944 dgram_socket(&inpkt.dgram, proto_socket);
945 if(dgram_recv(&inpkt.dgram, 0, &inpkt.peer) == -1) {
947 if(errno == ECONNREFUSED)
954 fprintf(stderr,"protocol packet receive: %s\n", strerror(errno));
958 dbprintf(("%s: got packet:\n----\n%s----\n\n",
959 debug_prefix_time(": protocol"),
963 parse_pkt_header(&inpkt);
964 if(inpkt.type == P_BOGUS)
966 if((p = handle2ptr(inpkt.handle)) == NULL) {
967 /* ack succeeded reps */
968 if(inpkt.type == P_REP)
969 send_ack_repl(&inpkt);
974 dbprintf(("%s: handle %s p %X got packet type %s\n",
975 debug_prefix_time(": protocol"),
978 prnpktype(inpkt.type)));
982 state_machine(p, A_RCVDATA, &inpkt);
987 void check_protocol()
992 while(packet_arrived())
993 handle_incoming_packet();
996 while(pending_head && curtime >= pending_head->timeout) {
997 p = pending_dequeue();
998 state_machine(p, A_TIMEOUT, NULL);
1008 while(pending_head) {
1009 wakeup_time = pending_head->timeout;
1012 dbprintf(("%s: run_protocol: waiting %d secs for %d pending reqs\n",
1013 debug_prefix_time(": protocol"),
1014 (int)(wakeup_time - time(0)),
1018 if(select_til(wakeup_time))
1019 handle_incoming_packet();
1021 p = pending_dequeue();
1022 state_machine(p, A_TIMEOUT, NULL);