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.3 2004/04/14 13:24:35 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_ACK: return "P_ACK";
144 case P_NAK: return "P_NAK";
146 ap_snprintf(str, sizeof(str), "<bad pktype %d>", s);
154 void proto_init(socket, startseq, handles)
155 int socket, startseq, handles;
160 dbprintf(("%s: proto_init(socket %d, startseq %d, handles %d)\n",
161 debug_prefix_time(": protocol"),
166 if(socket < 0 || socket >= FD_SETSIZE) {
167 error("proto_init: socket %d out of range (0 .. %d)\n",
168 socket, FD_SETSIZE-1);
170 proto_socket = socket;
171 proto_global_seq = startseq;
172 proto_handles = handles;
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);
183 static void pending_enqueue(newp)
188 /* common case shortcut: check if adding to end of list */
190 if(pending_tail && pending_tail->timeout <= newp->timeout)
193 /* scan list for insert-sort */
195 while(curp && curp->timeout <= newp->timeout)
201 newp->prev = curp->prev;
205 newp->prev = pending_tail;
209 if(newp->prev) newp->prev->next = newp;
210 else pending_head = newp;
215 static proto_t *pending_dequeue()
221 pending_head = p->next;
224 pending_head->prev = NULL;
233 static void pending_remove(p)
236 if(p->next) p->next->prev = p->prev;
237 else pending_tail = p->prev;
239 if(p->prev) p->prev->next = p->next;
240 else pending_head = p->next;
242 p->prev = p->next = NULL;
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" */
252 unsigned char c[PTR_CHARS];
256 static void alloc_handle(p)
262 hp = proto_next_handle;
263 for(i = 0; i < proto_handles; i++) {
264 if(*hp == NULL) break;
266 if(hp >= proto_handle_table + proto_handles)
267 hp = proto_handle_table;
269 if(i == proto_handles)
270 error("protocol out of handles");
271 p->handleofs = hp-proto_handle_table;
275 static void free_handle(p)
278 if(proto_handle_table[p->handleofs] == p)
279 proto_handle_table[p->handleofs] = NULL;
283 static void hex(str, digits, v)
288 str = str + digits - 1;
291 *str-- = "0123456789ABCDEF"[v % 16];
296 static int unhex(str, digits)
302 while(*str && digits--) {
303 d = *str >= 'A'? *str - 'A' + 10 : *str - '0';
311 static proto_t *handle2ptr(str)
316 if(strlen(str) != HANDLE_CHARS)
319 ofs = unhex(str, OFS_DIGITS);
321 if(ofs < 0 || ofs >= proto_handles)
327 for(i=0; i < PTR_CHARS; i++) {
328 hu.c[i] = unhex(str, CHAR_DIGITS);
332 if(proto_handle_table[ofs] != hu.p)
339 static char *ptr2handle(p)
344 static char hstr[HANDLE_CHARS+1];
346 assert(p->handleofs != -1 && proto_handle_table[p->handleofs] == p);
350 hex(hstr, OFS_DIGITS, p->handleofs);
351 s = &hstr[OFS_DIGITS];
354 for(i=0;i<PTR_CHARS;i++) {
355 hex(s, CHAR_DIGITS, hu.c[i]);
364 jmp_buf parse_failed;
365 char *parse_errmsg = NULL;
367 static void eat_string(msg, str)
371 char *saved_str, *saved_msg;
373 /* eat leading whitespace */
374 while(isspace((int)(*msg->cur))) msg->cur++;
376 saved_msg = msg->cur;
379 /* eat any characters that match str */
380 while(*str && *msg->cur++ == *str++);
382 /* if we didn't eat all of str, we've failed */
384 int len = strlen(saved_str);
388 strncpy(tmp, saved_msg, len);
390 parse_errmsg = newvstralloc(parse_errmsg,
391 "expected \"", saved_str, "\",",
392 " got \"", tmp, "\"",
395 longjmp(parse_failed,1);
399 static int parse_integer(msg)
405 /* eat leading whitespace */
406 while(isspace((int)(*msg->cur))) msg->cur++;
408 /* handle negative values */
409 if(*msg->cur == '-') {
414 /* must have at least one digit */
415 if(*msg->cur < '0' || *msg->cur > '9') {
418 non_digit[0] = *msg->cur;
420 parse_errmsg = newvstralloc(parse_errmsg,
421 "expected digit, got \"", non_digit, "\"",
423 longjmp(parse_failed,1);
426 while(*msg->cur >= '0' && *msg->cur <= '9') {
427 i = i * 10 + (*msg->cur - '0');
433 static char *parse_string(msg)
438 /* eat leading whitespace */
439 while(isspace((int)(*msg->cur))) msg->cur++;
441 /* mark start of string */
444 /* stop at whitespace (including newlines) or end-of-packet */
445 while(*msg->cur && !isspace((int)(*msg->cur))) msg->cur++;
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);
454 /* mark end of string in the packet, but don't fall off the end of it */
455 if(*msg->cur) *msg->cur++ = '\0';
460 static char *parse_line(msg)
465 /* eat leading whitespace */
466 while(isspace((int)(*msg->cur))) msg->cur++;
468 /* mark start of string */
471 /* stop at end of line or end-of-packet */
472 while(*msg->cur && *msg->cur != '\n') msg->cur++;
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);
481 /* mark end of string in the packet, but don't fall off the end of it */
482 if(*msg->cur) *msg->cur++ = '\0';
487 void parse_pkt_header(pkt)
493 if(setjmp(parse_failed)) {
494 /* dbprintf(("%s: leftover:\n----\n%s----\n\n", errmsg, msg->cur)); */
502 dbprintf(("%s: parsing packet:\n-------\n%s-------\n\n",
503 debug_prefix_time(": protocol"),
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);
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;
517 eat_string(msg, "HANDLE"); pkt->handle = parse_string(msg);
518 eat_string(msg, "SEQ"); pkt->sequence = parse_integer(msg);
521 #define sc "SECURITY "
522 if(strncmp(msg->cur, sc, sizeof(sc)-1) == 0) {
523 /* got security tag */
526 pkt->security = parse_line(msg);
528 else pkt->security = NULL;
530 if(pkt->type == P_REQ) {
534 pkt->cksum = kerberos_cksum(msg->cur);
536 dbprintf(("%s: parse_pkt/cksum %ld over \'%s\'\n\n",
537 debug_prefix_time(": protocol"),
543 eat_string(msg, "SERVICE"); pkt->service = parse_string(msg);
547 pkt->body = msg->cur;
550 static void setup_dgram(p, msg, security, typestr)
553 char *security, *typestr;
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];
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);
565 dgram_socket(msg,proto_socket);
566 linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
568 " HANDLE ", ptr2handle(p),
571 security ? security : "",
572 security ? "\n" : "",
574 dgram_cat(msg, linebuf);
578 static void send_req(p)
583 setup_dgram(p, &outmsg, p->security, "REQ");
584 dgram_cat(&outmsg, p->req);
587 dbprintf(("%s: send_req: len %d: packet:\n----\n%s----\n\n",
588 debug_prefix_time(": protocol"),
593 if(dgram_send_addr(p->peer, &outmsg))
594 fprintf(stderr,"send req failed: %s\n", strerror(errno));
597 static void send_ack(p)
602 setup_dgram(p, &outmsg, NULL, "ACK");
605 dbprintf(("%s: send_ack: len %d: packet:\n----\n%s----\n\n",
606 debug_prefix_time(": protocol"),
611 if(dgram_send_addr(p->peer, &outmsg))
612 error("send ack failed: %s", strerror(errno));
615 static void send_ack_repl(pkt)
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];
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);
629 dgram_socket(&outmsg,proto_socket);
631 linebuf = vstralloc("Amanda ", major_str, ".", minor_str,
632 " ACK HANDLE ", pkt->handle,
636 dgram_cat(&outmsg, linebuf);
640 dbprintf(("%s: send_ack_repl: len %d: packet:\n----\n%s----\n\n",
641 debug_prefix_time(": protocol"),
646 if(dgram_send_addr(pkt->peer, &outmsg))
647 error("send ack failed: %s", strerror(errno));
651 static void state_machine(p, action, pkt)
658 dbprintf(("%s: state_machine: p %X state %s action %s%s%s\n",
659 debug_prefix_time(": protocol"),
663 pkt == NULL? "" : " pktype ",
664 pkt == NULL? "" : prnpktype(pkt->type)));
668 p->prevstate = p->state;
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;
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;
689 if(action == A_TIMEOUT) {
690 if(--p->acktries == 0) {
693 p->continuation(p, NULL);
700 p->state = S_SENDREQ;
704 else if(action != A_RCVDATA)
707 /* got the packet with the right handle, now check it */
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)));
719 if(pkt->type == P_ACK) {
720 if(pkt->sequence != p->origseq)
722 p->state = S_REPWAIT;
723 p->timeout = time(0) + p->repwait;
727 else if(pkt->type == P_NAK) {
730 p->continuation(p, pkt);
736 else if(pkt->type == P_REP) {
737 /* no ack, just rep */
738 p->state = S_REPWAIT;
741 /* else unexpected packet, put back on queue */
746 if(action == A_TIMEOUT) {
747 if(p->reqtries == 0 ||
748 (CURTIME - p->origtime > DROP_DEAD_TIME)) {
751 p->continuation(p, NULL);
759 p->state = S_SENDREQ;
760 p->acktries = ACK_TRIES;
764 else if(action != A_RCVDATA)
766 /* got the packet with the right handle, now check it */
767 if(pkt->type != P_REP) {
772 p->state = S_SUCCEEDED;
774 p->continuation(p, pkt);
782 error("protocol error: no handler for state %s action %s\n",
783 prnpstate(p->state), prnaction(action));
788 static void add_bsd_security(p)
791 p->security = get_bsd_security();
794 int make_request(hostname, port, req, datap, repwait, continuation)
800 void (*continuation) P((proto_t *p, pkt_t *pkt));
806 p = alloc(sizeof(proto_t));
807 p->state = S_STARTUP;
808 p->prevstate = S_STARTUP;
809 p->continuation = continuation;
811 p->repwait = repwait;
815 dbprintf(("%s: make_request: host %s -> p %X\n",
816 debug_prefix_time(": protocol"),
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);
828 state_machine(p, A_START, NULL);
834 static int add_krb_security P((proto_t *p, char *host_inst, char *realm));
836 static int add_krb_security(p, host_inst, realm)
838 char *host_inst, *realm;
840 p->security = get_krb_security(p->req, host_inst, realm, &p->auth_cksum);
843 dbprintf(("%s: add_krb_security() cksum: %lu: \'%s\'\n",
844 debug_prefix_time(": protocol"),
849 return p->security == NULL;
852 int make_krb_request(hostname, port, req, datap, repwait, continuation)
858 void (*continuation) P((proto_t *p, pkt_t *pkt));
862 char inst[256], realm[256];
865 p = alloc(sizeof(proto_t));
866 p->state = S_STARTUP;
867 p->prevstate = S_STARTUP;
868 p->continuation = continuation;
870 p->repwait = repwait;
874 dbprintf(("%s: make_krb_request: host %s -> p %X\n",
875 debug_prefix_time(": protocol"),
880 if((hp = host2krbname(hostname, inst, realm)) == 0)
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);
886 if((rc = add_krb_security(p, inst, realm)))
889 state_machine(p, A_START, NULL);
895 static int select_til(waketime)
903 waittime = waketime - time(0);
904 if(waittime < 0) waittime = 0; /* just poll */
907 FD_SET(proto_socket, &ready);
908 to.tv_sec = waittime;
911 rc = select(proto_socket+1, (SELECT_ARG_TYPE *)&ready, NULL, NULL, &to);
913 error("protocol socket select: %s", strerror(errno));
918 static int packet_arrived()
920 return select_til(0);
923 static void handle_incoming_packet()
928 dgram_zero(&inpkt.dgram);
929 dgram_socket(&inpkt.dgram, proto_socket);
930 if(dgram_recv(&inpkt.dgram, 0, &inpkt.peer) == -1) {
932 if(errno == ECONNREFUSED)
940 fprintf(stderr,"protocol packet receive: %s\n", strerror(errno));
944 dbprintf(("%s: got packet:\n----\n%s----\n\n",
945 debug_prefix_time(": protocol"),
949 parse_pkt_header(&inpkt);
950 if(inpkt.type == P_BOGUS)
952 if((p = handle2ptr(inpkt.handle)) == NULL) {
953 /* ack succeeded reps */
954 if(inpkt.type == P_REP)
955 send_ack_repl(&inpkt);
960 dbprintf(("%s: handle %s p %X got packet type %s\n",
961 debug_prefix_time(": protocol"),
964 prnpktype(inpkt.type)));
968 state_machine(p, A_RCVDATA, &inpkt);
973 void check_protocol()
978 while(packet_arrived())
979 handle_incoming_packet();
982 while(pending_head && curtime >= pending_head->timeout) {
983 p = pending_dequeue();
984 state_machine(p, A_TIMEOUT, NULL);
994 while(pending_head) {
995 wakeup_time = pending_head->timeout;
998 dbprintf(("%s: run_protocol: waiting %d secs for %d pending reqs\n",
999 debug_prefix_time(": protocol"),
1000 (int)(wakeup_time - time(0)),
1004 if(select_til(wakeup_time))
1005 handle_incoming_packet();
1007 p = pending_dequeue();
1008 state_machine(p, A_TIMEOUT, NULL);