home *** CD-ROM | disk | FTP | other *** search
- /*
- * ftp.c: the 'file transfer' protocols.
- * Sliding-window protocol with selective retransmit.
- */
- #include "ni.h"
-
- #include "ftp.h"
-
- extern char *sys_errlist[];
- int debug;
-
- #define NULL 0
- #define slotno(num) ((num) % WINDOW)
-
- struct request buffer[WINDOW];
- struct request *packet[WINDOW];
- int retry = 0;
- sequence_t lower, upper, num;
-
- wakeup(){
- signal(SIGALRM, wakeup);
- }
- /*
- * ftpout: send the file to the indicated host.
- */
- ftpout(f, addr)
- char *addr;
- {
- struct request ackreq, *ack = &ackreq;
- register struct request *r;
- register int n;
- int i, eof = 0;
- lower = upper = num = 0;
- retry = 0;
- for(i=0;i<WINDOW;i++) packet[i] = NULL;
- wakeup();
- while(1) {
- /*
- * If the window is not sent, send another packet.
- * If last packet was null, then all data has been sent.
- */
- while(!fullwindow(lower, upper) && !eof) {
- num = upper++;
- r = &buffer[slotno(num)];
- packet[slotno(num)] = r;
- r->r_sequence = num;
- n = read(f, r->r_data, sizeof r->r_data);
- /*
- * read error causes premature EOF
- */
- if(n == -1) n = 0;
- if(debug)
- printf("Sending packet %d\n", r->r_sequence);
- /*
- * Reset the counter for ack timeouts, then send out the block
- */
- retry = 0;
- send(r, n, DATA, addr);
- if(n == 0) eof = 1;
- }
- /*
- * We have to wait for an ACK from the other end ...
- * If it's an ACK, then the sequence field determines which packet is OK
- * If it's a NACK, then the data is a list of packets which
- * must have got lost somewhere. so we re-transmit them.
- */
- if(debug) printf("Window full - waiting for ack\n");
- alarm(TRANSIT);
- recv(ack);
- alarm(0);
- switch(ack->r_type) {
- /*
- * ACK - mark the packet as sent (delete pointer).
- * Move lower bound of window up as far as possible.
- */
- case ACCEPT:
- if(debug)
- printf("Got ack on packet %d\n", ack->r_sequence);
- release(ack->r_sequence);
- break;
- case REJECT:
- if(debug)
- printf("Got reject (ack packet %d)\n", ack->r_sequence);
- release(ack->r_sequence);
- for(i=0;i<ack->r_size;i++) {
- if(debug)
- printf("\tpacket %d\n", ack->r_data[i]);
- r = packet[slotno(ack->r_data[i])];
- if(inwindow(ack->r_data[i], lower, upper))
- send(r, r->r_size, DATA, addr);
- }
- break;
- /*
- * Timeout waiting for ACK - retransmit all the outstanding packets.
- */
- case UNDEFINED:
- printf("Timeout\n");
- if(retry++ > MAXRETRY) {
- printf("Too many timeouts - ftp abort\n");
- n = sprintf(ack->r_data, "Too many timeouts");
- send(ack, n, TERMINATE, addr);
- exit(0);
- }
- for(i=0;i<WINDOW;i++) if(packet[i])
- send(packet[i], packet[i]->r_size, DATA, addr);
- break;
- default:
- printf("Illegal ack type %d\n", ack->r_type);
- } /* end switch */
- /*
- * If we've transmitted all the data, and got acks back for all the packets
- * then the transfer is complete.
- */
- if(eof && debug)
- printf("EOF - waiting to clear %d - %d\n", lower, upper);
- if(eof && (upper == lower)) return(1);
- } /* end data loop */
- }
- /*
- * ftpin: recv data, plonk in file.
- * Packet with data size 0 ends file transmission.
- */
- ftpin(f, addr, kb)
- char *addr;
- long *kb; /* data size received */
- {
- register struct request *r;
- long count = 0;
- int i, eof = 0;
- struct request ackreq, *ack = &ackreq;
- register int n;
- sequence_t slot, datablock, last, diskblock = 0;
- lower = num = 0;
- upper = WINDOW - 1;
- datablock = 0;
- for(i=0;i<WINDOW;i++) packet[i] = NULL;
- while(1) {
- /*
- * Zero received buffer space, so we know what has got here
- */
- retry = 0;
- if(debug)
- printf("Waiting for %d - %d, using buffer %d\n",
- lower, lower + WINDOW, slotno(datablock));
- r = &buffer[slotno(datablock++)]; /* get a buffer */
- recv(r);
- switch(r->r_type) {
- /*
- * Normal data - fix in the correct place
- * If we missed packets in the sequence, ask for them again.
- */
- case DATA:
- upper = lower + WINDOW - 1;
- if(debug) printf("Received packet %d, size %d\n",
- r->r_sequence, r->r_size);
- /*
- * incoming packet not within our current window. The ack for a previous
- * packet must have got lost - send it again!
- * If the packet doesn't seem to fit anywhere, complain!
- */
- if(!inwindow(r->r_sequence, lower, upper)) {
- if(inwindow(r->r_sequence, lower - WINDOW, lower)) {
- ack->r_sequence = r->r_sequence;
- send(ack, 0, ACCEPT, addr);
- }
- else {
- if(debug) printf("packet %d out of bounds %d - %d\n",
- r->r_sequence, lower, upper);
- n = sprintf(ack->r_data, "protocol violation: packet %d arrived in window %d - %d\n",
- r->r_sequence, lower, upper);
- send(ack, n, TERMINATE, addr);
- exit(0);
- }
- datablock--;
- continue; /* out of window */
- }
- slot = slotno(r->r_sequence);
- if(packet[slot]) {
- if(debug) printf("Duplicate - ignored\n");
- datablock--;
- continue; /* duplicate */
- }
- packet[slot] = r;
- count += r->r_size;
- /*
- * Check for eof, and send ack for this packet.
- */
- if(r->r_size == 0) {
- eof = 1;
- last = r->r_sequence;
- }
- ack->r_sequence = r->r_sequence;
- send(ack, 0, ACCEPT, addr);
- /*
- * Write as many packets as possible.
- */
- while(r = packet[slotno(lower)]) {
- if(debug)
- printf("Packet %d written to disk\n", r->r_sequence);
- if(r->r_sequence != diskblock++) {
- printf("Bug detected! data block %d written as disk block %d\n",
- r->r_sequence, datablock - 1);
- n = sprintf(ack->r_data, "data block sequence error");
- send(ack, n, TERMINATE, addr);
- exit(1);
- }
- if(write(f, r->r_data, r->r_size) != r_size) {
- perror("write error");
- n = sprintf(ack->r_data, "write error: %s",
- sys_errlist[errno]);
- send(ack, n, TERMINATE, addr);
- exit(1);
- }
- packet[slotno(lower)] = NULL;
- lower++;
- }
- break;
- default:
- printf("Illegal ftp packet %d\n", r->r_type);
- } /* end switch */
- if(eof && (lower == (last + 1))) {
- *kb = count;
- return(1);
- }
- } /* end data loop */
- }
- /*
- * Check if 'num' falls between lbound and ubound
- */
- inwindow(number, lbound, ubound)
- sequence_t number, lbound, ubound;
- {
- while(1) {
- if(number == lbound) return(1);
- if(lbound == ubound) return(0);
- else lbound++;
- }
- }
- /*
- * retransmit: we were looking for packet 'last', but got packet 'this'
- * Ask for retransmit of intervening packets - this also acks packet 'this'
- */
- retransmit(last, this, ack, addr)
- sequence_t last, this;
- register struct request *ack;
- char *addr;
- {
- int j, n = 0;
- for(j=0;j<WINDOW;j++) if(packet[j] == NULL)
- ack->r_data[n++] = (sequence_t) lower + j;
- ack->r_sequence = this;
- send(ack, n, REJECT, addr);
- }
- /*
- * release: mark packet 'n' as being succesfully received.
- * Move lower bound of window up, if possible.
- */
- release(n)
- sequence_t n;
- {
- packet[slotno(n)] = NULL;
- while(packet[slotno(lower)] == NULL) {
- lower++;
- if(lower == upper) break;
- }
- if(debug) printf("Packet %d released - lbound now %d\n", n, lower);
- }
- /*
- * fullwindow: if distance between lower and upper is >= WINDOW
- */
- fullwindow(lower, upper)
- register sequence_t lower, upper;
- {
- register int n = 0;
- while(lower++ != upper) n++;
- if(n >= WINDOW) return(1);
- return(0);
- }
-