home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume14 / 3bconnect / ftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-05-08  |  6.9 KB  |  279 lines

  1. /*
  2.  * ftp.c: the 'file transfer' protocols.
  3.  * Sliding-window protocol with selective retransmit.
  4.  */
  5. #include "ni.h"
  6.  
  7. #include "ftp.h"
  8.  
  9. extern char *sys_errlist[];
  10. int debug;
  11.  
  12. #define NULL 0
  13. #define slotno(num) ((num) % WINDOW)
  14.  
  15. struct request buffer[WINDOW];
  16. struct request *packet[WINDOW];
  17. int retry = 0;
  18. sequence_t lower, upper, num;
  19.  
  20. wakeup(){
  21.     signal(SIGALRM, wakeup);
  22. /*
  23.  * ftpout: send the file to the indicated host.
  24.  */
  25. ftpout(f, addr)
  26. char *addr;
  27. {
  28.     struct request ackreq, *ack = &ackreq;
  29.     register struct request *r;
  30.     register int n;
  31.     int i, eof = 0;
  32.     lower = upper = num = 0;
  33.     retry = 0;
  34.     for(i=0;i<WINDOW;i++) packet[i] = NULL;
  35.     wakeup();
  36.     while(1) {
  37. /*
  38.  * If the window is not sent, send another packet.
  39.  * If last packet was null, then all data has been sent.
  40.  */
  41.         while(!fullwindow(lower, upper) && !eof) {
  42.              num = upper++;
  43.              r = &buffer[slotno(num)];
  44.              packet[slotno(num)] = r;
  45.             r->r_sequence = num;
  46.             n = read(f, r->r_data, sizeof r->r_data);
  47. /*
  48.  * read error causes premature EOF
  49.  */
  50.             if(n == -1) n = 0;
  51.             if(debug) 
  52.                 printf("Sending packet %d\n", r->r_sequence);
  53. /*
  54.  * Reset the counter for ack timeouts, then send out the block
  55.  */
  56.             retry = 0;
  57.             send(r, n, DATA, addr);
  58.             if(n == 0) eof = 1;
  59.         }
  60. /* 
  61.  * We have to wait for an ACK from the other end ...
  62.  * If it's an ACK, then the sequence field determines which packet is OK
  63.  * If it's a NACK, then the data is a list of packets which 
  64.  * must have got lost somewhere. so we re-transmit them.
  65.  */
  66.         if(debug) printf("Window full - waiting for ack\n");
  67.          alarm(TRANSIT);
  68.         recv(ack);
  69.         alarm(0);
  70.         switch(ack->r_type) {
  71. /*
  72.  * ACK - mark the packet as sent (delete pointer).
  73.  * Move lower bound of window up as far as possible.
  74.  */
  75.         case ACCEPT:
  76.             if(debug)
  77.                 printf("Got ack on packet %d\n", ack->r_sequence);
  78.             release(ack->r_sequence);
  79.             break;
  80.         case REJECT:
  81.             if(debug)
  82.                 printf("Got reject (ack packet %d)\n", ack->r_sequence);
  83.             release(ack->r_sequence);
  84.             for(i=0;i<ack->r_size;i++) {
  85.                 if(debug) 
  86.                     printf("\tpacket %d\n", ack->r_data[i]);
  87.                 r = packet[slotno(ack->r_data[i])];
  88.                 if(inwindow(ack->r_data[i], lower, upper))
  89.                     send(r, r->r_size, DATA, addr);
  90.             }
  91.             break;
  92. /*
  93.  * Timeout waiting for ACK - retransmit all the outstanding packets.
  94.  */
  95.         case UNDEFINED:
  96.             printf("Timeout\n");
  97.             if(retry++ > MAXRETRY) {
  98.                 printf("Too many timeouts - ftp abort\n");
  99.                 n = sprintf(ack->r_data, "Too many timeouts");
  100.                 send(ack, n, TERMINATE, addr);
  101.                 exit(0);
  102.             }
  103.             for(i=0;i<WINDOW;i++) if(packet[i])
  104.                 send(packet[i], packet[i]->r_size, DATA, addr);
  105.             break;
  106.          default:
  107.             printf("Illegal ack type %d\n", ack->r_type);
  108.         } /* end switch */
  109. /*
  110.  * If we've transmitted all the data, and got acks back for all the packets
  111.  * then the transfer is complete.
  112.  */
  113.          if(eof && debug) 
  114.              printf("EOF - waiting to clear %d - %d\n", lower, upper);
  115.         if(eof && (upper == lower)) return(1);
  116.     } /* end data loop */
  117. }
  118. /*
  119.  * ftpin: recv data, plonk in file.
  120.  * Packet with data size 0 ends file transmission.
  121.  */
  122. ftpin(f, addr, kb)
  123. char *addr;
  124. long *kb;            /* data size received */
  125. {
  126.     register struct request *r;
  127.     long count = 0;
  128.     int i, eof = 0;
  129.     struct request ackreq, *ack = &ackreq;
  130.     register int n;
  131.     sequence_t slot, datablock, last, diskblock = 0;
  132.     lower = num = 0;
  133.     upper = WINDOW - 1;
  134.     datablock = 0;
  135.     for(i=0;i<WINDOW;i++) packet[i] = NULL;
  136.     while(1) {
  137. /*
  138.  * Zero received buffer space, so we know what has got here
  139.  */
  140.         retry = 0;
  141.         if(debug) 
  142.             printf("Waiting for %d - %d, using buffer %d\n", 
  143.                 lower, lower + WINDOW, slotno(datablock));
  144.         r = &buffer[slotno(datablock++)];    /* get a buffer */
  145.         recv(r);
  146.         switch(r->r_type) {
  147. /*
  148.  * Normal data - fix in the correct place
  149.  * If we missed packets in the sequence, ask for them again.
  150.  */
  151.         case DATA:
  152.             upper = lower + WINDOW - 1;
  153.             if(debug) printf("Received packet %d, size %d\n", 
  154.                 r->r_sequence, r->r_size);
  155. /*
  156.  * incoming packet not within our current window. The ack for a previous
  157.  * packet must have got lost - send it again!
  158.  * If the packet doesn't seem to fit anywhere, complain!
  159.  */
  160.             if(!inwindow(r->r_sequence, lower, upper)) {
  161.                 if(inwindow(r->r_sequence, lower - WINDOW, lower)) {
  162.                     ack->r_sequence = r->r_sequence;
  163.                     send(ack, 0, ACCEPT, addr);
  164.                 }
  165.                 else {
  166.                     if(debug) printf("packet %d out of bounds %d - %d\n",
  167.                     r->r_sequence, lower, upper);
  168.                     n = sprintf(ack->r_data, "protocol violation: packet %d arrived in window %d - %d\n",
  169.                         r->r_sequence, lower, upper);
  170.                     send(ack, n, TERMINATE, addr);
  171.                     exit(0);
  172.                 }
  173.                 datablock--;
  174.                 continue;        /* out of window */
  175.             }
  176.             slot = slotno(r->r_sequence);
  177.             if(packet[slot]) {
  178.                 if(debug) printf("Duplicate - ignored\n");
  179.                 datablock--;
  180.                 continue;    /* duplicate */
  181.             }
  182.             packet[slot] = r;
  183.             count += r->r_size;
  184. /*
  185.  * Check for eof, and send ack for this packet.
  186.  */
  187.             if(r->r_size == 0) {
  188.                 eof = 1;
  189.                 last = r->r_sequence;
  190.             }
  191.             ack->r_sequence = r->r_sequence;
  192.             send(ack, 0, ACCEPT, addr);
  193. /*
  194.  * Write as many packets as possible.
  195.  */
  196.              while(r = packet[slotno(lower)]) {
  197.                  if(debug) 
  198.                     printf("Packet %d written to disk\n", r->r_sequence);
  199.                 if(r->r_sequence != diskblock++) {
  200.                     printf("Bug detected! data block %d written as disk block %d\n",
  201.                         r->r_sequence, datablock - 1);
  202.                     n = sprintf(ack->r_data, "data block sequence error");
  203.                     send(ack, n, TERMINATE, addr);
  204.                     exit(1);
  205.                 }
  206.                  if(write(f, r->r_data, r->r_size) != r_size) {
  207.                      perror("write error");
  208.                      n = sprintf(ack->r_data, "write error: %s",
  209.                          sys_errlist[errno]);
  210.                      send(ack, n, TERMINATE, addr);
  211.                      exit(1);
  212.                  }
  213.                  packet[slotno(lower)] = NULL;
  214.                  lower++;
  215.              }
  216.             break;
  217.         default:
  218.             printf("Illegal ftp packet %d\n", r->r_type);
  219.         }    /* end switch */
  220.         if(eof && (lower == (last + 1))) {
  221.             *kb = count;
  222.             return(1);
  223.         }
  224.     } /* end data loop */
  225. }
  226. /*
  227.  * Check if 'num' falls between lbound and ubound
  228.  */
  229. inwindow(number, lbound, ubound)
  230. sequence_t number, lbound, ubound;
  231. {
  232.     while(1) {
  233.         if(number == lbound) return(1);
  234.         if(lbound == ubound) return(0);
  235.         else lbound++;
  236.     }
  237. }
  238. /*
  239.  * retransmit: we were looking for packet 'last', but got packet 'this'
  240.  * Ask for retransmit of intervening packets - this also acks packet 'this'
  241.  */
  242. retransmit(last, this, ack, addr)
  243. sequence_t last, this;
  244. register struct request *ack;
  245. char *addr;
  246. {
  247.     int j, n = 0;
  248.     for(j=0;j<WINDOW;j++) if(packet[j] == NULL)
  249.         ack->r_data[n++] = (sequence_t) lower + j;
  250.     ack->r_sequence = this;
  251.     send(ack, n, REJECT, addr);
  252. }
  253. /*
  254.  * release: mark packet 'n' as being succesfully received.
  255.  * Move lower bound of window up, if possible.
  256.  */
  257. release(n)
  258. sequence_t n;
  259. {
  260.     packet[slotno(n)] = NULL;
  261.     while(packet[slotno(lower)] == NULL) {
  262.         lower++;
  263.         if(lower == upper) break;
  264.     }
  265.     if(debug) printf("Packet %d released - lbound now %d\n", n, lower);
  266. }
  267. /*
  268.  * fullwindow: if distance between lower and upper is >= WINDOW
  269.  */
  270. fullwindow(lower, upper)
  271. register sequence_t lower, upper;
  272. {
  273.     register int n = 0;
  274.     while(lower++ != upper) n++;
  275.     if(n >= WINDOW) return(1);
  276.     return(0);
  277. }
  278.